diff --git a/nds/include/nitrofs.h b/nds/include/nitrofs.h new file mode 100644 index 0000000..209e75f --- /dev/null +++ b/nds/include/nitrofs.h @@ -0,0 +1,46 @@ +/* + nitrofs.h + Simple functionality for initialising nitrofs filesystem. + + Copyright (c) 2008 + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + + +#ifndef _NITROFS_H +#define _NITROFS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +bool nitroFSInit(); + +#ifdef __cplusplus +} +#endif + +#endif // _NITROFS_H diff --git a/nds/source/nitrofs.c b/nds/source/nitrofs.c new file mode 100644 index 0000000..00b58d0 --- /dev/null +++ b/nds/source/nitrofs.c @@ -0,0 +1,370 @@ +/* + 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 +#include +#include + +#include +#include +#include + +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); +}