libfat/nds/source/nitrofs.c
Michael Chisholm 2cc2cc2e39 Moved disc interfaces to appropriate libraries (DLDI to libnds, SD/USB to libogc).
Changed read/write functions to use size_t and seek function to use off_t -- will help on 64 bit platforms.
Partitions no longer specified by fat1:/, fat2:/, etc.
Use "nice" device names. On GBA/NDS the default partition is still fat:/; however on Wii and GC sd:/, usb:/, etc. are used instead.
Added fsync and ftruncate functions.
Mount and unmount no longer startup/shutdown the device, this is the job of the host app.
Mount now accepts a partition start parameter.
Used standard types, eg uint32_t instead of u32.
Archive bit is set when closing/synchronizing a modified file.
Various minor bug fixes.
2008-11-20 05:22:28 +00:00

374 lines
14 KiB
C

/*
nitrofs.c - eris's wai ossum nitro filesystem device driver
Based on information found at http://frangoassado.org/ds/rom_spec.txt and from the #dsdev ppls
Kallisti (K) 2008-01-26 All rights reversed.
*/
#include <nds.h>
#include <string.h>
#include <fat.h>
#include <sys/dir.h>
#include <sys/iosupport.h>
#include <stdio.h>
DIR_ITER* nitroFSDirOpen(struct _reent *r, DIR_ITER *dirState, const char *path);
int nitroDirReset(struct _reent *r, DIR_ITER *dirState);
int nitroFSDirNext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st);
int nitroFSDirClose(struct _reent *r, DIR_ITER *dirState);
int nitroFSOpen(struct _reent *r, void *fileStruct, const char *path,int flags,int mode);
int nitroFSClose(struct _reent *r,int fd);
ssize_t nitroFSRead(struct _reent *r,int fd,char *ptr,size_t len);
off_t nitroFSSeek(struct _reent *r,int fd,off_t pos,int dir);
int nitroFSFstat(struct _reent *r,int fd,struct stat *st);
#define LOADERSTR "PASS" //look for this
#define LOADERSTROFFSET 0xac
#define FNTOFFSET 0x40
#define FATOFFSET 0x48
#define NITRONAMELENMAX 0x80 //max file name is 127 +1 for zero byte :D
#define NITROMAXPATHLEN 0x100 //256 bytes enuff?
#define NITROROOT 0xf000 //root entry_file_id
#define NITRODIRMASK 0x0fff //remove leading 0xf
#define NITROISDIR 0x80 //mask to indicate this name entry is a dir, other 7 bits = name length
//Directory filename subtable entry structure
struct ROM_FNTDir {
u32 entry_start;
u16 entry_file_id;
u16 parent_id;
};
//Yo, dis table is fat (describes the structures
struct ROM_FAT {
u32 top; //start of file in rom image
u32 bottom; //end of file in rom image
};
//used (mostly by the 'sub' functions) for the raw reading of rom image either via gba rom or dldi
struct nitroRawStruct {
FILE *ndsFile; //used if going thru dldi >_> (if set to null we assume GBA with loader)
unsigned int pos; //where in the file am i?
};
struct nitroFSStruct {
struct nitroRawStruct nrs;
unsigned int start; //where in the rom this file starts
unsigned int end; //where in the rom this file ends
// unsigned int pos; //where in current file are we?
};
struct nitroDIRStruct {
struct nitroRawStruct nrs;
unsigned int namepos; //ptr to next name to lookup in list
struct ROM_FAT romfat;
u16 entry_id; //which entry this is (for files only) incremented with each new file in dir?
u16 dir_id; //which directory entry this is.. used ofc for dirs only
u16 cur_dir_id; //which directory entry we are using
u16 parent_id; //who is the parent of the current directory (this can be used to easily ../ )
};
//Globals!
u32 fntOffset; //offset to start of filename table
u32 fatOffset; //offset to start of file alloc table
bool isNdsFile; //is it a nds file?
const char *ndsfilename; //what nds file to use
devoptab_t nitroFSdevoptab={
"nitro", // const char *name;
sizeof(struct nitroFSStruct), // int structSize;
&nitroFSOpen, // int (*open_r)(struct _reent *r, void *fileStruct, const char *path,int flags,int mode);
&nitroFSClose, // int (*close_r)(struct _reent *r,int fd);
NULL, // ssize_t (*write_r)(struct _reent *r,int fd,const char *ptr,size_t len);
&nitroFSRead, // ssize_t (*read_r)(struct _reent *r,int fd,char *ptr,size_t len);
&nitroFSSeek, // off_t (*seek_r)(struct _reent *r,int fd,off_t pos,int dir);
&nitroFSFstat, // int (*fstat_r)(struct _reent *r,int fd,struct stat *st);
NULL, // int (*stat_r)(struct _reent *r,const char *file,struct stat *st);
NULL, // int (*link_r)(struct _reent *r,const char *existing, const char *newLink);
NULL, // int (*unlink_r)(struct _reent *r,const char *name);
NULL, // int (*chdir_r)(struct _reent *r,const char *name);
NULL, // int (*rename_r) (struct _reent *r, const char *oldName, const char *newName);
NULL, // int (*mkdir_r) (struct _reent *r, const char *path, int mode);
sizeof(struct nitroDIRStruct), // int dirStateSize;
&nitroFSDirOpen, // DIR_ITER* (*diropen_r)(struct _reent *r, DIR_ITER *dirState, const char *path);
&nitroDirReset, // int (*dirreset_r)(struct _reent *r, DIR_ITER *dirState);
&nitroFSDirNext, // int (*dirnext_r)(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
&nitroFSDirClose, // int (*dirclose_r)(struct _reent *r, DIR_ITER *dirState);
NULL, // int (*statvfs_r)(struct _reent *r, const char *path, struct statvfs *buf);
NULL, // int (*ftruncate_r)(struct _reent *r, int fd, off_t len);
NULL, // int (*fsync_r)(struct _reent *r, int fd);
NULL, // void *deviceData;
};
//inline these mebbe? these 4 'sub' functions deal with actually reading from either gba rom or .nds file :)
//what i rly rly rly wanna know is how an actual nds cart reads from itself, but it seems no one can tell me ~_~
//so, instead we have this weird weird haxy try gbaslot then try dldi method. If i (or you!!) ever do figure out
//how to read the proper way can replace these 4 functions and everything should work normally :)
int nitroSubOpen(struct nitroRawStruct *nrs) {
if(isNdsFile) {
if((nrs->ndsFile = fopen(ndsfilename,"rb+"))) {
nrs->pos=0;
return 1;
}
} else {
nrs->ndsFile=(FILE*)NULL;
nrs->pos=0;
return 1;
}
return(0);
}
int nitroSubClose(struct nitroRawStruct *nrs) {
if(isNdsFile)
return(fclose(nrs->ndsFile));
return(0);
}
//reads from rom image either gba rom or dldi
int nitroSubRead(struct nitroRawStruct *nrs, void *ptr, int len) {
if(isNdsFile) { //read from ndsfile
len=fread(ptr,1,len,nrs->ndsFile);
} else { //reading from gbarom
memcpy(ptr, nrs->pos+(void*)GBAROM,len); //len isnt checked here because other checks exist in the callers (hopefully)
}
nrs->pos+=len;
return(len);
}
int nitroSubSeek(struct nitroRawStruct *nrs, int pos, int dir) {
if(dir==SEEK_SET) //otherwise just set the pos :)
nrs->pos=pos;
else if(dir==SEEK_CUR)
nrs->pos+=pos; //see ez!
if(isNdsFile) { //read from ndsfile actually do a seek
return(fseek(nrs->ndsFile,pos,dir));
} else {
return(nrs->pos);
}
}
//Figure out if its gba or ds, setup stuff
bool nitroFSInit() {
struct nitroRawStruct nrs;
char romstr[0x10];
sysSetCartOwner(BUS_OWNER_ARM9 );
if(strncmp(((const char *)GBAROM)+LOADERSTROFFSET,LOADERSTR,strlen(LOADERSTR))==0) { // found standard nds file in gba cart
fntOffset=((u32)*(u32*)(((const char *)GBAROM)+FNTOFFSET));
fatOffset=((u32)*(u32*)(((const char *)GBAROM)+FATOFFSET));
isNdsFile=false;
AddDevice(&nitroFSdevoptab);
return true;
} else { //okay then try something else ~_~
isNdsFile=true;
if ( __system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc >= 1 ) {
ndsfilename=__system_argv->argv[0]; //set global for what file.nds to use
if (!fatInitDefault()) return false;
if(nitroSubOpen(&nrs)) {
nitroSubSeek(&nrs,LOADERSTROFFSET,SEEK_SET);
nitroSubRead(&nrs,romstr,strlen(LOADERSTR));
if(strncmp(romstr,LOADERSTR,strlen(LOADERSTR))!=0) return false;
nitroSubSeek(&nrs,FNTOFFSET,SEEK_SET);
nitroSubRead(&nrs,&fntOffset,sizeof(fntOffset));
nitroSubSeek(&nrs,FATOFFSET,SEEK_SET);
nitroSubRead(&nrs,&fatOffset,sizeof(fatOffset));
nitroSubClose(&nrs);
AddDevice(&nitroFSdevoptab);
return true;
}
}
}
return false;
}
//Directory functs
DIR_ITER* nitroFSDirOpen(struct _reent *r, DIR_ITER *dirState, const char *path) {
struct nitroDIRStruct *dirStruct=(struct nitroDIRStruct*)dirState->dirStruct; //this makes it lots easier!
struct stat st;
char dirname[NITRONAMELENMAX];
char *cptr;
char mydirpath[NITROMAXPATHLEN]; //to hold copy of path string
char *dirpath=mydirpath;
//NOTE: might add prepending of chdir path? seems like lotta work for something silly that shoulda been handled in newlib anyways ~_~
bool pathfound;
if((cptr=strchr(path,':')))
path=cptr+1; //move path past any device names (if it was nixy style wouldnt need this step >_>)
strncpy(dirpath,path,sizeof(mydirpath)-1); //copy the string (as im gonna mutalate it)
nitroSubOpen(&dirStruct->nrs); //open a file for me to use
dirStruct->cur_dir_id=NITROROOT; //first root dir
nitroDirReset(r,dirState); //set dir to current path
do {
while((cptr=strchr(dirpath,'/'))==dirpath) {
dirpath++; //move past any leading / or // together
}
if(cptr)
*cptr=0; //erase /
//here is prolly where you should handle .. and . filenames if desired
if(*dirpath==0) {//are we at the end of the path string?? if so there is nothing to search for we're already here !
pathfound=true; //mostly this handles searches for root or / or no path specified cases
break;
}
pathfound=false;
while(nitroFSDirNext(r,dirState,dirname,&st)==0) {
if((st.st_mode==S_IFDIR) && !(strcmp(dirname,dirpath))) { //if its a directory and name matches dirpath
dirStruct->cur_dir_id=dirStruct->dir_id; //move us to the next dir in tree
nitroDirReset(r,dirState); //set dir to current path we just found...
pathfound=true;
break;
}
};
if(!pathfound)
break;
dirpath=cptr+1; //move to right after last / we found
} while(cptr); // go till after the last /
if(pathfound) {
return(dirState);
} else {
nitroSubClose(&dirStruct->nrs); //oops almost forgot to close the file we opened <_<
return(NULL);
}
}
int nitroFSDirClose(struct _reent *r, DIR_ITER *dirState) {
return(nitroSubClose(&((struct nitroDIRStruct*)dirState->dirStruct)->nrs));
}
//reset dir to start of entry selected by dirStruct->cur_dir_id which should be set in dirOpen
int nitroDirReset(struct _reent *r, DIR_ITER *dirState) {
struct nitroDIRStruct *dirStruct=(struct nitroDIRStruct*)dirState->dirStruct; //this makes it lots easier!
struct ROM_FNTDir dirsubtable;
nitroSubSeek(&dirStruct->nrs,fntOffset+((dirStruct->cur_dir_id&NITRODIRMASK)*sizeof(struct ROM_FNTDir)),SEEK_SET);
nitroSubRead(&dirStruct->nrs, &dirsubtable, sizeof(dirsubtable));
dirStruct->namepos=dirsubtable.entry_start; //set namepos to first entry in this dir's table
dirStruct->entry_id=dirsubtable.entry_file_id; //get number of first file ID in this branch
dirStruct->parent_id=dirsubtable.parent_id; //save parent ID in case we wanna add ../ functionality
return(0);
}
int nitroFSDirNext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st) {
unsigned char next;
struct nitroDIRStruct *dirStruct=(struct nitroDIRStruct*)dirState->dirStruct; //this makes it lots easier!
nitroSubSeek(&dirStruct->nrs,fntOffset+dirStruct->namepos,SEEK_SET);
nitroSubRead(&dirStruct->nrs, &next , sizeof(next));
// next: high bit 0x80 = entry isdir.. other 7 bits r size, the 16 bits following name are dir's entryid (starts with f000)
// 00 = endoftable //
if(next) {
if(next&NITROISDIR) {
if(st) st->st_mode=S_IFDIR;
next&=NITROISDIR^0xff; //invert bits and mask off 0x80
nitroSubRead(&dirStruct->nrs,filename,next);
// nitroSubRead(&dirStruct->nrs,&dirStruct->dir_id,sizeof(struct nitroDIRStruct.dir_id)); //read the dir_id
//grr cant get the struct member size?, just wanna test it so moving on...
nitroSubRead(&dirStruct->nrs,&dirStruct->dir_id,sizeof(u16)); //read the dir_id
dirStruct->namepos+=next+sizeof(u16)+1; //now we points to next one plus dir_id size:D
} else {
if(st) st->st_mode=0;
nitroSubRead(&dirStruct->nrs,filename,next);
dirStruct->namepos+=next+1; //now we points to next one :D
//read file info to get filesize (and for fileopen)
nitroSubSeek(&dirStruct->nrs,fatOffset+(dirStruct->entry_id*sizeof(struct ROM_FAT)),SEEK_SET);
nitroSubRead(&dirStruct->nrs, &dirStruct->romfat, sizeof(dirStruct->romfat)); //retrieve romfat entry (contains filestart and end positions)
dirStruct->entry_id++; //advance ROM_FNTStrFile ptr
if(st) st->st_size=dirStruct->romfat.bottom-dirStruct->romfat.top; //calculate filesize
}
filename[(int)next]=0; //zero last char
return(0);
} else {
return(-1);
}
}
//fs functs
int nitroFSOpen(struct _reent *r, void *fileStruct, const char *path,int flags,int mode) {
struct nitroFSStruct *fatStruct=(struct nitroFSStruct *)fileStruct;
struct nitroDIRStruct dirStruct;
DIR_ITER dirState;
dirState.dirStruct=&dirStruct; //create a temp dirstruct
struct _reent dre;
struct stat st; //all these are just used for reading the dir ~_~
char dirfilename[NITROMAXPATHLEN]; // to hold a full path (i tried to avoid using so much stack but blah :/)
char *filename; // to hold filename
char *cptr; //used to string searching and manipulation
cptr=(char*)path+strlen(path); //find the end...
filename=NULL;
do {
if((*cptr=='/') || (*cptr==':')) { // split at either / or : (whichever comes first from the end!)
cptr++;
strncpy(dirfilename,path,cptr-path); //copy string up till and including/ or : zero rest
dirfilename[cptr-path]=0; //it seems strncpy doesnt always zero?!
filename=cptr; //filename = now remainder of string
break;
}
} while(cptr--!=dirfilename); //search till start
if(!filename) { //we didnt find a / or : ? shouldnt realyl happen but if it does...
filename=(char*)path; //filename = complete path
dirfilename[0]=0; //make directory path ""
}
if(nitroFSDirOpen(&dre,&dirState,dirfilename)) {
fatStruct->start=0;
while(nitroFSDirNext(&dre,&dirState, dirfilename, &st)==0) {
if(!(st.st_mode & S_IFDIR) && (strcmp(dirfilename,filename)==0)) { //Found the *file* your looking for!!
fatStruct->start=dirStruct.romfat.top;
fatStruct->end=dirStruct.romfat.bottom;
break;
}
}
if(fatStruct->start) {
fatStruct->nrs.ndsFile=dirStruct.nrs.ndsFile; //reuse already open'd filehandle
nitroSubSeek(&fatStruct->nrs,fatStruct->start,SEEK_SET); //seek to start of file
return(0); //woot!
}
nitroFSDirClose(&dre,&dirState);
}
return(-1);
}
int nitroFSClose(struct _reent *r,int fd) {
return(nitroSubClose(&((struct nitroFSStruct *)fd)->nrs));
}
ssize_t nitroFSRead(struct _reent *r,int fd,char *ptr,size_t len) {
struct nitroFSStruct *fatStruct=(struct nitroFSStruct *)fd;
struct nitroRawStruct *nrs=&((struct nitroFSStruct *)fd)->nrs;
if(nrs->pos+len > fatStruct->end)
len=fatStruct->end-nrs->pos; //dont let us read past the end plz!
if(nrs->pos > fatStruct->end)
return(0); //hit eof
return(nitroSubRead(nrs,ptr,len));
}
off_t nitroFSSeek(struct _reent *r,int fd ,off_t pos,int dir) {
//need check for eof here...
struct nitroFSStruct *fatStruct=(struct nitroFSStruct *)fd;
struct nitroRawStruct *nrs=&((struct nitroFSStruct *)fd)->nrs;
if(dir==SEEK_SET)
pos+=fatStruct->start; //add start from .nds file offset
if(pos > fatStruct->end)
return(0); //dont let us read past the end plz!
return(nitroSubSeek(nrs,pos,dir));
}
int nitroFSFstat(struct _reent *r,int fd,struct stat *st) {
struct nitroFSStruct *fatStruct=(struct nitroFSStruct *)fd;
//cant think of what else to do here besides report the size atm
st->st_size=fatStruct->end-fatStruct->start;
return(0);
}