diff --git a/arm9/source/driveMenu.cpp b/arm9/source/driveMenu.cpp index fef7547..e7a0b96 100644 --- a/arm9/source/driveMenu.cpp +++ b/arm9/source/driveMenu.cpp @@ -40,7 +40,7 @@ using namespace std; bool flashcardMountSkipped = true; static bool flashcardMountRan = true; static bool dmTextPrinted = false; -int dmCursorPosition = 0; +static int dmCursorPosition = 0; static u8 gbaFixedValue = 0; @@ -120,6 +120,8 @@ void gbaCartDump(void) { void driveMenu (void) { int pressed = 0; int held = 0; + int assignedOp[3] = {0}; + int maxCursors = -1; while (true) { if (isDSiMode() && !flashcardMountSkipped && !pressed && !held) { @@ -137,24 +139,36 @@ void driveMenu (void) { gbaFixedValue = *(u8*)(0x080000B2); } + maxCursors = -1; + if (isDSiMode()){ + maxCursors++; + assignedOp[maxCursors] = 0; + } + maxCursors++; + assignedOp[maxCursors] = 1; + if (!isDSiMode() && isRegularDS) { + maxCursors++; + assignedOp[maxCursors] = 2; + } + if (nitroMounted) { + maxCursors++; + assignedOp[maxCursors] = 3; + } + if (!dmTextPrinted) { consoleInit(NULL, 1, BgType_Text4bpp, BgSize_T_256x256, 15, 0, false, true); - if (isDSiMode()) { - if (dmCursorPosition == 0) { - printf ("[sd:] SDCARD\n"); - printf ("(SD FAT)"); - } else { - printf ("[fat:] GAMECART\n"); - printf ("(Flashcart FAT)"); - } - } else { - if (dmCursorPosition == 0) { - printf ("[fat:] GAMECART\n"); - printf ("(Flashcart FAT)"); - } else if (!isDSiMode() && isRegularDS) { - printf ("GBA GAMECART\n"); - printf ("(GBA Game)"); - } + if (assignedOp[dmCursorPosition] == 0) { + printf ("[sd:] SDCARD\n"); + printf ("(SD FAT)"); + } else if (assignedOp[dmCursorPosition] == 1) { + printf ("[fat:] GAMECART\n"); + printf ("(Flashcart FAT)"); + } else if (assignedOp[dmCursorPosition] == 2) { + printf ("GBA GAMECART\n"); + printf ("(GBA Game)"); + } else if (assignedOp[dmCursorPosition] == 3) { + printf ("[nitro:] NDS GAME IMAGE\n"); + printf ("(Game Virtual)"); } iprintf ("\x1b[%i;0H", 21); printf (titleName); @@ -179,26 +193,28 @@ void driveMenu (void) { // Show cursor printf ("\x1b[%d;0H*", dmCursorPosition + ENTRIES_START_ROW); - printf ("\x1b[2;1H"); - if (isDSiMode()){ - printf ("[sd:] SDCARD"); - if (!sdMounted) { - printf ("\x1b[2;29H"); - printf ("[x]"); - } - printf ("\x1b[3;1H"); - } - printf ("[fat:] GAMECART"); - if (!flashcardMounted) { - iprintf ("\x1b[%i;29H", 2+isDSiMode()); - printf ("[x]"); - } - if (!isDSiMode() && isRegularDS) { - printf ("\x1b[3;1H"); - printf ("GBA GAMECART"); - if (gbaFixedValue != 0x96) { - printf ("\x1b[3;29H"); - printf ("[x]"); + for (int i = 0; i <= maxCursors; i++) { + iprintf ("\x1b[%d;1H", i + ENTRIES_START_ROW); + if (assignedOp[i] == 0) { + printf ("[sd:] SDCARD"); + if (!sdMounted) { + iprintf ("\x1b[%d;29H", i + ENTRIES_START_ROW); + printf ("[x]"); + } + } else if (assignedOp[i] == 1) { + printf ("[fat:] GAMECART"); + if (!flashcardMounted) { + iprintf ("\x1b[%d;29H", i + ENTRIES_START_ROW); + printf ("[x]"); + } + } else if (assignedOp[i] == 2) { + printf ("GBA GAMECART"); + if (gbaFixedValue != 0x96) { + iprintf ("\x1b[%d;29H", i + ENTRIES_START_ROW); + printf ("[x]"); + } + } else if (assignedOp[i] == 3) { + printf ("[nitro:] NDS GAME IMAGE"); } } @@ -233,53 +249,39 @@ void driveMenu (void) { } while (!(pressed & KEY_UP) && !(pressed & KEY_DOWN) && !(pressed & KEY_A) && !(held & KEY_R)); if (pressed & KEY_UP) { - if (isDSiMode() || isRegularDS) { - dmCursorPosition -= 1; - dmTextPrinted = false; - } + dmCursorPosition -= 1; + dmTextPrinted = false; } if (pressed & KEY_DOWN) { - if (isDSiMode() || isRegularDS) { - dmCursorPosition += 1; - dmTextPrinted = false; - } + dmCursorPosition += 1; + dmTextPrinted = false; } - if (dmCursorPosition < 0) dmCursorPosition = 1; // Wrap around to bottom of list - if (dmCursorPosition > 1) dmCursorPosition = 0; // Wrap around to top of list + if (dmCursorPosition < 0) dmCursorPosition = maxCursors; // Wrap around to bottom of list + if (dmCursorPosition > maxCursors) dmCursorPosition = 0; // Wrap around to top of list if (pressed & KEY_A) { - if (dmCursorPosition == 0) { - if (isDSiMode()) { - if (sdMounted) { - dmTextPrinted = false; - secondaryDrive = false; - chdir("sd:/"); - screenMode = 1; - break; - } - } else { - if (flashcardMounted) { - dmTextPrinted = false; - secondaryDrive = true; - chdir("fat:/"); - screenMode = 1; - break; - } - } - } else { - if (isDSiMode()) { - if (flashcardMounted) { - dmTextPrinted = false; - secondaryDrive = true; - chdir("fat:/"); - screenMode = 1; - break; - } - } else if (isRegularDS && flashcardMounted && gbaFixedValue == 0x96) { - dmTextPrinted = false; - gbaCartDump(); - } + if (assignedOp[dmCursorPosition] == 0 && isDSiMode() && sdMounted) { + dmTextPrinted = false; + secondaryDrive = false; + chdir("sd:/"); + screenMode = 1; + break; + } else if (assignedOp[dmCursorPosition] == 1 && flashcardMounted) { + dmTextPrinted = false; + secondaryDrive = true; + chdir("fat:/"); + screenMode = 1; + break; + } else if (assignedOp[dmCursorPosition] == 2 && isRegularDS && flashcardMounted && gbaFixedValue == 0x96) { + dmTextPrinted = false; + gbaCartDump(); + } else if (assignedOp[dmCursorPosition] == 3 && nitroMounted) { + dmTextPrinted = false; + secondaryDrive = nitroSecondaryDrive; + chdir("nitro:/"); + screenMode = 1; + break; } } diff --git a/arm9/source/driveMenu.h b/arm9/source/driveMenu.h index d81d163..6dc521d 100644 --- a/arm9/source/driveMenu.h +++ b/arm9/source/driveMenu.h @@ -5,7 +5,6 @@ #include extern bool flashcardMountSkipped; -extern int dmCursorPosition; extern void driveMenu (void); diff --git a/arm9/source/driveOperations.cpp b/arm9/source/driveOperations.cpp index f61f395..ee5a33c 100644 --- a/arm9/source/driveOperations.cpp +++ b/arm9/source/driveOperations.cpp @@ -12,8 +12,10 @@ u8 stored_SCFG_MC = 0; bool sdMounted = false; bool flashcardMounted = false; +bool nitroMounted = false; -bool secondaryDrive = false; // false == SD card, true == Flashcard +bool secondaryDrive = false; // false == SD card, true == Flashcard +bool nitroSecondaryDrive = false; // false == SD card, true == Flashcard bool sdFound(void) { if (access("sd:/", F_OK) == 0) { diff --git a/arm9/source/driveOperations.h b/arm9/source/driveOperations.h index 5ebd4e4..1a7fc78 100644 --- a/arm9/source/driveOperations.h +++ b/arm9/source/driveOperations.h @@ -5,8 +5,10 @@ extern u8 stored_SCFG_MC; extern bool sdMounted; extern bool flashcardMounted; +extern bool nitroMounted; -extern bool secondaryDrive; // false == SD card, true == Flashcard +extern bool secondaryDrive; // false == SD card, true == Flashcard +extern bool nitroSecondaryDrive; // false == SD card, true == Flashcard extern bool sdFound(void); extern bool flashcardFound(void); diff --git a/arm9/source/fileOperations.cpp b/arm9/source/fileOperations.cpp index 310bd63..68b1770 100644 --- a/arm9/source/fileOperations.cpp +++ b/arm9/source/fileOperations.cpp @@ -10,6 +10,7 @@ char clipboardFilename[256]; bool clipboardOn = false; bool clipboardUsed = false; bool clipboardDrive = false; // false == SD card, true == Flashcard +bool clipboardInNitro = false; off_t getFileSize(const char *fileName) { diff --git a/arm9/source/fileOperations.h b/arm9/source/fileOperations.h index 0f6da12..f3d1abf 100644 --- a/arm9/source/fileOperations.h +++ b/arm9/source/fileOperations.h @@ -6,6 +6,7 @@ extern char clipboardFilename[256]; extern bool clipboardOn; extern bool clipboardUsed; extern bool clipboardDrive; // false == SD card, true == Flashcard +extern bool clipboardInNitro; extern off_t getFileSize(const char *fileName); extern int fcopy(const char *sourcePath, const char *destinationPath); diff --git a/arm9/source/file_browse.cpp b/arm9/source/file_browse.cpp index 52613ec..e8ec910 100644 --- a/arm9/source/file_browse.cpp +++ b/arm9/source/file_browse.cpp @@ -35,6 +35,7 @@ #include "fileOperations.h" #include "driveMenu.h" #include "driveOperations.h" +#include "nitrofs.h" #define SCREEN_COLS 32 #define ENTRIES_PER_SCREEN 22 @@ -180,6 +181,13 @@ int fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { assignedOp[maxCursors] = 0; printf(" Boot file\n"); } + if((entry->name.substr(entry->name.find_last_of(".") + 1) == "nds") + || (entry->name.substr(entry->name.find_last_of(".") + 1) == "NDS")) + { + maxCursors++; + assignedOp[maxCursors] = 3; + printf(" Mount NitroFS\n"); + } if (sdMounted && (strcmp (path, "sd:/gm9i/out/") != 0)) { maxCursors++; assignedOp[maxCursors] = 1; @@ -251,6 +259,12 @@ int fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { iprintf ("\x1b[%d;3H", optionOffset + ENTRIES_START_ROW+cursorScreenPos); printf("Copying... "); fcopy(entry->name.c_str(), destPath); + } else if (assignedOp[optionOffset] == 3) { + nitroMounted = nitroFSInit(entry->name.c_str()); + if (nitroMounted) { + chdir("nitro:/"); + nitroSecondaryDrive = secondaryDrive; + } } return assignedOp[optionOffset]; } @@ -260,7 +274,7 @@ int fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { } } -bool fileBrowse_paste(void) { +bool fileBrowse_paste(char path[PATH_MAX]) { int pressed = 0; int optionOffset = 0; int maxCursors = -1; @@ -272,7 +286,7 @@ bool fileBrowse_paste(void) { iprintf ("\x1b[%d;0H", ENTRIES_START_ROW); maxCursors++; printf(" Copy path\n"); - if (secondaryDrive == clipboardDrive) { + if (!clipboardInNitro && secondaryDrive == clipboardDrive) { maxCursors++; printf(" Move path\n"); } @@ -301,8 +315,6 @@ bool fileBrowse_paste(void) { if (optionOffset > maxCursors) optionOffset = 0; // Wrap around to top of list if (pressed & KEY_A) { - char path[PATH_MAX]; - getcwd(path, PATH_MAX); char destPath[256]; snprintf(destPath, sizeof(destPath), "%s%s", path, clipboardFilename); iprintf ("\x1b[%d;3H", optionOffset + ENTRIES_START_ROW); @@ -390,7 +402,7 @@ string browseForFile (void) { iprintf ("\x1b[%d;0H*", fileOffset - screenOffset + ENTRIES_START_ROW); - if (isDSiMode() && !pressed && dmCursorPosition == 1 && REG_SCFG_MC == 0x11 && flashcardMounted) { + if (isDSiMode() && !pressed && secondaryDrive && REG_SCFG_MC == 0x11 && flashcardMounted) { flashcardUnmount(); screenMode = 0; return "null"; @@ -433,8 +445,12 @@ string browseForFile (void) { if (getOp == 0) { // Return the chosen file return entry->name; - } else if (getOp == 1 || getOp == 2) { + } else if (getOp == 1 || getOp == 2 || nitroMounted) { getDirectoryContents (dirContents); // Refresh directory listing + if (nitroMounted) { + screenOffset = 0; + fileOffset = 0; + } } } } @@ -443,7 +459,7 @@ string browseForFile (void) { if (pressed & KEY_B) { char path[PATH_MAX]; getcwd(path, PATH_MAX); - if ((strcmp (path, "sd:/") == 0) || (strcmp (path, "fat:/") == 0)) { + if ((strcmp (path, "sd:/") == 0) || (strcmp (path, "fat:/") == 0) || (strcmp (path, "nitro:/") == 0)) { screenMode = 0; return "null"; } @@ -487,8 +503,12 @@ string browseForFile (void) { if (pressed & KEY_Y) { if (clipboardOn) { - if (fileBrowse_paste()) { - getDirectoryContents (dirContents); + char path[PATH_MAX]; + getcwd(path, PATH_MAX); + if (strncmp (path, "nitro:/", 7) != 0) { + if (fileBrowse_paste(path)) { + getDirectoryContents (dirContents); + } } } else if (strcmp(entry->name.c_str(), "..") != 0) { char path[PATH_MAX]; @@ -497,6 +517,7 @@ string browseForFile (void) { snprintf(clipboardFilename, sizeof(clipboardFilename), "%s", entry->name.c_str()); clipboardOn = true; clipboardDrive = secondaryDrive; + clipboardInNitro = (strncmp (path, "nitro:/", 7) == 0); clipboardUsed = true; } } diff --git a/arm9/source/nitrofs.c b/arm9/source/nitrofs.c new file mode 100644 index 0000000..664a49a --- /dev/null +++ b/arm9/source/nitrofs.c @@ -0,0 +1,453 @@ +/* + 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. + + 2008-05-19 v0.2 - New And Improved!! :DDD + * fix'd the fseek SEEK_CUR issue (my fseek funct should not have returned a value :/) + * also thx to wintermute's input realized: + * if you dont give ndstool the -o wifilogo.bmp option it will run on emulators in gba mode + * you then dont need the gba's LOADEROFFSET, so it was set to 0x000 + + 2008-05-21 v0.3 - newer and more improved + * fixed some issues with ftell() (again was fseek's fault u_u;;) + * fixed possible error in detecting sc.gba files when using dldi + * readded support for .gba files in addition to .nds emu + * added stat() support for completedness :) + + 2008-05-22 v0.3.1 - slight update + * again fixed fseek(), this time SEEK_END oddly i kinda forgot about it >_> sry + * also went ahead and inlined the functions, makes slight proformance improvement + + 2008-05-26 v0.4 - added chdir + * added proper chdir functionality + + 2008-05-30 v0.5.Turbo - major speed improvement + * This version uses a single filehandle to access the .nds file when not in GBA mode + improving the speed it takes to open a .nds file by around 106ms. This is great for + situations requiring reading alot of seperate small files. However it does take a little + bit longer when reading from multiple files simultainously + (around 122ms over 10,327 0x100 byte reads between 2 files). + 2008-06-09 + * Fixed bug with SEEK_END where it wouldnt utilize the submitted position.. + (now can fseek(f,-128,SEEK_END) to read from end of file :D) + + 2008-06-18 v0.6.Turbo - . and .. :D + * Today i have added full "." and ".." support. + dirnext() will return . and .. first, and all relevent operations will + support . and .. in pathnames. + + 2009-05-10 v0.7.Turbo - small changes @_@?! + + 2009-08-08 v0.8.Turbo - fix fix fix + * fixed problem with some cards where the header would be loaded to GBA ram even if running + in NDS mode causing nitroFSInit() to think it was a valid GBA cart header and attempt to + read from GBA SLOT instead of SLOT 1. Fixed this by making it check that filename is not NULL + and then to try FAT/SLOT1 first. The NULL option allows forcing nitroFS to use gba. + + 2018-09-05 v0.9 - modernize devoptab (by RonnChyran) + * Updated for libsysbase change in devkitARM r46 and above. + +*/ + +#include +#include +#include +#include "nitrofs.h" + +//This seems to be a typo! memory.h has REG_EXEMEMCNT +#ifndef REG_EXMEMCNT +#define REG_EXMEMCNT (*(vuint16 *)0x04000204) +#endif + +#define __itcm __attribute__((section(".itcm"))) + +//Globals! +u32 fntOffset; //offset to start of filename table +u32 fatOffset; //offset to start of file alloc table +bool hasLoader; //single global nds filehandle (is null if not in dldi/fat mode) +u16 chdirpathid; //default dir path id... +FILE *ndsFile; +off_t ndsFileLastpos; //Used to determine need to fseek or not + +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,void* fd); + NULL, // int (*write_r)(struct _reent *r,void* fd,const char *ptr,int len); + &nitroFSRead, // int (*read_r)(struct _reent *r,void* fd,char *ptr,int len); + &nitroFSSeek, // int (*seek_r)(struct _reent *r,void* fd,int pos,int dir); + &nitroFSFstat, // int (*fstat_r)(struct _reent *r,void* fd,struct stat *st); + &nitroFSstat, // 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); + &nitroFSChdir, // 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); + +}; + +//K, i decided to inline these, improves speed slightly.. +//these 2 '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 :) + +//reads from rom image either gba rom or dldi +inline ssize_t nitroSubRead(off_t *npos, void *ptr, size_t len) +{ + if (ndsFile != NULL) + { //read from ndsfile + if (ndsFileLastpos != *npos) + fseek(ndsFile, *npos, SEEK_SET); //if we need to, move! (might want to verify this succeed) + len = fread(ptr, 1, len, ndsFile); + } + else + { //reading from gbarom + memcpy(ptr, *npos + (void *)GBAROM, len); //len isnt checked here because other checks exist in the callers (hopefully) + } + if (len > 0) + *npos += len; + ndsFileLastpos = *npos; //save the current file nds pos + return (len); +} + +//seek around +inline void nitroSubSeek(off_t *npos, int pos, int dir) +{ + if ((dir == SEEK_SET) || (dir == SEEK_END)) //otherwise just set the pos :) + *npos = pos; + else if (dir == SEEK_CUR) + *npos += pos; //see ez! +} + +//Figure out if its gba or ds, setup stuff +int __itcm +nitroFSInit(const char *ndsfile) +{ + off_t pos = 0; + char romstr[0x10]; + chdirpathid = NITROROOT; + ndsFileLastpos = 0; + ndsFile = NULL; + if (ndsfile != NULL) + { + if ((ndsFile = fopen(ndsfile, "rb"))) + { + nitroSubRead(&pos, romstr, strlen(LOADERSTR)); + if (strncmp(romstr, LOADERSTR, strlen(LOADERSTR)) == 0) + { + nitroSubSeek(&pos, LOADEROFFSET + FNTOFFSET, SEEK_SET); + nitroSubRead(&pos, &fntOffset, sizeof(fntOffset)); + nitroSubSeek(&pos, LOADEROFFSET + FATOFFSET, SEEK_SET); + nitroSubRead(&pos, &fatOffset, sizeof(fatOffset)); + fatOffset += LOADEROFFSET; + fntOffset += LOADEROFFSET; + hasLoader = true; + } + else + { + nitroSubSeek(&pos, FNTOFFSET, SEEK_SET); + nitroSubRead(&pos, &fntOffset, sizeof(fntOffset)); + nitroSubSeek(&pos, FATOFFSET, SEEK_SET); + nitroSubRead(&pos, &fatOffset, sizeof(fatOffset)); + hasLoader = false; + } + setvbuf(ndsFile, NULL, _IONBF, 0); //we dont need double buffs u_u + AddDevice(&nitroFSdevoptab); + return (1); + } + } + return (0); +} + +//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; + 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) + dirStruct->pos = 0; + if (*dirpath == '/') //if first character is '/' use absolute root path plz + dirStruct->cur_dir_id = NITROROOT; //first root dir + else + dirStruct->cur_dir_id = chdirpathid; //else use chdirpath + 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 / + 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 + { + r->_errno = ENOENT; + return (NULL); + } +} + +int nitroFSDirClose(struct _reent *r, DIR_ITER *dirState) +{ + return (0); +} + +/*Consts containing relative system path strings*/ +const char *syspaths[2] = { + ".", + ".."}; + +//reset dir to start of entry selected by dirStruct->cur_dir_id which should be set in dirOpen okai?! +int nitroDirReset(struct _reent *r, DIR_ITER *dirState) +{ + struct nitroDIRStruct *dirStruct = (struct nitroDIRStruct *)dirState->dirStruct; //this makes it lots easier! + struct ROM_FNTDir dirsubtable; + off_t *pos = &dirStruct->pos; + nitroSubSeek(pos, fntOffset + ((dirStruct->cur_dir_id & NITRODIRMASK) * sizeof(struct ROM_FNTDir)), SEEK_SET); + nitroSubRead(pos, &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 + dirStruct->spc = 0; //system path counter, first two dirnext's deliver . and .. + 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! + off_t *pos = &dirStruct->pos; + if (dirStruct->spc <= 1) + { + if (st) + st->st_mode = S_IFDIR; + if ((dirStruct->spc == 0) || (dirStruct->cur_dir_id == NITROROOT)) + { // "." or its already root (no parent) + dirStruct->dir_id = dirStruct->cur_dir_id; + } + else + { // ".." + dirStruct->dir_id = dirStruct->parent_id; + } + strcpy(filename, syspaths[dirStruct->spc++]); + return (0); + } + nitroSubSeek(pos, fntOffset + dirStruct->namepos, SEEK_SET); + nitroSubRead(pos, &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(pos, filename, next); + nitroSubRead(&dirStruct->pos, &dirStruct->dir_id, sizeof(dirStruct->dir_id)); //read the dir_id + //grr cant get the struct member size?, just wanna test it so moving on... + // nitroSubRead(pos,&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(pos, filename, next); + dirStruct->namepos += next + 1; //now we points to next one :D + //read file info to get filesize (and for fileopen) + nitroSubSeek(pos, fatOffset + (dirStruct->entry_id * sizeof(struct ROM_FAT)), SEEK_SET); + nitroSubRead(pos, &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 + { + r->_errno = EIO; + 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 form 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-- != path); //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* youre looking for!! + fatStruct->start = dirStruct.romfat.top; + fatStruct->end = dirStruct.romfat.bottom; + if (hasLoader) + { + fatStruct->start += LOADEROFFSET; + fatStruct->end += LOADEROFFSET; + } + break; + } + } + if (fatStruct->start) + { + nitroSubSeek(&fatStruct->pos, fatStruct->start, SEEK_SET); //seek to start of file + return (0); //woot! + } + nitroFSDirClose(&dre, &dirState); + } + if (r->_errno == 0) + { + r->_errno = ENOENT; + } + return (-1); //teh fail +} + +int nitroFSClose(struct _reent *r, void* fd) +{ + return (0); +} + +ssize_t nitroFSRead(struct _reent *r, void* fd, char *ptr, size_t len) +{ + struct nitroFSStruct *fatStruct = (struct nitroFSStruct *)fd; + off_t *npos = &fatStruct->pos; + if (*npos + len > fatStruct->end) + len = fatStruct->end - *npos; //dont let us read past the end plz! + if (*npos > fatStruct->end) + return (0); //hit eof + return (nitroSubRead(npos, ptr, len)); +} + +off_t nitroFSSeek(struct _reent *r, void* fd, off_t pos, int dir) +{ + //need check for eof here... + struct nitroFSStruct *fatStruct = (struct nitroFSStruct *)fd; + off_t *npos = &fatStruct->pos; + if (dir == SEEK_SET) + pos += fatStruct->start; //add start from .nds file offset + else if (dir == SEEK_END) + pos += fatStruct->end; //set start to end of file (useless?) + if (pos > fatStruct->end) + return (-1); //dont let us read past the end plz! + nitroSubSeek(npos, pos, dir); + return (*npos - fatStruct->start); +} + +int nitroFSFstat(struct _reent *r, void* fd, struct stat *st) +{ + struct nitroFSStruct *fatStruct = (struct nitroFSStruct *)fd; + st->st_size = fatStruct->end - fatStruct->start; + return (0); +} + +int nitroFSstat(struct _reent *r, const char *file, struct stat *st) +{ + struct nitroFSStruct fatStruct; + struct nitroDIRStruct dirStruct; + DIR_ITER dirState; + + if (nitroFSOpen(NULL, &fatStruct, file, 0, 0) >= 0) + { + st->st_mode = S_IFREG; + st->st_size = fatStruct.end - fatStruct.start; + return (0); + } + + dirState.dirStruct = &dirStruct; + if ((nitroFSDirOpen(r, &dirState, file) != NULL)) + { + + st->st_mode = S_IFDIR; + nitroFSDirClose(r, &dirState); + return (0); + } + r->_errno = ENOENT; + return (-1); +} + +int nitroFSChdir(struct _reent *r, const char *name) +{ + struct nitroDIRStruct dirStruct; + DIR_ITER dirState; + dirState.dirStruct = &dirStruct; + if ((name != NULL) && (nitroFSDirOpen(r, &dirState, name) != NULL)) + { + chdirpathid = dirStruct.cur_dir_id; + nitroFSDirClose(r, &dirState); + return (0); + } + else + { + r->_errno = ENOENT; + return (-1); + } +} \ No newline at end of file diff --git a/arm9/source/nitrofs.h b/arm9/source/nitrofs.h new file mode 100644 index 0000000..6d98b2e --- /dev/null +++ b/arm9/source/nitrofs.h @@ -0,0 +1,113 @@ +/* + nitrofs.h - eris's wai ossum nitro filesystem device driver header + 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. + + 2008-05-19 v0.2 - New And Improved!! :DDD + * fix'd the fseek SEEK_CUR issue (my fseek funct should not have returned a value :/) + * also thx to wintermute's input realized: + * if you dont give ndstool the -o wifilogo.bmp option it will run on emulators in gba mode + * you then dont need the gba's LOADEROFFSET, so it was set to 0x000 + + 2008-05-21 v0.3 - newer and more improved + * fixed some issues with ftell() (again was fseek's fault u_u;;) + * fixed possible error in detecting sc.gba files when using dldi + * readded support for .gba files in addition to .nds emu + * added stat() support for completedness :) + + 2008-05-30 v0.5.Turbo - major speed improvement + * This version uses a single filehandle to access the .nds file when not in GBA mode + improving the speed it takes to open a .nds file by around 106ms. This is great for + situations requiring reading alot of seperate small files. However it does take a little + bit longer when reading from multiple files simultainously + (around 122ms over 10,327 0x100 byte reads between 2 files). + 2008-06-09 + * Fixed bug with SEEK_END where it wouldnt utilize the submitted position.. + (now can fseek(f,-128,SEEK_END) to read from end of file :D) + + 2008-06-18 v0.6.Turbo - . and .. :D + * Today i have added full "." and ".." support. + dirnext() will return . and .. first, and all relevent operations will + support . and .. in pathnames. + + 2018-09-05 v0.9 - modernize devoptab (by RonnChyran) + * Updated for libsysbase change in devkitARM r46 and above. +*/ + +#ifndef NITROFS_H +#define NITROFS_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + + int nitroFSInit(const char *ndsfile); + 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, void *fd); + ssize_t nitroFSRead(struct _reent *r, void *fd, char *ptr, size_t len); + off_t nitroFSSeek(struct _reent *r, void *fd, off_t pos, int dir); + int nitroFSFstat(struct _reent *r, void *fd, struct stat *st); + int nitroFSstat(struct _reent *r, const char *file, struct stat *st); + int nitroFSChdir(struct _reent *r, const char *name); +#define LOADERSTR "PASS" //look for this +#define LOADERSTROFFSET 0xac +#define LOADEROFFSET 0x0200 +#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 + }; + + struct nitroFSStruct + { + off_t pos; //where in the file am i? + off_t start; //where in the rom this file starts + off_t end; //where in the rom this file ends + }; + + struct nitroDIRStruct + { + off_t pos; //where in the file am i? + off_t 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 ../ ) + u8 spc; //system path count.. used by dirnext, when 0=./ 1=../ >=2 actual dirs + }; + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file