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 \
debug.cpp debug.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 \
GPU_osd.h \
mem.h mc.cpp mc.h \

View File

@ -1,7 +1,6 @@
/* Copyright (C) 2006 yopyop
Copyright (C) 2006 Mic
Copyright (C) 2009 CrazyMax
Copyright (C) 2009 DeSmuME team
Copyright (C) 2009-2010 DeSmuME team
This file is part of DeSmuME
@ -28,6 +27,9 @@
#include <stdio.h>
#include <stdlib.h>
#include "../emufat.h"
#include <stack>
#include <fcntl.h>
#include <sys/stat.h>
#ifdef _MSC_VER
@ -77,9 +79,6 @@ typedef struct {
#define CF_CMD_READ 0x20
#define CF_CMD_WRITE 0x30
static int disk_image = -1;
static off_t file_size;
static u16 cf_reg_sts,
cf_reg_lba1,
cf_reg_lba2,
@ -119,6 +118,8 @@ static BOOL cflashDeviceEnabled = FALSE;
static std::string sFlashPath;
static EMUFILE* file;
// ===========================
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
static void list_files(const char *filepath)
static void list_files(const char *filepath, ListCallback list_callback)
{
char DirSpec[255+1], SubDir[255+1];
FsEntry entry;
@ -265,12 +272,14 @@ static void list_files(const char *filepath)
if (hFind == NULL) return;
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)
{
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);
if (numFiles==MAXFILES-1) break;
@ -280,7 +289,8 @@ static void list_files(const char *filepath)
if (strlen(fname)+strlen(filepath)+2 < 256)
{
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);
if (dwError != FS_ERR_NO_MORE_FILES) return;
if (numFiles < MAXFILES)
{
fileLink[numFiles].parent = fileLevel;
files[numFiles++].name[0] = 0;
}
//if (numFiles < MAXFILES)
//{
// fileLink[numFiles].parent = fileLevel;
// 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
static BOOL cflash_build_fat()
{
int i,j,k,l,
clust,numClusters,
clusterNum2,rootCluster;
int fileLevel;
dataSectors = 0;
currPath = sFlashPath;
list_files(sFlashPath.c_str(), count_ListCallback);
numFiles = 0;
fileLevel = -1;
maxLevel = -1;
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)
delete file;
file = new EMUFILE_MEMORY(dataSectors*512+1);
EmuFat fat(file);
EmuFatVolume vol;
u8 ok = vol.init(&fat);
vol.format(dataSectors);
files = (DIR_ENT *) malloc(MAXFILES*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;
}
reconstruct(&currFatFile);
currFatFile.openRoot(&vol);
for (i=0; i<MAXFILES; i++)
{
files[i].attrib = 0;
files[i].name[0] = FILE_FREE;
files[i].fileSize = 0;
list_files(sFlashPath.c_str(), build_ListCallback);
fileLink[i].filesInDir = 0;
FILE* outf = fopen("d:\\test.ima","wb");
EMUFILE_MEMORY* memf = (EMUFILE_MEMORY*)file;
fwrite(memf->buf(),1,memf->size(),outf);
fclose(outf);
extraDirEntries[i] = NULL;
numExtraEntries[i] = 0;
}
list_files(sFlashPath.c_str());
//int i,j,k,l,
//clust,numClusters,
//clusterNum2,rootCluster;
//int fileLevel;
k = 0;
clusterNum = rootCluster = (SECRESV + SECPERFAT)/SECPERCLUS;
numClusters = 0;
clust = 0;
numRootFiles = 0;
//numFiles = 0;
//fileLevel = -1;
//maxLevel = -1;
// Allocate memory to hold information about the files
dirEntries = (DIR_ENT *) malloc(numFiles*sizeof(DIR_ENT));
if (dirEntries==NULL) return FALSE;
//files = (DIR_ENT *) malloc(MAXFILES*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;
//}
dirEntryLink = (FILE_INFO *) malloc(numFiles*sizeof(FILE_INFO));
if (dirEntryLink==NULL)
{
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;
}
//for (i=0; i<MAXFILES; i++)
//{
// files[i].attrib = 0;
// files[i].name[0] = FILE_FREE;
// files[i].fileSize = 0;
memset(dirEntriesInCluster, 0, NUMCLUSTERS*sizeof(int));
memset(dirEntryPtr, NULL, NUMCLUSTERS*sizeof(DIR_ENT*));
// fileLink[i].filesInDir = 0;
// 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++;
}
// extraDirEntries[i] = NULL;
// numExtraEntries[i] = 0;
//}
// Free the file indexing buffer
free(files);
free(fileLink);
//list_files(sFlashPath.c_str());
// 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;
//k = 0;
//clusterNum = rootCluster = (SECRESV + SECPERFAT)/SECPERCLUS;
//numClusters = 0;
//clust = 0;
//numRootFiles = 0;
filesysFAT = 0 + MBR.reservedSectors;
filesysRootDir = filesysFAT + (MBR.numFATs * MBR.sectorsPerFAT);
filesysData = filesysRootDir + ((MBR.rootEntries * sizeof(DIR_ENT)) / 512);
//// Allocate memory to hold information about the files
//dirEntries = (DIR_ENT *) malloc(numFiles*sizeof(DIR_ENT));
//if (dirEntries==NULL) return FALSE;
// 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;
//dirEntryLink = (FILE_INFO *) malloc(numFiles*sizeof(FILE_INFO));
//if (dirEntryLink==NULL)
//{
// 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;
//}
// 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;
//memset(dirEntriesInCluster, 0, NUMCLUSTERS*sizeof(int));
//memset(dirEntryPtr, NULL, NUMCLUSTERS*sizeof(DIR_ENT*));
// 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;
}
}
//// 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++;
//}
for (i=(filesysData/SECPERCLUS); i<NUMCLUSTERS; i++)
{
if (dirEntriesInCluster[i]==256)
FAT16[i] = i+1;
}
//// 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;
}
@ -540,25 +644,12 @@ static BOOL cflash_init()
{
sFlashPath = CFlash_Path;
INFO("Using CFlash disk image file %s\n", sFlashPath.c_str());
disk_image = OPEN_FN( sFlashPath.c_str(), OPEN_MODE);
if ( disk_image != -1)
file = new EMUFILE_FILE(sFlashPath.c_str(),"rb+");
if(file->fail())
{
file_size = LSEEK_FN( disk_image, 0, SEEK_END);
if (0 && file_size == -1)
{
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;
}
CFLASHLOG("Failed to open file %s\n", sFlashPath.c_str());
delete file;
}
else
// TODO: create image if not exist
CFLASHLOG("Failed to open file %s: \"%s\"\n", sFlashPath.c_str(), strerror( errno));
}
// READY
@ -692,126 +783,16 @@ static unsigned int cflash_read(unsigned int address)
case CF_REG_DATA:
if (cf_reg_cmd == CF_CMD_READ)
{
if (!CFlash_IsUsingPath())
if(file)
{
if ( disk_image != -1)
{
u8 data[2];
#if 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;
u8 data[2];
file->fseek(currLBA, SEEK_SET);
elems_read += file->fread(data,2);
ret_value = data[1] << 8 | data[0];
}
currLBA += 2;
}
break;
break;
case CF_REG_CMD:
break;
@ -838,7 +819,6 @@ static void cflash_write(unsigned int address,unsigned int data)
case CF_REG_DATA:
if (cf_reg_cmd == CF_CMD_WRITE)
{
if (!CFlash_IsUsingPath())
{
sector_data[sector_write_index] = (data >> 0) & 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);
size_t written = 0;
if (currLBA + 512 < file_size)
{
if (disk_image != -1)
//TODO - calling size() every time we write something is sort of unnecessarily slow in the case of disk-backed files...
if(file)
if(currLBA + 512 < file->size())
{
LSEEK_FN( disk_image, currLBA, SEEK_SET);
file->fseek(currLBA,SEEK_SET);
while(written < 512)
{
size_t cur_write =
WRITE_FN( disk_image, &sector_data[written], 512 - written);
size_t todo = 512-written;
file->fwrite(&sector_data[written], todo);
size_t cur_write = todo;
written += cur_write;
if ( cur_write == (size_t)-1) break;
}
}
}
CFLASHLOG("Wrote %u bytes\n", written);
@ -873,9 +852,6 @@ static void cflash_write(unsigned int address,unsigned int data)
sector_write_index = 0;
}
}
else // TODO: write to real partition
{
}
}
break;
@ -917,11 +893,8 @@ static void cflash_close( void)
if (!inited) return;
if (!CFlash_IsUsingPath())
{
if (disk_image != -1)
{
CLOSE_FN(disk_image);
disk_image = -1;
}
delete file;
file = NULL;
}
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 <vector>
@ -11,4 +10,18 @@ bool EMUFILE::readAllBytes(std::vector<u8>* dstbuf, const std::string& fname)
dstbuf->resize(size);
file.fread(&dstbuf->at(0),size);
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
*
@ -17,18 +17,25 @@
* 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
#define EMUFILE_H
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "types.h"
#include <vector>
#include <algorithm>
#include <string>
#include <stdarg.h>
#include "types.h"
#ifdef _MSC_VER
#include <io.h>
#endif
#ifdef _XBOX
#undef min;
#undef max;
@ -43,11 +50,16 @@ public:
: 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() {}
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(); }
@ -78,6 +90,8 @@ public:
virtual int ftell() = 0;
virtual int size() = 0;
virtual void truncate(s32 length) = 0;
};
//todo - handle read-only specially?
@ -94,14 +108,29 @@ protected:
public:
EMUFILE_MEMORY(std::vector<u8> *underlying) : vec(underlying), ownvec(false), pos(0), len(underlying->size()) { }
EMUFILE_MEMORY(u32 preallocate) : vec(new std::vector<u8>()), ownvec(true), pos(0), len(0) { vec->reserve(preallocate); }
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->resize(preallocate);
len = preallocate;
}
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() {
if(ownvec) delete vec;
}
virtual void truncate(s32 length)
{
vec->resize(length);
len = length;
if(pos>length) pos=length;
}
u8* buf() { return &(*vec)[0]; }
std::vector<u8>* get_vec() { return vec; };
@ -124,9 +153,19 @@ public:
virtual int fgetc() {
u8 temp;
if(_fread(&temp,1) != 1)
return EOF;
else return temp;
//need an optimized codepath
//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) {
u8 temp = (u8)c;
@ -151,9 +190,9 @@ public:
//they handle the return values correctly
virtual void fwrite(const void *ptr, size_t bytes){
reserve(pos+bytes);
reserve(pos+(s32)bytes);
memcpy(buf()+pos,ptr,bytes);
pos += bytes;
pos += (s32)bytes;
len = std::max(pos,len);
}
@ -180,6 +219,11 @@ public:
return pos;
}
void trim()
{
vec->resize(len);
}
virtual int size() { return (int)len; }
};
@ -187,14 +231,18 @@ class EMUFILE_FILE : public EMUFILE {
protected:
FILE* fp;
public:
EMUFILE_FILE(const char* fname, const char* mode)
private:
void open(const char* fname, const char* mode)
{
fp = fopen(fname,mode);
if(!fp)
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() {
if(NULL != fp)
@ -205,6 +253,17 @@ public:
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, ...) {
va_list argptr;
va_start(argptr, format);

View File

@ -1,20 +1,20 @@
/* 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
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
DeSmuME 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 2 of the License, or
(at your option) any later version.
DeSmuME 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.
DeSmuME 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 DeSmuME; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
You should have received a copy of the GNU General Public License
along with DeSmuME; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef FS_H
@ -33,7 +33,7 @@ typedef struct {
char cFileName[256];
char cAlternateFileName[14];
u32 flags;
u32 fileSize;
u32 fileSize;
} FsEntry;
void * FsReadFirst(const char * path, FsEntry * entry);

View File

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

View File

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

View File

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

View File

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