begin rewriting and vastly simplifying compact flash fat generation code so that it is more amenable to bugfixing

This commit is contained in:
zeromus 2010-09-03 10:39:59 +00:00
parent c1f2d4629a
commit b68eba259b
12 changed files with 2683 additions and 373 deletions

View File

@ -19,7 +19,7 @@ libdesmume_a_SOURCES = \
common.cpp common.h \ common.cpp common.h \
debug.cpp debug.h \ debug.cpp debug.h \
Disassembler.cpp Disassembler.h \ Disassembler.cpp Disassembler.h \
emufile.h emufile.cpp fat.h FIFO.cpp FIFO.h \ emufat.h emufat.cpp emufat_types.h emufile.h emufile.cpp fat.h FIFO.cpp FIFO.h \
firmware.cpp firmware.h GPU.cpp GPU.h \ firmware.cpp firmware.h GPU.cpp GPU.h \
GPU_osd.h \ GPU_osd.h \
mem.h mc.cpp mc.h \ mem.h mc.cpp mc.h \

View File

@ -1,7 +1,6 @@
/* Copyright (C) 2006 yopyop /* Copyright (C) 2006 yopyop
Copyright (C) 2006 Mic Copyright (C) 2006 Mic
Copyright (C) 2009 CrazyMax Copyright (C) 2009-2010 DeSmuME team
Copyright (C) 2009 DeSmuME team
This file is part of DeSmuME This file is part of DeSmuME
@ -28,6 +27,9 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "../emufat.h"
#include <stack>
#include <fcntl.h> #include <fcntl.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifdef _MSC_VER #ifdef _MSC_VER
@ -77,9 +79,6 @@ typedef struct {
#define CF_CMD_READ 0x20 #define CF_CMD_READ 0x20
#define CF_CMD_WRITE 0x30 #define CF_CMD_WRITE 0x30
static int disk_image = -1;
static off_t file_size;
static u16 cf_reg_sts, static u16 cf_reg_sts,
cf_reg_lba1, cf_reg_lba1,
cf_reg_lba2, cf_reg_lba2,
@ -119,6 +118,8 @@ static BOOL cflashDeviceEnabled = FALSE;
static std::string sFlashPath; static std::string sFlashPath;
static EMUFILE* file;
// =========================== // ===========================
BOOL inited; BOOL inited;
@ -245,8 +246,14 @@ static void add_file(char *fname, FsEntry * entry, int fileLevel)
} }
} }
enum EListCallbackArg {
EListCallbackArg_Item, EListCallbackArg_Pop
};
typedef void (*ListCallback)(FsEntry* fs, EListCallbackArg);
// List all files and subdirectories recursively // List all files and subdirectories recursively
static void list_files(const char *filepath) static void list_files(const char *filepath, ListCallback list_callback)
{ {
char DirSpec[255+1], SubDir[255+1]; char DirSpec[255+1], SubDir[255+1];
FsEntry entry; FsEntry entry;
@ -265,12 +272,14 @@ static void list_files(const char *filepath)
if (hFind == NULL) return; if (hFind == NULL) return;
fname = (strlen(entry.cAlternateFileName)>0) ? entry.cAlternateFileName : entry.cFileName; fname = (strlen(entry.cAlternateFileName)>0) ? entry.cAlternateFileName : entry.cFileName;
add_file(fname, &entry, fileLevel); list_callback(&entry,EListCallbackArg_Item);
//add_file(fname, &entry, fileLevel);
while (FsReadNext(hFind, &entry) != 0) while (FsReadNext(hFind, &entry) != 0)
{ {
fname = (strlen(entry.cAlternateFileName)>0) ? entry.cAlternateFileName : entry.cFileName; fname = (strlen(entry.cAlternateFileName)>0) ? entry.cAlternateFileName : entry.cFileName;
add_file(fname, &entry, fileLevel); //add_file(fname, &entry, fileLevel);
list_callback(&entry,EListCallbackArg_Item);
printf("cflash added %s\n",fname); printf("cflash added %s\n",fname);
if (numFiles==MAXFILES-1) break; if (numFiles==MAXFILES-1) break;
@ -280,7 +289,8 @@ static void list_files(const char *filepath)
if (strlen(fname)+strlen(filepath)+2 < 256) if (strlen(fname)+strlen(filepath)+2 < 256)
{ {
sprintf(SubDir, "%s%c%s", filepath, FS_SEPARATOR, fname); sprintf(SubDir, "%s%c%s", filepath, FS_SEPARATOR, fname);
list_files(SubDir); list_files(SubDir, list_callback);
list_callback(&entry, EListCallbackArg_Pop);
} }
} }
} }
@ -289,213 +299,307 @@ static void list_files(const char *filepath)
FsClose(hFind); FsClose(hFind);
if (dwError != FS_ERR_NO_MORE_FILES) return; if (dwError != FS_ERR_NO_MORE_FILES) return;
if (numFiles < MAXFILES) //if (numFiles < MAXFILES)
{ //{
fileLink[numFiles].parent = fileLevel; // fileLink[numFiles].parent = fileLevel;
files[numFiles++].name[0] = 0; // files[numFiles++].name[0] = 0;
} //}
} }
static u32 dataSectors = 0;
void count_ListCallback(FsEntry* fs, EListCallbackArg arg)
{
if(arg == EListCallbackArg_Pop) return;
u32 sectors = 1;
if(fs->flags & FS_IS_DIR)
{
}
else
sectors += (fs->fileSize+511)/512 + 1;
dataSectors += sectors;
}
static std::string currPath;
static EmuFatFile currFatFile;
static std::stack<EmuFatFile> fatStack;
static std::stack<std::string> pathStack;
void build_ListCallback(FsEntry* fs, EListCallbackArg arg)
{
char* fname = (strlen(fs->cAlternateFileName)>0) ? fs->cAlternateFileName : fs->cFileName;
if(arg == EListCallbackArg_Pop)
{
currFatFile = fatStack.top();
fatStack.pop();
currPath = pathStack.top();
pathStack.pop();
return;
}
if(fs->flags & FS_IS_DIR)
{
if(!strcmp(fname,".")) return;
if(!strcmp(fname,"..")) return;
pathStack.push(currPath);
fatStack.push(currFatFile);
EmuFatFile newDir;
newDir.makeDir(&currFatFile,fname);
newDir.sync();
currFatFile = newDir;
currPath = currPath + std::string(1,FS_SEPARATOR) + fname;
return;
}
else
{
std::string path = currPath + std::string(1,FS_SEPARATOR) + fname;
FILE* inf = fopen(path.c_str(),"rb");
fseek(inf,0,SEEK_END);
long len = ftell(inf);
fseek(inf,0,SEEK_SET);
u8 *buf = new u8[len];
fread(buf,1,len,inf);
fclose(inf);
EmuFatFile f;
f.open(&currFatFile,fname,EO_RDWR | EO_CREAT);
f.write(buf,len);
f.close();
delete[] buf;
}
}
// Set up the MBR, FAT and DIR_ENTs // Set up the MBR, FAT and DIR_ENTs
static BOOL cflash_build_fat() static BOOL cflash_build_fat()
{ {
int i,j,k,l, dataSectors = 0;
clust,numClusters, currPath = sFlashPath;
clusterNum2,rootCluster; list_files(sFlashPath.c_str(), count_ListCallback);
int fileLevel;
numFiles = 0; dataSectors += 16*1024*1024/512; //add 16MB worth of write space. this is probably enough for anyone, but maybe it should be configurable.
fileLevel = -1; //we could always suggest to users to add a big file to their directory to overwrite (that would cause the image to get padded)
maxLevel = -1;
files = (DIR_ENT *) malloc(MAXFILES*sizeof(DIR_ENT)); delete file;
memset(files,0,MAXFILES*sizeof(DIR_ENT)); file = new EMUFILE_MEMORY(dataSectors*512+1);
if (files == NULL) return FALSE; EmuFat fat(file);
fileLink = (FILE_INFO *) malloc(MAXFILES*sizeof(FILE_INFO)); EmuFatVolume vol;
if (fileLink == NULL) u8 ok = vol.init(&fat);
{ vol.format(dataSectors);
free(files);
return FALSE;
}
for (i=0; i<MAXFILES; i++) reconstruct(&currFatFile);
{ currFatFile.openRoot(&vol);
files[i].attrib = 0;
files[i].name[0] = FILE_FREE;
files[i].fileSize = 0;
fileLink[i].filesInDir = 0; list_files(sFlashPath.c_str(), build_ListCallback);
extraDirEntries[i] = NULL; FILE* outf = fopen("d:\\test.ima","wb");
numExtraEntries[i] = 0; EMUFILE_MEMORY* memf = (EMUFILE_MEMORY*)file;
} fwrite(memf->buf(),1,memf->size(),outf);
fclose(outf);
list_files(sFlashPath.c_str());
k = 0;
clusterNum = rootCluster = (SECRESV + SECPERFAT)/SECPERCLUS;
numClusters = 0;
clust = 0;
numRootFiles = 0;
// Allocate memory to hold information about the files //int i,j,k,l,
dirEntries = (DIR_ENT *) malloc(numFiles*sizeof(DIR_ENT)); //clust,numClusters,
if (dirEntries==NULL) return FALSE; //clusterNum2,rootCluster;
//int fileLevel;
dirEntryLink = (FILE_INFO *) malloc(numFiles*sizeof(FILE_INFO)); //numFiles = 0;
if (dirEntryLink==NULL) //fileLevel = -1;
{ //maxLevel = -1;
free(dirEntries);
return FALSE;
}
dirEntriesInCluster = (int *) malloc(NUMCLUSTERS*sizeof(int));
if (dirEntriesInCluster==NULL)
{
free(dirEntries);
free(dirEntryLink);
return FALSE;
}
dirEntryPtr = (DIR_ENT **) malloc(NUMCLUSTERS*sizeof(DIR_ENT*));
if (dirEntryPtr==NULL)
{
free(dirEntries);
free(dirEntryLink);
free(dirEntriesInCluster);
return FALSE;
}
memset(dirEntriesInCluster, 0, NUMCLUSTERS*sizeof(int)); //files = (DIR_ENT *) malloc(MAXFILES*sizeof(DIR_ENT));
memset(dirEntryPtr, NULL, NUMCLUSTERS*sizeof(DIR_ENT*)); //memset(files,0,MAXFILES*sizeof(DIR_ENT));
//if (files == NULL) return FALSE;
//fileLink = (FILE_INFO *) malloc(MAXFILES*sizeof(FILE_INFO));
//if (fileLink == NULL)
//{
// free(files);
// return FALSE;
//}
// Change the hierarchical layout to a flat one //for (i=0; i<MAXFILES; i++)
for (i=0; i<=maxLevel; i++) //{
{ // files[i].attrib = 0;
clusterNum2 = clusterNum; // files[i].name[0] = FILE_FREE;
for (j=0; j<numFiles; j++) // files[i].fileSize = 0;
{
if (fileLink[j].parent == i)
{
if (dirEntryPtr[clusterNum] == NULL)
dirEntryPtr[clusterNum] = &dirEntries[k];
dirEntryLink[k] = fileLink[j];
dirEntries[k++] = files[j];
if ((files[j].attrib & ATTRIB_LFN)==0)
{
if (files[j].attrib & ATTRIB_DIR)
{
if (strncmp((char*)&files[j].name[0],". ",NAME_LEN)==0)
dirEntries[k-1].startCluster = dirEntryLink[k-1].level;
else
if (strncmp((char*)&files[j].name[0],".. ",NAME_LEN)==0)
{
dirEntries[k-1].startCluster = dirEntryLink[k-1].parent;
}
else
{
clust++;
dirEntries[k-1].startCluster = clust;
l = (fileLink[fileLink[j].level].filesInDir)>>8;
clust += l;
numClusters += l;
}
}
else
dirEntries[k-1].startCluster = clusterNum;
}
if (i==0) numRootFiles++;
dirEntriesInCluster[clusterNum]++;
if (dirEntriesInCluster[clusterNum]==256)
clusterNum++;
}
}
clusterNum = clusterNum2 + ((fileLink[i].filesInDir)>>8) + 1;
numClusters++;
}
// Free the file indexing buffer // fileLink[i].filesInDir = 0;
free(files);
free(fileLink);
// Set up the MBR // extraDirEntries[i] = NULL;
MBR.bytesPerSector = 512; // numExtraEntries[i] = 0;
MBR.numFATs = 1; //}
// replaced strcpy with strncpy. It doesnt matter here, as the strings are constant
// but we should extingish all unrestricted strcpy,strcat from the project
strncpy((char*)&MBR.OEMName[0],"DESMUM",8);
strncpy((char*)&MBR.fat16.fileSysType[0],"FAT16 ",8);
MBR.reservedSectors = SECRESV;
MBR.numSectors = 524288;
MBR.numSectorsSmall = 0;
MBR.sectorsPerCluster = SECPERCLUS;
MBR.sectorsPerFAT = SECPERFAT;
MBR.rootEntries = 512;
MBR.fat16.signature = 0xAA55;
MBR.mediaDesc = 1;
filesysFAT = 0 + MBR.reservedSectors; //list_files(sFlashPath.c_str());
filesysRootDir = filesysFAT + (MBR.numFATs * MBR.sectorsPerFAT);
filesysData = filesysRootDir + ((MBR.rootEntries * sizeof(DIR_ENT)) / 512);
// Set up the cluster values for all subdirectories //k = 0;
clust = filesysData / SECPERCLUS; //clusterNum = rootCluster = (SECRESV + SECPERFAT)/SECPERCLUS;
firstDirEntCluster = clust; //numClusters = 0;
for (i=1; i<numFiles; i++) //clust = 0;
{ //numRootFiles = 0;
if (((dirEntries[i].attrib & ATTRIB_DIR)!=0) && ((dirEntries[i].attrib & ATTRIB_LFN)==0))
{
if (dirEntries[i].startCluster > rootCluster)
dirEntries[i].startCluster += clust-rootCluster;
}
}
lastDirEntCluster = clust+numClusters-1;
// Set up the cluster values for all files //// Allocate memory to hold information about the files
clust += numClusters; //clusterNum; //dirEntries = (DIR_ENT *) malloc(numFiles*sizeof(DIR_ENT));
for (i=0; i<numFiles; i++) //if (dirEntries==NULL) return FALSE;
{
if (((dirEntries[i].attrib & ATTRIB_DIR)==0) && ((dirEntries[i].attrib & ATTRIB_LFN)==0))
{
dirEntries[i].startCluster = clust;
clust += (dirEntries[i].fileSize/(512*SECPERCLUS));
clust++;
}
}
lastFileDataCluster = clust-1;
// Set up the FAT16 //dirEntryLink = (FILE_INFO *) malloc(numFiles*sizeof(FILE_INFO));
memset(FAT16,0,SECPERFAT*256*sizeof(u16)); //if (dirEntryLink==NULL)
FAT16[0] = 0xFF01; //{
FAT16[1] = 0xFFFF; // free(dirEntries);
for (i=2; i<=lastDirEntCluster; i++) // return FALSE;
FAT16[i] = 0xFFFF; //}
k = 2; //dirEntriesInCluster = (int *) malloc(NUMCLUSTERS*sizeof(int));
for (i=0; i<numFiles; i++) //if (dirEntriesInCluster==NULL)
{ //{
if (((dirEntries[i].attrib & ATTRIB_LFN)==0) && (dirEntries[i].name[0] != FILE_FREE)) // free(dirEntries);
{ // free(dirEntryLink);
j = 0; // return FALSE;
l = dirEntries[i].fileSize - (512*SECPERCLUS); //}
while (l > 0) //dirEntryPtr = (DIR_ENT **) malloc(NUMCLUSTERS*sizeof(DIR_ENT*));
{ //if (dirEntryPtr==NULL)
if (dirEntries[i].startCluster+j < MAXFILES) //{
FAT16[dirEntries[i].startCluster+j] = dirEntries[i].startCluster+j+1; // free(dirEntries);
j++; // free(dirEntryLink);
l -= (512*16); // free(dirEntriesInCluster);
} // return FALSE;
if ((dirEntries[i].attrib & ATTRIB_DIR)==0) //}
{
if (dirEntries[i].startCluster+j < MAXFILES)
FAT16[dirEntries[i].startCluster+j] = 0xFFFF;
}
k = dirEntries[i].startCluster+j;
}
}
for (i=(filesysData/SECPERCLUS); i<NUMCLUSTERS; i++) //memset(dirEntriesInCluster, 0, NUMCLUSTERS*sizeof(int));
{ //memset(dirEntryPtr, NULL, NUMCLUSTERS*sizeof(DIR_ENT*));
if (dirEntriesInCluster[i]==256)
FAT16[i] = i+1; //// Change the hierarchical layout to a flat one
} //for (i=0; i<=maxLevel; i++)
//{
// clusterNum2 = clusterNum;
// for (j=0; j<numFiles; j++)
// {
// if (fileLink[j].parent == i)
// {
// if (dirEntryPtr[clusterNum] == NULL)
// dirEntryPtr[clusterNum] = &dirEntries[k];
// dirEntryLink[k] = fileLink[j];
// dirEntries[k++] = files[j];
// if ((files[j].attrib & ATTRIB_LFN)==0)
// {
// if (files[j].attrib & ATTRIB_DIR)
// {
// if (strncmp((char*)&files[j].name[0],". ",NAME_LEN)==0)
// dirEntries[k-1].startCluster = dirEntryLink[k-1].level;
// else
// if (strncmp((char*)&files[j].name[0],".. ",NAME_LEN)==0)
// {
// dirEntries[k-1].startCluster = dirEntryLink[k-1].parent;
// }
// else
// {
// clust++;
// dirEntries[k-1].startCluster = clust;
// l = (fileLink[fileLink[j].level].filesInDir)>>8;
// clust += l;
// numClusters += l;
// }
// }
// else
// dirEntries[k-1].startCluster = clusterNum;
// }
// if (i==0) numRootFiles++;
// dirEntriesInCluster[clusterNum]++;
// if (dirEntriesInCluster[clusterNum]==256)
// clusterNum++;
// }
// }
// clusterNum = clusterNum2 + ((fileLink[i].filesInDir)>>8) + 1;
// numClusters++;
//}
//// Free the file indexing buffer
//free(files);
//free(fileLink);
//// Set up the MBR
//MBR.bytesPerSector = 512;
//MBR.numFATs = 1;
//// replaced strcpy with strncpy. It doesnt matter here, as the strings are constant
//// but we should extingish all unrestricted strcpy,strcat from the project
//strncpy((char*)&MBR.OEMName[0],"DESMUM",8);
//strncpy((char*)&MBR.fat16.fileSysType[0],"FAT16 ",8);
//MBR.reservedSectors = SECRESV;
//MBR.numSectors = 524288;
//MBR.numSectorsSmall = 0;
//MBR.sectorsPerCluster = SECPERCLUS;
//MBR.sectorsPerFAT = SECPERFAT;
//MBR.rootEntries = 512;
//MBR.fat16.signature = 0xAA55;
//MBR.mediaDesc = 1;
//filesysFAT = 0 + MBR.reservedSectors;
//filesysRootDir = filesysFAT + (MBR.numFATs * MBR.sectorsPerFAT);
//filesysData = filesysRootDir + ((MBR.rootEntries * sizeof(DIR_ENT)) / 512);
//// Set up the cluster values for all subdirectories
//clust = filesysData / SECPERCLUS;
//firstDirEntCluster = clust;
//for (i=1; i<numFiles; i++)
//{
// if (((dirEntries[i].attrib & ATTRIB_DIR)!=0) && ((dirEntries[i].attrib & ATTRIB_LFN)==0))
// {
// if (dirEntries[i].startCluster > rootCluster)
// dirEntries[i].startCluster += clust-rootCluster;
// }
//}
//lastDirEntCluster = clust+numClusters-1;
//// Set up the cluster values for all files
//clust += numClusters; //clusterNum;
//for (i=0; i<numFiles; i++)
//{
// if (((dirEntries[i].attrib & ATTRIB_DIR)==0) && ((dirEntries[i].attrib & ATTRIB_LFN)==0))
// {
// dirEntries[i].startCluster = clust;
// clust += (dirEntries[i].fileSize/(512*SECPERCLUS));
// clust++;
// }
//}
//lastFileDataCluster = clust-1;
//// Set up the FAT16
//memset(FAT16,0,SECPERFAT*256*sizeof(u16));
//FAT16[0] = 0xFF01;
//FAT16[1] = 0xFFFF;
//for (i=2; i<=lastDirEntCluster; i++)
//FAT16[i] = 0xFFFF;
//k = 2;
//for (i=0; i<numFiles; i++)
//{
// if (((dirEntries[i].attrib & ATTRIB_LFN)==0) && (dirEntries[i].name[0] != FILE_FREE))
// {
// j = 0;
// l = dirEntries[i].fileSize - (512*SECPERCLUS);
// while (l > 0)
// {
// if (dirEntries[i].startCluster+j < MAXFILES)
// FAT16[dirEntries[i].startCluster+j] = dirEntries[i].startCluster+j+1;
// j++;
// l -= (512*16);
// }
// if ((dirEntries[i].attrib & ATTRIB_DIR)==0)
// {
// if (dirEntries[i].startCluster+j < MAXFILES)
// FAT16[dirEntries[i].startCluster+j] = 0xFFFF;
// }
// k = dirEntries[i].startCluster+j;
// }
//}
//for (i=(filesysData/SECPERCLUS); i<NUMCLUSTERS; i++)
//{
// if (dirEntriesInCluster[i]==256)
// FAT16[i] = i+1;
//}
return TRUE; return TRUE;
} }
@ -540,25 +644,12 @@ static BOOL cflash_init()
{ {
sFlashPath = CFlash_Path; sFlashPath = CFlash_Path;
INFO("Using CFlash disk image file %s\n", sFlashPath.c_str()); INFO("Using CFlash disk image file %s\n", sFlashPath.c_str());
disk_image = OPEN_FN( sFlashPath.c_str(), OPEN_MODE); file = new EMUFILE_FILE(sFlashPath.c_str(),"rb+");
if(file->fail())
if ( disk_image != -1)
{ {
file_size = LSEEK_FN( disk_image, 0, SEEK_END); CFLASHLOG("Failed to open file %s\n", sFlashPath.c_str());
if (0 && file_size == -1) delete file;
{
CFLASHLOG( "Error when seeking to end of disk image" );
}
else
{
LSEEK_FN( disk_image, 0, SEEK_SET);
CFLASHLOG( "Disk image size = %ld (%ld sectors)\n", file_size, file_size / 512);
init_good = TRUE;
}
} }
else
// TODO: create image if not exist
CFLASHLOG("Failed to open file %s: \"%s\"\n", sFlashPath.c_str(), strerror( errno));
} }
// READY // READY
@ -692,126 +783,16 @@ static unsigned int cflash_read(unsigned int address)
case CF_REG_DATA: case CF_REG_DATA:
if (cf_reg_cmd == CF_CMD_READ) if (cf_reg_cmd == CF_CMD_READ)
{ {
if (!CFlash_IsUsingPath()) if(file)
{ {
if ( disk_image != -1) u8 data[2];
{ file->fseek(currLBA, SEEK_SET);
u8 data[2]; elems_read += file->fread(data,2);
#if 0 ret_value = data[1] << 8 | data[0];
if (currLBA < buffered_start_index || currLBA >= (buffered_start_index + BUFFERED_BLOCK_SIZE))
{
size_t read_bytes = 0;
LSEEK_FN( disk_image, currLBA, SEEK_SET);
while (read_bytes < BUFFERED_BLOCK_SIZE)
{
size_t cur_read = READ_FN( disk_image, &block_buffer[read_bytes],
BUFFERED_BLOCK_SIZE - read_bytes);
if ( cur_read == -1)
{
CFLASHLOG( "Error during read: %s\n", strerror(errno) );
break;
}
read_bytes += cur_read;
}
CFLASHLOG( "Read %d bytes\n", read_bytes);
buffered_start_index = currLBA;
}
data[0] = block_buffer[currLBA - buffered_start_index];
data[1] = block_buffer[currLBA + 1 - buffered_start_index];
#else
LSEEK_FN( disk_image, currLBA, SEEK_SET);
elems_read += READ_FN( disk_image, data, 2);
#endif
ret_value = data[1] << 8 | data[0];
}
currLBA += 2;
}
else // use path
{
unsigned char *p;
int i;
u32 cluster,cluster2,cluster3,fileLBA;
cluster = (currLBA / (512 * SECPERCLUS));
cluster2 = (((currLBA/512) - filesysData) / SECPERCLUS) + 2;
// Reading from the MBR
if (currLBA < 512 && currLBA >= 0)
{
p = (unsigned char*)&MBR;
ret_value = T1ReadWord(p, currLBA);
// Reading the FAT
}
else
if (((u32)currLBA >= filesysFAT*512) && ((u32)currLBA < filesysRootDir*512))
{
p = (unsigned char*)&FAT16[0];
ret_value = T1ReadWord(p, currLBA - filesysFAT * 512);
// Reading directory entries
}
else
if (((u32)currLBA >= filesysRootDir*512) && (cluster <= (u32)lastDirEntCluster))
{
cluster3 = ((currLBA - (SECRESV * 512)) / (512 * SECPERCLUS));
i = (currLBA-(((cluster3-(filesysRootDir/SECPERCLUS))*SECPERCLUS+filesysRootDir)*512)); //(currLBA - cluster*BYTESPERCLUS);
if (i < (dirEntriesInCluster[cluster3]*32))
{
p = (unsigned char*)dirEntryPtr[cluster3];
ret_value = T1ReadWord(p, i);
}
else
{
i /= 32;
i -= dirEntriesInCluster[cluster3];
if ((i>=0)&&(i<numExtraEntries[cluster3]))
{
p = (unsigned char*)extraDirEntries[cluster3];
ret_value = T1ReadWord(p, i * 32 + (currLBA & 0x1F));
}
else
if ((currLBA&0x1F)==0)
ret_value = FILE_FREE;
else
ret_value = 0;
}
// Reading file data
}
else
if ((cluster2 > (u32)lastDirEntCluster) && (cluster2 <= (u32)lastFileDataCluster))
{
//else if ((cluster>lastDirEntCluster)&&(cluster<=lastFileDataCluster)) {
fileLBA = currLBA - (filesysData-32)*512; // 32 = # sectors used for the root entries
// Check if the read is from the currently opened file
if ((fileLBA >= fileStartLBA) && (fileLBA < fileEndLBA))
{
cluster = (fileLBA / (512 * SECPERCLUS));
ret_value = fread_buffered(activeDirEnt,cluster-dirEntries[activeDirEnt].startCluster,(fileLBA-fileStartLBA)&(BYTESPERCLUS-1));
}
else
{
for (i=0; i<numFiles; i++)
{
if ((fileLBA>=(u32)(dirEntries[i].startCluster*512*SECPERCLUS)) &&
(fileLBA <(dirEntries[i].startCluster*512*SECPERCLUS)+dirEntries[i].fileSize) &&
((dirEntries[i].attrib & (ATTRIB_DIR|ATTRIB_LFN))==0))
{
cluster = (fileLBA / (512 * SECPERCLUS));
ret_value = fread_buffered(i,cluster-dirEntries[i].startCluster,fileLBA&(BYTESPERCLUS-1));
break;
}
}
}
}
currLBA += 2;
} }
currLBA += 2;
} }
break; break;
case CF_REG_CMD: case CF_REG_CMD:
break; break;
@ -838,7 +819,6 @@ static void cflash_write(unsigned int address,unsigned int data)
case CF_REG_DATA: case CF_REG_DATA:
if (cf_reg_cmd == CF_CMD_WRITE) if (cf_reg_cmd == CF_CMD_WRITE)
{ {
if (!CFlash_IsUsingPath())
{ {
sector_data[sector_write_index] = (data >> 0) & 0xff; sector_data[sector_write_index] = (data >> 0) & 0xff;
sector_data[sector_write_index + 1] = (data >> 8) & 0xff; sector_data[sector_write_index + 1] = (data >> 8) & 0xff;
@ -850,22 +830,21 @@ static void cflash_write(unsigned int address,unsigned int data)
CFLASHLOG( "Write sector to %ld\n", currLBA); CFLASHLOG( "Write sector to %ld\n", currLBA);
size_t written = 0; size_t written = 0;
if (currLBA + 512 < file_size) //TODO - calling size() every time we write something is sort of unnecessarily slow in the case of disk-backed files...
{ if(file)
if (disk_image != -1) if(currLBA + 512 < file->size())
{ {
LSEEK_FN( disk_image, currLBA, SEEK_SET); file->fseek(currLBA,SEEK_SET);
while(written < 512) while(written < 512)
{ {
size_t cur_write = size_t todo = 512-written;
WRITE_FN( disk_image, &sector_data[written], 512 - written); file->fwrite(&sector_data[written], todo);
size_t cur_write = todo;
written += cur_write; written += cur_write;
if ( cur_write == (size_t)-1) break; if ( cur_write == (size_t)-1) break;
} }
} }
}
CFLASHLOG("Wrote %u bytes\n", written); CFLASHLOG("Wrote %u bytes\n", written);
@ -873,9 +852,6 @@ static void cflash_write(unsigned int address,unsigned int data)
sector_write_index = 0; sector_write_index = 0;
} }
} }
else // TODO: write to real partition
{
}
} }
break; break;
@ -917,11 +893,8 @@ static void cflash_close( void)
if (!inited) return; if (!inited) return;
if (!CFlash_IsUsingPath()) if (!CFlash_IsUsingPath())
{ {
if (disk_image != -1) delete file;
{ file = NULL;
CLOSE_FN(disk_image);
disk_image = -1;
}
} }
else else
{ {

1525
desmume/src/emufat.cpp Normal file

File diff suppressed because it is too large Load Diff

699
desmume/src/emufat.h Normal file
View File

@ -0,0 +1,699 @@
//Copyright (C) 2009-2010 DeSmuME team
//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
* <http://www.gnu.org/licenses/>.
*/
#ifndef EMUFAT_H
#define EMUFAT_H
#include "emufat_types.h"
#include "emufile.h"
#include <stdio.h>
// use the gnu style oflag in open()
/** open() oflag for reading */
static const u8 EO_READ = 0X01;
/** open() oflag - same as O_READ */
static const u8 EO_RDONLY = EO_READ;
/** open() oflag for write */
static const u8 EO_WRITE = 0X02;
/** open() oflag - same as O_WRITE */
static const u8 EO_WRONLY = EO_WRITE;
/** open() oflag for reading and writing */
static const u8 EO_RDWR = (EO_READ | EO_WRITE);
/** open() oflag mask for access modes */
static const u8 EO_ACCMODE = (EO_READ | EO_WRITE);
/** The file offset shall be set to the end of the file prior to each write. */
static const u8 EO_APPEND = 0X04;
/** synchronous writes - call sync() after each write */
static const u8 EO_SYNC = 0X08;
/** create the file if nonexistent */
static const u8 EO_CREAT = 0X10;
/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */
static const u8 EO_EXCL = 0X20;
/** truncate the file to zero length */
static const u8 EO_TRUNC = 0X40;
//Value for byte 510 of boot block or MBR
static const u8 BOOTSIG0 = 0X55;
//Value for byte 511 of boot block or MBR
static const u8 BOOTSIG1 = 0XAA;
static void (*dateTime_)(u16* date, u16* time) = NULL;
#include "PACKED.h"
//A partition table entry for a MBR formatted storage device.
//The MBR partition table has four entries.
struct __PACKED TPartitionRecord {
//Boot Indicator . Indicates whether the volume is the active
//partition. Legal values include: 0X00. Do not use for booting.
//0X80 Active partition.
u8 boot;
//Head part of Cylinder-head-sector address of the first block in
//the partition. Legal values are 0-255. Only used in old PC BIOS.
u8 beginHead;
struct
{
//Sector part of Cylinder-head-sector address of the first block in
//the partition. Legal values are 1-63. Only used in old PC BIOS.
u32 beginSector : 6;
//High bits cylinder for first block in partition.
u32 beginCylinderHigh : 2;
};
//Combine beginCylinderLow with beginCylinderHigh. Legal values
//are 0-1023. Only used in old PC BIOS.
u8 beginCylinderLow;
//Partition type. See defines that begin with PART_TYPE_ for
//some Microsoft partition types.
u8 type;
//head part of cylinder-head-sector address of the last sector in the
//partition. Legal values are 0-255. Only used in old PC BIOS.
u8 endHead;
struct
{
//Sector part of cylinder-head-sector address of the last sector in
//the partition. Legal values are 1-63. Only used in old PC BIOS.
u32 endSector : 6;
// High bits of end cylinder
u32 endCylinderHigh : 2;
};
//Combine endCylinderLow with endCylinderHigh. Legal values
//are 0-1023. Only used in old PC BIOS.
u8 endCylinderLow;
//Logical block address of the first block in the partition.
u32 firstSector;
//Length of the partition, in blocks.
u32 totalSectors;
};
//Master Boot Record:
//The first block of a storage device that is formatted with a MBR.
struct __PACKED TMasterBootRecord {
//Code Area for master boot program.
u8 codeArea[440];
//Optional WindowsNT disk signature. May contain more boot code.
u32 diskSignature;
//Usually zero but may be more boot code.
u16 usuallyZero;
//Partition tables.
TPartitionRecord part[4];
//First MBR signature byte. Must be 0X55
u8 mbrSig0;
//Second MBR signature byte. Must be 0XAA
u8 mbrSig1;
};
//BIOS parameter block
//The BIOS parameter block describes the physical layout of a FAT volume.
struct __PACKED TBiosParmBlock {
//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;
//Number of sectors before the first FAT.
//This value must not be zero.
u16 reservedSectorCount;
//The count of FAT data structures on the volume. This field should
//always contain the value 2 for any FAT volume of any type.
u8 fatCount;
//For FAT12 and FAT16 volumes, this field contains the count of
//32-byte directory entries in the root directory. For FAT32 volumes,
//this field must be set to 0. For FAT12 and FAT16 volumes, this
//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;
//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
//must be non-zero. For FAT32 volumes, this field must be 0. For
//FAT12 and FAT16 volumes, this field contains the sector count, and
//totalSectors32 is 0 if the total sector count fits
//(is less than 0x10000).
u16 totalSectors16;
//This dates back to the old MS-DOS 1.x media determination and is
//no longer usually used for anything. 0xF8 is the standard value
//for fixed (non-removable) media. For removable media, 0xF0 is
//frequently used. Legal values are 0xF0 or 0xF8-0xFF.
u8 mediaType;
//Count of sectors occupied by one FAT on FAT12/FAT16 volumes.
//On FAT32 volumes this field must be 0, and sectorsPerFat32
//contains the FAT size count.
u16 sectorsPerFat16;
//Sectors per track for interrupt 0x13. Not used otherwise.
u16 sectorsPerTrack;
//Number of heads for interrupt 0x13. Not used otherwise.
u16 headCount;
//Count of hidden sectors preceding the partition that contains this
//FAT volume. This field is generally only relevant for media
//visible on interrupt 0x13.
u32 hiddenSectors;
//This field is the new 32-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
//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;
};
#include "PACKED_END.h"
// End Of Chain values for FAT entries
//FAT16 end of chain value used by Microsoft.
static const u16 FAT16EOC = 0XFFFF;
//Minimum value for FAT16 EOC. Use to test for EOC.
static const u16 FAT16EOC_MIN = 0XFFF8;
//FAT32 end of chain value used by Microsoft.
static const u32 FAT32EOC = 0X0FFFFFFF;
//Minimum value for FAT32 EOC. Use to test for EOC.
static const u32 FAT32EOC_MIN = 0X0FFFFFF8;
//Mask a for FAT32 entry. Entries are 28 bits.
static const u32 FAT32MASK = 0X0FFFFFFF;
//------------------------------------------------------------------------------
//\struct directoryEntry
//\brief FAT short directory entry
//Short means short 8.3 name, not the entry size.
//
//Date Format. A FAT directory entry date stamp is a 16-bit field that is
//basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the
//format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the
//16-bit word):
//
//Bits 9-15: Count of years from 1980, valid value range 0-127
//inclusive (1980-2107).
//
//Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive.
//Bits 0-4: Day of month, valid value range 1-31 inclusive.
//Time Format. A FAT directory entry time stamp is a 16-bit field that has
//a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the
//16-bit word, bit 15 is the MSB of the 16-bit word).
//
//Bits 11-15: Hours, valid value range 0-23 inclusive.
//
//Bits 5-10: Minutes, valid value range 0-59 inclusive.
//
//Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds).
//
//The valid time range is from Midnight 00:00:00 to 23:59:58.
struct TDirectoryEntry {
//Short 8.3 name.
//The first eight bytes contain the file name with blank fill.
//The last three bytes contain the file extension with blank fill.
u8 name[11];
//Entry attributes.
//The upper two bits of the attribute byte are reserved and should
//always be set to 0 when a file is created and never modified or
//looked at after that. See defines that begin with DIR_ATT_.
u8 attributes;
//Reserved for use by Windows NT. Set value to 0 when a file is
//created and never modify or look at it after that.
u8 reservedNT;
//The granularity of the seconds part of creationTime is 2 seconds
//so this field is a count of tenths of a second and its valid
//value range is 0-199 inclusive. (WHG note - seems to be hundredths)
u8 creationTimeTenths;
//Time file was created.
u16 creationTime;
//Date file was created.
u16 creationDate;
//Last access date. Note that there is no last access time, only
//a date. This is the date of last read or write. In the case of
//a write, this should be set to the same date as lastWriteDate.
u16 lastAccessDate;
//High word of this entry's first cluster number (always 0 for a
//FAT12 or FAT16 volume).
u16 firstClusterHigh;
//Time of last write. File creation is considered a write.
u16 lastWriteTime;
// Date of last write. File creation is considered a write.
u16 lastWriteDate;
// Low word of this entry's first cluster number.
u16 firstClusterLow;
//32-bit unsigned holding this file's size in bytes.
u32 fileSize;
};
//escape for name[0] = 0xE5
static const u8 DIR_NAME_0XE5 = 0x05;
//name[0] value for entry that is free after being "deleted"
static const u8 DIR_NAME_DELETED = 0xE5;
//name[0] value for entry that is free and no allocated entries follow
static const u8 DIR_NAME_FREE = 0x00;
//file is read-only
static const u8 DIR_ATT_READ_ONLY = 0x01;
//File should hidden in directory listings
static const u8 DIR_ATT_HIDDEN = 0x02;
//Entry is for a system file
static const u8 DIR_ATT_SYSTEM = 0x04;
//Directory entry contains the volume label
static const u8 DIR_ATT_VOLUME_ID = 0x08;
//Entry is for a directory
static const u8 DIR_ATT_DIRECTORY = 0x10;
//Old DOS archive bit for backup support
static const u8 DIR_ATT_ARCHIVE = 0x20;
//Test value for long name entry. Test is
//(d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME.
static const u8 DIR_ATT_LONG_NAME = 0x0F;
//Test mask for long name entry
static const u8 DIR_ATT_LONG_NAME_MASK = 0x3F;
//defined attribute bits
static const u8 DIR_ATT_DEFINED_BITS = 0x3F;
//Directory entry is part of a long name
static inline u8 DIR_IS_LONG_NAME(const TDirectoryEntry* dir) {
return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME;
}
//Mask for file/subdirectory tests
static const u8 DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY);
//Directory entry is for a file
static inline u8 DIR_IS_FILE(const TDirectoryEntry* dir) {
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0;
}
//Directory entry is for a subdirectory
static inline u8 DIR_IS_SUBDIR(const TDirectoryEntry* dir) {
return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY;
}
//Directory entry is for a file or subdirectory
static inline u8 DIR_IS_FILE_OR_SUBDIR(const TDirectoryEntry* dir) {
return (dir->attributes & DIR_ATT_VOLUME_ID) == 0;
}
// flags for timestamp
/** set the file's last access date */
static const u8 T_ACCESS = 1;
/** set the file's creation date and time */
static const u8 T_CREATE = 2;
/** Set the file's write date and time */
static const u8 T_WRITE = 4;
// values for type_
/** This SdFile has not been opened. */
static const u8 FAT_FILE_TYPE_CLOSED = 0;
/** SdFile for a file */
static const u8 FAT_FILE_TYPE_NORMAL = 1;
/** SdFile for a FAT16 root directory */
static const u8 FAT_FILE_TYPE_ROOT16 = 2;
/** SdFile for a FAT32 root directory */
static const u8 FAT_FILE_TYPE_ROOT32 = 3;
/** SdFile for a subdirectory */
static const u8 FAT_FILE_TYPE_SUBDIR = 4;
/** Test value for directory type */
static const u8 FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT16;
/** date field for FAT directory entry */
static inline u16 FAT_DATE(u16 year, u8 month, u8 day) {
return (year - 1980) << 9 | month << 5 | day;
}
/** year part of FAT directory date field */
static inline u16 FAT_YEAR(u16 fatDate) {
return 1980 + (fatDate >> 9);
}
/** month part of FAT directory date field */
static inline u8 FAT_MONTH(u16 fatDate) {
return (fatDate >> 5) & 0XF;
}
/** day part of FAT directory date field */
static inline u8 FAT_DAY(u16 fatDate) {
return fatDate & 0X1F;
}
/** time field for FAT directory entry */
static inline u16 FAT_TIME(u8 hour, u8 minute, u8 second) {
return hour << 11 | minute << 5 | second >> 1;
}
/** hour part of FAT directory time field */
static inline u8 FAT_HOUR(u16 fatTime) {
return fatTime >> 11;
}
/** minute part of FAT directory time field */
static inline u8 FAT_MINUTE(u16 fatTime) {
return(fatTime >> 5) & 0X3F;
}
/** second part of FAT directory time field */
static inline u8 FAT_SECOND(u16 fatTime) {
return 2*(fatTime & 0X1F);
}
/** Default date for file timestamps is 1 Jan 2000 */
static const u16 FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1;
/** Default time for file timestamp is 1 am */
static const u16 FAT_DEFAULT_TIME = (1 << 11);
//------------------------------------------------------
class EmuFat;
class EmuFatVolume;
class EmuFatFile;
union cache_t {
/** Used to access cached file data blocks. */
u8 data[512];
/** Used to access cached FAT16 entries. */
u16 fat16[256];
/** Used to access cached FAT32 entries. */
u32 fat32[128];
/** Used to access cached directory entries. */
TDirectoryEntry dir[16];
/** Used to access a cached MasterBoot Record. */
TMasterBootRecord mbr;
/** Used to access to a cached FAT boot sector. */
TFat32BootSector fbs;
};
class EmuFatFile
{
public:
/** Create an instance of EmuFatFile. */
EmuFatFile() : type_(FAT_FILE_TYPE_CLOSED) {}
bool writeError;
void clearUnbufferedRead(void) {
flags_ &= ~F_FILE_UNBUFFERED_READ;
}
void setUnbufferedRead(void) {
if (isFile()) flags_ |= F_FILE_UNBUFFERED_READ;
}
u8 unbufferedRead(void) const {
return flags_ & F_FILE_UNBUFFERED_READ;
}
u8 close(void);
u8 contiguousRange(u32* bgnBlock, u32* endBlock);
u8 createContiguous(EmuFatFile* dirFile, const char* fileName, u32 size);
/** \return The current cluster number for a file or directory. */
u32 curCluster(void) const {return curCluster_;}
/** \return The current position for a file or directory. */
u32 curPosition(void) const {return curPosition_;}
u8 rmDir(void);
u8 rmRfStar(void);
s16 read(void) {
u8 b;
return read(&b, 1) == 1 ? b : -1;
}
s32 read(void* buf, u32 nbyte);
s8 readDir(TDirectoryEntry* dir);
s32 write(const void* buf, u32 nbyte);
u8 openRoot(EmuFatVolume* vol);
u8 timestamp(u8 flag, u16 year, u8 month, u8 day, u8 hour, u8 minute, u8 second);
u8 sync(void);
u8 makeDir(EmuFatFile* dir, const char* dirName);
u8 open(EmuFatFile* dirFile, u16 index, u8 oflag);
u8 open(EmuFatFile* dirFile, const char* fileName, u8 oflag);
u8 remove(EmuFatFile* dirFile, const char* fileName);
u8 remove(void);
u8 dirEntry(TDirectoryEntry* dir);
u8 seekCur(u32 pos) {
return seekSet(curPosition_ + pos);
}
/**
* Set the files current position to end of file. Useful to position
* a file for append. See seekSet().
*/
u8 seekEnd(void) {return seekSet(fileSize_);}
u8 seekSet(u32 pos);
u8 type(void) const {return type_;}
u8 truncate(u32 size);
u32 dirBlock(void) const {return dirBlock_;}
/** \return Index of this file's directory in the block dirBlock. */
u8 dirIndex(void) const {return dirIndex_;}
static void dirName(const TDirectoryEntry& dir, char* name);
/** \return The total number of bytes in a file or directory. */
u32 fileSize(void) const {return fileSize_;}
/** \return The first cluster number for a file or directory. */
u32 firstCluster(void) const {return firstCluster_;}
/** \return True if this is a SdFile for a directory else false. */
u8 isDir(void) const {return type_ >= FAT_FILE_TYPE_MIN_DIR;}
/** \return True if this is a SdFile for a file else false. */
u8 isFile(void) const {return type_ == FAT_FILE_TYPE_NORMAL;}
/** \return True if this is a SdFile for an open file/directory else false. */
u8 isOpen(void) const {return type_ != FAT_FILE_TYPE_CLOSED;}
/** \return True if this is a SdFile for a subdirectory else false. */
u8 isSubDir(void) const {return type_ == FAT_FILE_TYPE_SUBDIR;}
/** \return True if this is a SdFile for the root directory. */
u8 isRoot(void) const {
return type_ == FAT_FILE_TYPE_ROOT16 || type_ == FAT_FILE_TYPE_ROOT32;
}
/** Set the file's current position to zero. */
void rewind(void) {
curPosition_ = curCluster_ = 0;
}
private:
// bits defined in flags_
// should be 0XF
static const u8 F_OFLAG = (EO_ACCMODE | EO_APPEND | EO_SYNC);
// available bits
static const u8 F_UNUSED = 0x30;
// use unbuffered SD read
static const u8 F_FILE_UNBUFFERED_READ = 0X40;
// sync of directory entry required
static const u8 F_FILE_DIR_DIRTY = 0x80;
// make sure F_OFLAG is ok
#if ((F_UNUSED | F_FILE_UNBUFFERED_READ | F_FILE_DIR_DIRTY) & F_OFLAG)
#error flags_ bits conflict
#endif // flags_ bits
// private data
u8 flags_; // See above for definition of flags_ bits
u8 type_; // type of file see above for values
u32 curCluster_; // cluster for current file position
u32 curPosition_; // current file position in bytes from beginning
u32 dirBlock_; // SD block that contains directory entry for file
u8 dirIndex_; // index of entry in dirBlock 0 <= dirIndex_ <= 0XF
u32 fileSize_; // file size in bytes
u32 firstCluster_; // first cluster of file
EmuFatVolume* vol_; // volume where file is located
// private functions
u8 addCluster(void);
u8 addDirCluster(void);
TDirectoryEntry* cacheDirEntry(u8 action);
static u8 make83Name(const char* str, u8* name);
u8 openCachedEntry(u8 cacheIndex, u8 oflags);
TDirectoryEntry* readDirCache(void);
};
class EmuFatVolume
{
public:
EmuFatVolume() :allocSearchStart_(2), fatType_(0) {}
//Initialize a FAT volume. Try partition one first then try super floppy format.
//dev The Sd2Card where the volume is located.
//return The value one, true, is returned for success and
//the value zero, false, is returned for failure. Reasons for
//failure include not finding a valid partition, not finding a valid
//FAT file system or an I/O error.
bool init(EmuFat* dev) { return init(dev, 1) ? true : init(dev, 0);}
bool init(EmuFat* dev, u8 part);
bool format(u32 sectors);
// inline functions that return volume info
//The volume's cluster size in blocks.
u8 blocksPerCluster(void) const {return blocksPerCluster_;}
//The number of blocks in one FAT.
u32 blocksPerFat(void) const {return blocksPerFat_;}
//The total number of clusters in the volume. //
u32 clusterCount(void) const {return clusterCount_;}
//The shift count required to multiply by blocksPerCluster. //
u8 clusterSizeShift(void) const {return clusterSizeShift_;}
//The logical block number for the start of file data. //
u32 dataStartBlock(void) const {return dataStartBlock_;}
//The number of FAT structures on the volume. //
u8 fatCount(void) const {return fatCount_;}
//The logical block number for the start of the first FAT. //
u32 fatStartBlock(void) const {return fatStartBlock_;}
//The FAT type of the volume. Values are 12, 16 or 32. //
u8 fatType(void) const {return fatType_;}
//The number of entries in the root directory for FAT16 volumes. //
u32 rootDirEntryCount(void) const {return rootDirEntryCount_;}
//The logical block number for the start of the root directory on FAT16 volumes or the first cluster number on FAT32 volumes. //
u32 rootDirStart(void) const {return rootDirStart_;}
EmuFat* dev_;
private:
friend class EmuFatFile;
u32 allocSearchStart_; // start cluster for alloc search
u8 blocksPerCluster_; // cluster size in blocks
u32 blocksPerFat_; // FAT size in blocks
u32 clusterCount_; // clusters in one FAT
u8 clusterSizeShift_; // shift to convert cluster count to block count
u32 dataStartBlock_; // first data block number
u8 fatCount_; // number of FATs on volume
u32 fatStartBlock_; // start block for first FAT
u8 fatType_; // volume type (12, 16, OR 32)
u16 rootDirEntryCount_; // number of entries in FAT16 root dir
u32 rootDirStart_; // root start block for FAT16, cluster for FAT32
u8 allocContiguous(u32 count, u32* curCluster);
u8 blockOfCluster(u32 position) const {
return (position >> 9) & (blocksPerCluster_ - 1);}
u32 clusterStartBlock(u32 cluster) const {
return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_);}
u32 blockNumber(u32 cluster, u32 position) const {
return clusterStartBlock(cluster) + blockOfCluster(position);}
u8 fatGet(u32 cluster, u32* value) const;
u8 fatPut(u32 cluster, u32 value);
u8 fatPutEOC(u32 cluster) {
return fatPut(cluster, 0x0FFFFFFF);
}
u8 chainSize(u32 beginCluster, u32* size) const;
u8 freeChain(u32 cluster);
u8 isEOC(u32 cluster) const {
return cluster >= (fatType_ == 16 ? FAT16EOC_MIN : FAT32EOC_MIN);
}
u8 readData(u32 block, u16 offset, u16 count, u8* dst);
u8 writeBlock(u32 block, const u8* dst);
};
class EmuFat
{
public:
EmuFat(const char* fname, bool readonly=false);
EmuFat();
EmuFat(EMUFILE* fileNotToDelete);
virtual ~EmuFat();
private:
EMUFILE* m_pFile;
bool m_readonly, m_owns;
friend class EmuFatVolume;
friend class EmuFatFile;
// value for action argument in cacheRawBlock to indicate read from cache
static const u8 CACHE_FOR_READ = 0;
// value for action argument in cacheRawBlock to indicate cache dirty
static const u8 CACHE_FOR_WRITE = 1;
struct Cache
{
Cache()
: cacheBlockNumber_(0xFFFFFFFF)
, cacheDirty_(0) // cacheFlush() will write block if true
, cacheMirrorBlock_(0) // mirror block for second FAT
{}
cache_t cacheBuffer_; // 512 byte cache for device blocks
u32 cacheBlockNumber_; // Logical number of block in the cache
u8 cacheDirty_; // cacheFlush() will write block if true
u32 cacheMirrorBlock_; // block number for mirror FAT
} cache_;
u8 cacheRawBlock(u32 blockNumber, u8 action);
void cacheSetDirty() {cache_.cacheDirty_ |= CACHE_FOR_WRITE;}
u8 cacheZeroBlock(u32 blockNumber);
u8 cacheFlush();
//IO functions:
u8 readBlock(u32 block, u8* dst);
u8 writeBlock(u32 blockNumber, const u8* src);
u8 readData(u32 block, u16 offset, u16 count, u8* dst);
void truncate(u32 size);
};
#endif //EMUFAT_H

