mirror of
https://github.com/wiiu-env/libfat.git
synced 2024-11-26 11:54:13 +01:00
371 lines
14 KiB
C
371 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);
|
|
int nitroFSRead(struct _reent *r,int fd,char *ptr,int len);
|
|
int nitroFSSeek(struct _reent *r,int fd,int 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, // int (*write_r)(struct _reent *r,int fd,const char *ptr,int len);
|
|
&nitroFSRead, // int (*read_r)(struct _reent *r,int fd,char *ptr,int len);
|
|
&nitroFSSeek, // int (*seek_r)(struct _reent *r,int fd,int 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);
|
|
|
|
};
|
|
|
|
//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));
|
|
}
|
|
|
|
int nitroFSRead(struct _reent *r,int fd,char *ptr,int 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));
|
|
}
|
|
|
|
int nitroFSSeek(struct _reent *r,int fd,int 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);
|
|
}
|