View File

@ -0,0 +1,6 @@
#ifndef EMUFAT_TYPES_H
#define EMUFAT_TYPES_H
#include "types.h"
#endif //EMUFAT_TYPES_H

View File

@ -1,4 +1,3 @@
#include "types.h"
#include "emufile.h" #include "emufile.h"
#include <vector> #include <vector>
@ -12,3 +11,17 @@ bool EMUFILE::readAllBytes(std::vector<u8>* dstbuf, const std::string& fname)
file.fread(&dstbuf->at(0),size); file.fread(&dstbuf->at(0),size);
return true; return true;
} }
EMUFILE* EMUFILE::memwrap(EMUFILE* fp)
{
EMUFILE_FILE* file;
EMUFILE_MEMORY* mem;
file = dynamic_cast<EMUFILE_FILE*>(fp);
mem = dynamic_cast<EMUFILE_MEMORY*>(fp);
if(mem) return mem;
mem = new EMUFILE_MEMORY(file->size());
if(file->size()==0) return mem;
file->fread(mem->buf(),file->size());
delete file;
return mem;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 DeSmuME team /* Copyright (C) 2009-2010 DeSmuME team
* *
* This file is part of DeSmuME * This file is part of DeSmuME
* *
@ -17,18 +17,25 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
//don't use emufile for files bigger than 2GB! you have been warned! some day this will be fixed.
#ifndef EMUFILE_H #ifndef EMUFILE_H
#define EMUFILE_H #define EMUFILE_H
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "types.h"
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include <stdarg.h> #include <stdarg.h>
#include "types.h"
#ifdef _MSC_VER
#include <io.h>
#endif
#ifdef _XBOX #ifdef _XBOX
#undef min; #undef min;
#undef max; #undef max;
@ -43,11 +50,16 @@ public:
: failbit(false) : failbit(false)
{} {}
//takes control of the provided EMUFILE and returns a new EMUFILE which is guranteed to be in memory
static EMUFILE* memwrap(EMUFILE* fp);
virtual ~EMUFILE() {} virtual ~EMUFILE() {}
static bool readAllBytes(std::vector<u8>* buf, const std::string& fname); static bool readAllBytes(std::vector<u8>* buf, const std::string& fname);
bool fail() { return failbit; } bool fail(bool unset=false) { bool ret = failbit; if(unset) unfail(); return ret; }
void unfail() { failbit=false; }
bool eof() { return size()==ftell(); } bool eof() { return size()==ftell(); }
@ -78,6 +90,8 @@ public:
virtual int ftell() = 0; virtual int ftell() = 0;
virtual int size() = 0; virtual int size() = 0;
virtual void truncate(s32 length) = 0;
}; };
//todo - handle read-only specially? //todo - handle read-only specially?
@ -94,14 +108,29 @@ protected:
public: public:
EMUFILE_MEMORY(std::vector<u8> *underlying) : vec(underlying), ownvec(false), pos(0), len(underlying->size()) { } EMUFILE_MEMORY(std::vector<u8> *underlying) : vec(underlying), ownvec(false), pos(0), len((s32)underlying->size()) { }
EMUFILE_MEMORY(u32 preallocate) : vec(new std::vector<u8>()), ownvec(true), pos(0), len(0) { vec->reserve(preallocate); } EMUFILE_MEMORY(u32 preallocate) : vec(new std::vector<u8>()), ownvec(true), pos(0), len(0) {
vec->resize(preallocate);
len = preallocate;
}
EMUFILE_MEMORY() : vec(new std::vector<u8>()), ownvec(true), pos(0), len(0) { vec->reserve(1024); } EMUFILE_MEMORY() : vec(new std::vector<u8>()), ownvec(true), pos(0), len(0) { vec->reserve(1024); }
EMUFILE_MEMORY(void* buf, s32 size) : vec(new std::vector<u8>()), ownvec(true), pos(0), len(size) {
vec->resize(size);
if(size != 0)
memcpy(&vec[0],buf,size);
}
~EMUFILE_MEMORY() { ~EMUFILE_MEMORY() {
if(ownvec) delete vec; if(ownvec) delete vec;
} }
virtual void truncate(s32 length)
{
vec->resize(length);
len = length;
if(pos>length) pos=length;
}
u8* buf() { return &(*vec)[0]; } u8* buf() { return &(*vec)[0]; }
std::vector<u8>* get_vec() { return vec; }; std::vector<u8>* get_vec() { return vec; };
@ -124,9 +153,19 @@ public:
virtual int fgetc() { virtual int fgetc() {
u8 temp; u8 temp;
if(_fread(&temp,1) != 1)
return EOF; //need an optimized codepath
else return temp; //if(_fread(&temp,1) != 1)
// return EOF;
//else return temp;
u32 remain = len-pos;
if(remain<1) {
failbit = true;
return -1;
}
temp = buf()[pos];
pos++;
return temp;
} }
virtual int fputc(int c) { virtual int fputc(int c) {
u8 temp = (u8)c; u8 temp = (u8)c;
@ -151,9 +190,9 @@ public:
//they handle the return values correctly //they handle the return values correctly
virtual void fwrite(const void *ptr, size_t bytes){ virtual void fwrite(const void *ptr, size_t bytes){
reserve(pos+bytes); reserve(pos+(s32)bytes);
memcpy(buf()+pos,ptr,bytes); memcpy(buf()+pos,ptr,bytes);
pos += bytes; pos += (s32)bytes;
len = std::max(pos,len); len = std::max(pos,len);
} }
@ -180,6 +219,11 @@ public:
return pos; return pos;
} }
void trim()
{
vec->resize(len);
}
virtual int size() { return (int)len; } virtual int size() { return (int)len; }
}; };
@ -187,14 +231,18 @@ class EMUFILE_FILE : public EMUFILE {
protected: protected:
FILE* fp; FILE* fp;
public: private:
void open(const char* fname, const char* mode)
EMUFILE_FILE(const char* fname, const char* mode)
{ {
fp = fopen(fname,mode); fp = fopen(fname,mode);
if(!fp) if(!fp)
failbit = true; failbit = true;
}; }
public:
EMUFILE_FILE(const std::string& fname, const char* mode) { open(fname.c_str(),mode); }
EMUFILE_FILE(const char* fname, const char* mode) { open(fname,mode); }
virtual ~EMUFILE_FILE() { virtual ~EMUFILE_FILE() {
if(NULL != fp) if(NULL != fp)
@ -205,6 +253,17 @@ public:
return fp; return fp;
} }
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 int fprintf(const char *format, ...) { virtual int fprintf(const char *format, ...) {
va_list argptr; va_list argptr;
va_start(argptr, format); va_start(argptr, format);

View File

@ -1,20 +1,20 @@
/* Copyright (C) 2006 Guillaume Duhamel /* Copyright (C) 2006 Guillaume Duhamel
This file is part of DeSmuME This file is part of DeSmuME
DeSmuME is free software; you can redistribute it and/or modify DeSmuME is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
(at your option) any later version. (at your option) any later version.
DeSmuME is distributed in the hope that it will be useful, DeSmuME is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with DeSmuME; if not, write to the Free Software along with DeSmuME; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#ifndef FS_H #ifndef FS_H
@ -33,7 +33,7 @@ typedef struct {
char cFileName[256]; char cFileName[256];
char cAlternateFileName[14]; char cAlternateFileName[14];
u32 flags; u32 flags;
u32 fileSize; u32 fileSize;
} FsEntry; } FsEntry;
void * FsReadFirst(const char * path, FsEntry * entry); void * FsReadFirst(const char * path, FsEntry * entry);

View File

@ -1842,6 +1842,18 @@
RelativePath="..\driver.h" RelativePath="..\driver.h"
> >
</File> </File>
<File
RelativePath="..\emufat.cpp"
>
</File>
<File
RelativePath="..\emufat.h"
>
</File>
<File
RelativePath="..\emufat_types.h"
>
</File>
<File <File
RelativePath="..\emufile.cpp" RelativePath="..\emufile.cpp"
> >

View File

@ -228,6 +228,7 @@
/> />
<Tool <Tool
Name="VCCLCompilerTool" Name="VCCLCompilerTool"
AdditionalOptions="/MP"
Optimization="2" Optimization="2"
InlineFunctionExpansion="2" InlineFunctionExpansion="2"
EnableIntrinsicFunctions="true" EnableIntrinsicFunctions="true"
@ -710,6 +711,18 @@
RelativePath="..\driver.h" RelativePath="..\driver.h"
> >
</File> </File>
<File
RelativePath="..\emufat.cpp"
>
</File>
<File
RelativePath="..\emufat.h"
>
</File>
<File
RelativePath="..\emufat_types.h"
>
</File>
<File <File
RelativePath="..\emufile.cpp" RelativePath="..\emufile.cpp"
> >

View File

@ -445,6 +445,7 @@
<ClCompile Include="..\debug.cpp" /> <ClCompile Include="..\debug.cpp" />
<ClCompile Include="..\Disassembler.cpp" /> <ClCompile Include="..\Disassembler.cpp" />
<ClCompile Include="..\driver.cpp" /> <ClCompile Include="..\driver.cpp" />
<ClCompile Include="..\emufat.cpp" />
<ClCompile Include="..\emufile.cpp" /> <ClCompile Include="..\emufile.cpp" />
<ClCompile Include="..\FIFO.cpp" /> <ClCompile Include="..\FIFO.cpp" />
<ClCompile Include="..\firmware.cpp" /> <ClCompile Include="..\firmware.cpp" />
@ -559,6 +560,8 @@
<ClInclude Include="..\debug.h" /> <ClInclude Include="..\debug.h" />
<ClInclude Include="..\Disassembler.h" /> <ClInclude Include="..\Disassembler.h" />
<ClInclude Include="..\driver.h" /> <ClInclude Include="..\driver.h" />
<ClInclude Include="..\emufat.h" />
<ClInclude Include="..\emufat_types.h" />
<ClInclude Include="..\emufile.h" /> <ClInclude Include="..\emufile.h" />
<ClInclude Include="..\fat.h" /> <ClInclude Include="..\fat.h" />
<ClInclude Include="..\FIFO.h" /> <ClInclude Include="..\FIFO.h" />

View File

@ -396,6 +396,9 @@
<ClCompile Include="..\addons\slot1_retail.cpp"> <ClCompile Include="..\addons\slot1_retail.cpp">
<Filter>Core\addons</Filter> <Filter>Core\addons</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\emufat.cpp">
<Filter>Core</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\addons.h"> <ClInclude Include="..\addons.h">
@ -728,6 +731,10 @@
<ClInclude Include="filter\hq2x.h" /> <ClInclude Include="filter\hq2x.h" />
<ClInclude Include="filter\interp.h" /> <ClInclude Include="filter\interp.h" />
<ClInclude Include="filter\lq2x.h" /> <ClInclude Include="filter\lq2x.h" />
<ClInclude Include="..\emufat.h">
<Filter>Core</Filter>
</ClInclude>
<ClInclude Include="..\emufat_types.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\instruction_tabdef.inc"> <None Include="..\instruction_tabdef.inc">