mirror of
https://github.com/rvtr/GodMode9i.git
synced 2025-06-19 03:05:43 -04:00

* Remove `using namespace std` * Improve extension function and use more * Add save restoring and remove fstream to save ~300KB
894 lines
27 KiB
C++
894 lines
27 KiB
C++
/*-----------------------------------------------------------------
|
|
Copyright (C) 2005 - 2013
|
|
Michael "Chishm" Chisholm
|
|
Dave "WinterMute" Murphy
|
|
Claudio "sverx"
|
|
|
|
This program 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.
|
|
|
|
This program 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 this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
------------------------------------------------------------------*/
|
|
|
|
#include "file_browse.h"
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <dirent.h>
|
|
|
|
#include <nds.h>
|
|
#include <fat.h>
|
|
|
|
#include "main.h"
|
|
#include "date.h"
|
|
#include "screenshot.h"
|
|
#include "fileOperations.h"
|
|
#include "driveMenu.h"
|
|
#include "driveOperations.h"
|
|
#include "dumpOperations.h"
|
|
#include "nitrofs.h"
|
|
|
|
#define SCREEN_COLS 22
|
|
#define ENTRIES_PER_SCREEN 23
|
|
#define ENTRIES_START_ROW 1
|
|
#define OPTIONS_ENTRIES_START_ROW 2
|
|
#define ENTRY_PAGE_LENGTH 10
|
|
bool bigJump = false;
|
|
extern PrintConsole topConsole, bottomConsole;
|
|
|
|
extern void printBorderTop(void);
|
|
extern void printBorderBottom(void);
|
|
extern void clearBorderTop(void);
|
|
extern void clearBorderBottom(void);
|
|
extern void reinitConsoles(void);
|
|
|
|
static char path[PATH_MAX];
|
|
|
|
bool extension(const std::string &filename, const std::vector<std::string> &extensions) {
|
|
for(const std::string &ext : extensions) {
|
|
if(filename.length() > ext.length() && strcasecmp(filename.substr(filename.length() - ext.length()).data(), ext.data()) == 0)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void OnKeyPressed(int key) {
|
|
if(key > 0)
|
|
iprintf("%c", key);
|
|
}
|
|
|
|
bool dirEntryPredicate (const DirEntry& lhs, const DirEntry& rhs) {
|
|
|
|
if (!lhs.isDirectory && rhs.isDirectory) {
|
|
return false;
|
|
}
|
|
if (lhs.isDirectory && !rhs.isDirectory) {
|
|
return true;
|
|
}
|
|
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
|
|
}
|
|
|
|
void getDirectoryContents (std::vector<DirEntry>& dirContents) {
|
|
struct stat st;
|
|
|
|
dirContents.clear();
|
|
|
|
DIR *pdir = opendir (".");
|
|
|
|
if (pdir == NULL) {
|
|
iprintf ("Unable to open the directory.\n");
|
|
} else {
|
|
|
|
while(true) {
|
|
DirEntry dirEntry;
|
|
|
|
struct dirent* pent = readdir(pdir);
|
|
if(pent == NULL) break;
|
|
|
|
stat(pent->d_name, &st);
|
|
if (strcmp(pent->d_name, "..") != 0) {
|
|
dirEntry.name = pent->d_name;
|
|
dirEntry.isDirectory = st.st_mode & S_IFDIR;
|
|
if (!dirEntry.isDirectory) {
|
|
dirEntry.size = getFileSize(dirEntry.name.c_str());
|
|
}
|
|
if (extension(dirEntry.name, {"nds", "argv", "dsi", "ids", "app"})) {
|
|
dirEntry.isApp = ((currentDrive == 0 && sdMounted) || (currentDrive == 1 && flashcardMounted));
|
|
} else if (extension(dirEntry.name, {"firm"})) {
|
|
dirEntry.isApp = (isDSiMode() && is3DS && sdMounted);
|
|
} else {
|
|
dirEntry.isApp = false;
|
|
}
|
|
|
|
if (dirEntry.name.compare(".") != 0) {
|
|
dirContents.push_back (dirEntry);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
closedir(pdir);
|
|
}
|
|
|
|
sort(dirContents.begin(), dirContents.end(), dirEntryPredicate);
|
|
|
|
DirEntry dirEntry;
|
|
dirEntry.name = ".."; // ".." entry
|
|
dirEntry.isDirectory = true;
|
|
dirEntry.isApp = false;
|
|
dirContents.insert (dirContents.begin(), dirEntry); // Add ".." to top of list
|
|
}
|
|
|
|
void showDirectoryContents (const std::vector<DirEntry>& dirContents, int fileOffset, int startRow) {
|
|
getcwd(path, PATH_MAX);
|
|
|
|
consoleClear();
|
|
|
|
// Print the path
|
|
printf ("\x1B[30m"); // Print black color
|
|
// Print time
|
|
printf ("\x1b[0;27H");
|
|
printf (RetTime().c_str());
|
|
|
|
printf ("\x1b[0;0H");
|
|
if (strlen(path) < SCREEN_COLS) {
|
|
iprintf ("%s", path);
|
|
} else {
|
|
iprintf ("%s", path + strlen(path) - SCREEN_COLS);
|
|
}
|
|
|
|
// Move to 2nd row
|
|
iprintf ("\x1b[1;0H");
|
|
|
|
// Print directory listing
|
|
for (int i = 0; i < ((int)dirContents.size() - startRow) && i < ENTRIES_PER_SCREEN; i++) {
|
|
const DirEntry* entry = &dirContents.at(i + startRow);
|
|
|
|
// Set row
|
|
iprintf ("\x1b[%d;0H", i + ENTRIES_START_ROW);
|
|
if ((fileOffset - startRow) == i) {
|
|
printf ("\x1B[47m"); // Print foreground white color
|
|
} else if (entry->isDirectory) {
|
|
printf ("\x1B[37m"); // Print custom blue color
|
|
} else {
|
|
printf ("\x1B[40m"); // Print foreground black color
|
|
}
|
|
|
|
printf ("%.*s", SCREEN_COLS, entry->name.c_str());
|
|
if (strcmp(entry->name.c_str(), "..") == 0) {
|
|
printf ("\x1b[%d;28H", i + ENTRIES_START_ROW);
|
|
printf ("(..)");
|
|
} else if (entry->isDirectory) {
|
|
printf ("\x1b[%d;27H", i + ENTRIES_START_ROW);
|
|
printf ("(dir)");
|
|
} else {
|
|
printf ("\x1b[%d;23H", i + ENTRIES_START_ROW);
|
|
printBytesAlign((int)entry->size);
|
|
}
|
|
}
|
|
|
|
printf ("\x1B[47m"); // Print foreground white color
|
|
}
|
|
|
|
FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) {
|
|
int pressed = 0;
|
|
FileOperation assignedOp[4] = {FileOperation::none};
|
|
int optionOffset = 0;
|
|
int cursorScreenPos = 0;
|
|
int maxCursors = -1;
|
|
|
|
consoleSelect(&bottomConsole);
|
|
consoleClear();
|
|
printf ("\x1B[47m"); // Print foreground white color
|
|
char fullPath[256];
|
|
snprintf(fullPath, sizeof(fullPath), "%s%s", path, entry->name.c_str());
|
|
printf(fullPath);
|
|
// Position cursor, depending on how long the full file path is
|
|
for (int i = 0; i < 256; i++) {
|
|
if (i == 33 || i == 65 || i == 97 || i == 129 || i == 161 || i == 193 || i == 225) {
|
|
cursorScreenPos++;
|
|
}
|
|
if (fullPath[i] == '\0') {
|
|
break;
|
|
}
|
|
}
|
|
iprintf ("\x1b[%d;0H", cursorScreenPos + OPTIONS_ENTRIES_START_ROW);
|
|
if (!entry->isDirectory) {
|
|
if (entry->isApp) {
|
|
maxCursors++;
|
|
assignedOp[maxCursors] = FileOperation::bootFile;
|
|
printf(" Boot file\n");
|
|
}
|
|
if(extension(entry->name, {"nds", "dsi", "ids", "app"}))
|
|
{
|
|
maxCursors++;
|
|
assignedOp[maxCursors] = FileOperation::mountNitroFS;
|
|
printf(" Mount NitroFS\n");
|
|
}
|
|
else if(extension(entry->name, {"sav"}))
|
|
{
|
|
maxCursors++;
|
|
assignedOp[maxCursors] = FileOperation::restoreSave;
|
|
printf(" Restore save\n");
|
|
}
|
|
else if(extension(entry->name, {"img", "sd"}))
|
|
{
|
|
maxCursors++;
|
|
assignedOp[maxCursors] = FileOperation::mountImg;
|
|
printf(" Mount as FAT image\n");
|
|
}
|
|
}
|
|
maxCursors++;
|
|
assignedOp[maxCursors] = FileOperation::showInfo;
|
|
printf(entry->isDirectory ? " Show directory info\n" : " Show file info\n");
|
|
if (sdMounted && (strcmp (path, "sd:/gm9i/out/") != 0)) {
|
|
maxCursors++;
|
|
assignedOp[maxCursors] = FileOperation::copySdOut;
|
|
printf(" Copy to sd:/gm9i/out\n");
|
|
}
|
|
if (flashcardMounted && (strcmp (path, "fat:/gm9i/out/") != 0)) {
|
|
maxCursors++;
|
|
assignedOp[maxCursors] = FileOperation::copyFatOut;
|
|
printf(" Copy to fat:/gm9i/out\n");
|
|
}
|
|
printf("\n");
|
|
printf("(<A> select, <B> cancel)");
|
|
consoleSelect(&bottomConsole);
|
|
printf ("\x1B[47m"); // Print foreground white color
|
|
while (true) {
|
|
// Clear old cursors
|
|
for (int i = OPTIONS_ENTRIES_START_ROW+cursorScreenPos; i < (maxCursors+1) + OPTIONS_ENTRIES_START_ROW+cursorScreenPos; i++) {
|
|
iprintf ("\x1b[%d;0H ", i);
|
|
}
|
|
// Show cursor
|
|
iprintf ("\x1b[%d;0H->", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos);
|
|
|
|
consoleSelect(&topConsole);
|
|
printf ("\x1B[30m"); // Print black color for time text
|
|
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
|
|
do {
|
|
// Move to right side of screen
|
|
printf ("\x1b[0;26H");
|
|
// Print time
|
|
printf (" %s" ,RetTime().c_str());
|
|
|
|
scanKeys();
|
|
pressed = keysDownRepeat();
|
|
swiWaitForVBlank();
|
|
} while (!(pressed & KEY_UP) && !(pressed & KEY_DOWN)
|
|
&& !(pressed & KEY_A) && !(pressed & KEY_B));
|
|
|
|
consoleSelect(&bottomConsole);
|
|
printf ("\x1B[47m"); // Print foreground white color
|
|
|
|
if (pressed & KEY_UP) optionOffset -= 1;
|
|
if (pressed & KEY_DOWN) optionOffset += 1;
|
|
|
|
if (optionOffset < 0) optionOffset = maxCursors; // Wrap around to bottom of list
|
|
if (optionOffset > maxCursors) optionOffset = 0; // Wrap around to top of list
|
|
|
|
if (pressed & KEY_A) {
|
|
switch(assignedOp[optionOffset]) {
|
|
case FileOperation::bootFile: {
|
|
applaunch = true;
|
|
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos);
|
|
printf("Now loading...");
|
|
break;
|
|
} case FileOperation::restoreSave: {
|
|
ndsCardSaveRestore(entry->name.c_str());
|
|
break;
|
|
} case FileOperation::copySdOut: {
|
|
if (access("sd:/gm9i", F_OK) != 0) {
|
|
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos);
|
|
printf("Creating directory...");
|
|
mkdir("sd:/gm9i", 0777);
|
|
}
|
|
if (access("sd:/gm9i/out", F_OK) != 0) {
|
|
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos);
|
|
printf("Creating directory...");
|
|
mkdir("sd:/gm9i/out", 0777);
|
|
}
|
|
char destPath[256];
|
|
snprintf(destPath, sizeof(destPath), "sd:/gm9i/out/%s", entry->name.c_str());
|
|
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos);
|
|
printf("Copying... ");
|
|
remove(destPath);
|
|
char sourceFolder[PATH_MAX];
|
|
getcwd(sourceFolder, PATH_MAX);
|
|
char sourcePath[PATH_MAX];
|
|
snprintf(sourcePath, sizeof(sourcePath), "%s%s", sourceFolder, entry->name.c_str());
|
|
fcopy(sourcePath, destPath);
|
|
chdir(sourceFolder); // For after copying a folder
|
|
break;
|
|
} case FileOperation::copyFatOut: {
|
|
if (access("fat:/gm9i", F_OK) != 0) {
|
|
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos);
|
|
printf("Creating directory...");
|
|
mkdir("fat:/gm9i", 0777);
|
|
}
|
|
if (access("fat:/gm9i/out", F_OK) != 0) {
|
|
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos);
|
|
printf("Creating directory...");
|
|
mkdir("fat:/gm9i/out", 0777);
|
|
}
|
|
char destPath[256];
|
|
snprintf(destPath, sizeof(destPath), "fat:/gm9i/out/%s", entry->name.c_str());
|
|
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW+cursorScreenPos);
|
|
printf("Copying... ");
|
|
remove(destPath);
|
|
char sourceFolder[PATH_MAX];
|
|
getcwd(sourceFolder, PATH_MAX);
|
|
char sourcePath[PATH_MAX];
|
|
snprintf(sourcePath, sizeof(sourcePath), "%s%s", sourceFolder, entry->name.c_str());
|
|
fcopy(sourcePath, destPath);
|
|
chdir(sourceFolder); // For after copying a folder
|
|
break;
|
|
} case FileOperation::mountNitroFS: {
|
|
nitroMounted = nitroFSInit(entry->name.c_str());
|
|
if (nitroMounted) {
|
|
chdir("nitro:/");
|
|
nitroCurrentDrive = currentDrive;
|
|
currentDrive = 5;
|
|
}
|
|
break;
|
|
} case FileOperation::showInfo: {
|
|
changeFileAttribs(entry);
|
|
break;
|
|
} case FileOperation::mountImg: {
|
|
imgMounted = imgMount(entry->name.c_str());
|
|
if (imgMounted) {
|
|
chdir("img:/");
|
|
imgCurrentDrive = currentDrive;
|
|
currentDrive = 6;
|
|
}
|
|
break;
|
|
} case FileOperation::none: {
|
|
break;
|
|
}
|
|
}
|
|
return assignedOp[optionOffset];
|
|
}
|
|
if (pressed & KEY_B) {
|
|
return FileOperation::none;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool fileBrowse_paste(char dest[256]) {
|
|
int pressed = 0;
|
|
int optionOffset = 0;
|
|
int maxCursors = -1;
|
|
|
|
consoleSelect(&bottomConsole);
|
|
consoleClear();
|
|
printf ("\x1B[47m"); // Print foreground white color
|
|
printf("Paste clipboard here?");
|
|
printf("\n\n");
|
|
iprintf ("\x1b[%d;0H", OPTIONS_ENTRIES_START_ROW);
|
|
maxCursors++;
|
|
printf(" Copy files\n");
|
|
for (auto &file : clipboard) {
|
|
if (file.nitro)
|
|
continue;
|
|
maxCursors++;
|
|
printf(" Move files\n");
|
|
break;
|
|
}
|
|
printf("\n");
|
|
printf("(<A> select, <B> cancel)");
|
|
consoleSelect(&bottomConsole);
|
|
printf ("\x1B[47m"); // Print foreground white color
|
|
while (true) {
|
|
// Clear old cursors
|
|
for (int i = OPTIONS_ENTRIES_START_ROW; i < (maxCursors+1) + OPTIONS_ENTRIES_START_ROW; i++) {
|
|
iprintf ("\x1b[%d;0H ", i);
|
|
}
|
|
// Show cursor
|
|
iprintf ("\x1b[%d;0H->", optionOffset + OPTIONS_ENTRIES_START_ROW);
|
|
|
|
consoleSelect(&topConsole);
|
|
printf ("\x1B[30m"); // Print black color for time text
|
|
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
|
|
do {
|
|
// Move to right side of screen
|
|
printf ("\x1b[0;26H");
|
|
// Print time
|
|
printf (" %s" ,RetTime().c_str());
|
|
|
|
scanKeys();
|
|
pressed = keysDownRepeat();
|
|
swiWaitForVBlank();
|
|
} while (!(pressed & KEY_UP) && !(pressed & KEY_DOWN)
|
|
&& !(pressed & KEY_A) && !(pressed & KEY_B));
|
|
|
|
consoleSelect(&bottomConsole);
|
|
printf ("\x1B[47m"); // Print foreground white color
|
|
|
|
if (pressed & KEY_UP) optionOffset -= 1;
|
|
if (pressed & KEY_DOWN) optionOffset += 1;
|
|
|
|
if (optionOffset < 0) optionOffset = maxCursors; // Wrap around to bottom of list
|
|
if (optionOffset > maxCursors) optionOffset = 0; // Wrap around to top of list
|
|
|
|
if (pressed & KEY_A) {
|
|
char destPath[256];
|
|
iprintf ("\x1b[%d;3H", optionOffset + OPTIONS_ENTRIES_START_ROW);
|
|
printf(optionOffset ? "Moving... " : "Copying...");
|
|
for (auto &file : clipboard) {
|
|
snprintf(destPath, sizeof(destPath), "%s%s", dest, file.name);
|
|
if (!strcmp (file.path, destPath))
|
|
continue; // If the source and destination for the clipped file is the same skip it
|
|
|
|
if (optionOffset && !file.nitro ) { // Don't remove if from nitro
|
|
if (currentDrive == file.drive) {
|
|
rename(file.path, destPath);
|
|
} else {
|
|
fcopy(file.path, destPath); // Copy file to destination, since renaming won't work
|
|
remove(file.path); // Delete source file after copying
|
|
}
|
|
clipboardUsed = false; // Disable clipboard restore
|
|
} else {
|
|
remove(destPath);
|
|
fcopy(file.path, destPath);
|
|
}
|
|
}
|
|
clipboardOn = false; // Clear clipboard after copying or moving
|
|
return true;
|
|
}
|
|
if (pressed & KEY_B) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void recRemove(const char *path, std::vector<DirEntry> dirContents) {
|
|
DirEntry *entry = NULL;
|
|
chdir (path);
|
|
getDirectoryContents(dirContents);
|
|
for (int i = 1; i < ((int)dirContents.size()); i++) {
|
|
entry = &dirContents.at(i);
|
|
if (entry->isDirectory) recRemove(entry->name.c_str(), dirContents);
|
|
if (!(FAT_getAttr(entry->name.c_str()) & ATTR_READONLY)) {
|
|
remove(entry->name.c_str());
|
|
}
|
|
}
|
|
chdir ("..");
|
|
remove(path);
|
|
}
|
|
|
|
void fileBrowse_drawBottomScreen(DirEntry* entry) {
|
|
consoleClear();
|
|
printf ("\x1B[47m"); // Print foreground white color
|
|
printf ("\x1b[22;0H");
|
|
printf ("%s\n", titleName);
|
|
printf ("X - DELETE/[+R] RENAME file\n");
|
|
printf ("L - COPY file\n");
|
|
printf ("Y - PASTE file/[+R] CREATE entry");
|
|
printf ("R+A - Directory options\n");
|
|
if (sdMounted || flashcardMounted) {
|
|
printf ("%s\n", SCREENSHOTTEXT);
|
|
}
|
|
printf ("%s\n", clipboardOn ? "SELECT - Clear Clipboard" : "SELECT - Restore Clipboard");
|
|
if (!isDSiMode() && isRegularDS) {
|
|
printf (POWERTEXT_DS);
|
|
} else if (is3DS) {
|
|
printf ("%s\n%s", POWERTEXT_3DS, HOMETEXT);
|
|
} else {
|
|
printf (POWERTEXT);
|
|
}
|
|
|
|
printf (entry->isDirectory ? "\x1B[37m" : "\x1B[40m"); // Print custom blue color or foreground black color
|
|
printf ("\x1b[0;0H");
|
|
printf ("%s\n", entry->name.c_str());
|
|
if (strcmp(entry->name.c_str(), "..") != 0) {
|
|
if (entry->isDirectory) {
|
|
printf ("(dir)");
|
|
} else if (entry->size == 1) {
|
|
printf ("%i Byte", (int)entry->size);
|
|
} else {
|
|
printf ("%i Bytes", (int)entry->size);
|
|
}
|
|
}
|
|
if (clipboardOn) {
|
|
printf ("\x1b[9;0H");
|
|
printf ("\x1B[47m"); // Print foreground white color
|
|
printf ("[CLIPBOARD]\n");
|
|
for (size_t i = 0; i < clipboard.size(); ++i) {
|
|
printf (clipboard[i].folder ? "\x1B[37m" : "\x1B[40m"); // Print custom blue color or foreground black color
|
|
if (i < 4) {
|
|
printf ("%s\n", clipboard[i].name);
|
|
} else {
|
|
printf ("%d more files...\n", clipboard.size() - 4);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string browseForFile (void) {
|
|
int pressed = 0;
|
|
int held = 0;
|
|
int screenOffset = 0;
|
|
int fileOffset = 0;
|
|
std::vector<DirEntry> dirContents;
|
|
|
|
getDirectoryContents (dirContents);
|
|
|
|
while (true) {
|
|
DirEntry* entry = &dirContents.at(fileOffset);
|
|
|
|
consoleSelect(&bottomConsole);
|
|
fileBrowse_drawBottomScreen(entry);
|
|
consoleSelect(&topConsole);
|
|
showDirectoryContents (dirContents, fileOffset, screenOffset);
|
|
|
|
stored_SCFG_MC = REG_SCFG_MC;
|
|
|
|
printf ("\x1B[30m"); // Print black color for time text
|
|
|
|
// Power saving loop. Only poll the keys once per frame and sleep the CPU if there is nothing else to do
|
|
do {
|
|
// Move to right side of screen
|
|
printf ("\x1b[0;26H");
|
|
// Print time
|
|
printf (" %s" ,RetTime().c_str());
|
|
|
|
scanKeys();
|
|
pressed = keysDownRepeat();
|
|
held = keysHeld();
|
|
swiWaitForVBlank();
|
|
|
|
if (REG_SCFG_MC != stored_SCFG_MC) {
|
|
break;
|
|
}
|
|
|
|
if ((held & KEY_R) && (pressed & KEY_L)) {
|
|
break;
|
|
}
|
|
} while (!(pressed & KEY_UP) && !(pressed & KEY_DOWN) && !(pressed & KEY_LEFT) && !(pressed & KEY_RIGHT)
|
|
&& !(pressed & KEY_A) && !(pressed & KEY_B) && !(pressed & KEY_X) && !(pressed & KEY_Y)
|
|
&& !(pressed & KEY_L) && !(pressed & KEY_SELECT));
|
|
|
|
printf ("\x1B[47m"); // Print foreground white color
|
|
iprintf ("\x1b[%d;0H", fileOffset - screenOffset + ENTRIES_START_ROW);
|
|
|
|
if (isDSiMode() && !pressed && currentDrive == 1 && REG_SCFG_MC == 0x11 && flashcardMounted) {
|
|
flashcardUnmount();
|
|
screenMode = 0;
|
|
return "null";
|
|
}
|
|
|
|
if (pressed & KEY_UP) { fileOffset -= 1; bigJump = false; }
|
|
if (pressed & KEY_DOWN) { fileOffset += 1; bigJump = false; }
|
|
if (pressed & KEY_LEFT) { fileOffset -= ENTRY_PAGE_LENGTH; bigJump = true; }
|
|
if (pressed & KEY_RIGHT) { fileOffset += ENTRY_PAGE_LENGTH; bigJump = true; }
|
|
|
|
if ((fileOffset < 0) & (bigJump == false)) fileOffset = dirContents.size() - 1; // Wrap around to bottom of list (UP press)
|
|
else if ((fileOffset < 0) & (bigJump == true)) fileOffset = 0; // Move to bottom of list (RIGHT press)
|
|
if ((fileOffset > ((int)dirContents.size() - 1)) & (bigJump == false)) fileOffset = 0; // Wrap around to top of list (DOWN press)
|
|
else if ((fileOffset > ((int)dirContents.size() - 1)) & (bigJump == true)) fileOffset = dirContents.size() - 1; // Move to top of list (LEFT press)
|
|
|
|
|
|
// Scroll screen if needed
|
|
if (fileOffset < screenOffset) {
|
|
screenOffset = fileOffset;
|
|
}
|
|
if (fileOffset > screenOffset + ENTRIES_PER_SCREEN - 1) {
|
|
screenOffset = fileOffset - ENTRIES_PER_SCREEN + 1;
|
|
}
|
|
|
|
getcwd(path, PATH_MAX);
|
|
|
|
if ((!(held & KEY_R) && (pressed & KEY_A))
|
|
|| (!entry->isDirectory && (held & KEY_R) && (pressed & KEY_A))) {
|
|
if ((strcmp (entry->name.c_str(), "..") == 0) && (strcmp (path, getDrivePath()) == 0))
|
|
{
|
|
screenMode = 0;
|
|
return "null";
|
|
} else if (entry->isDirectory) {
|
|
iprintf("Entering directory ");
|
|
// Enter selected directory
|
|
chdir (entry->name.c_str());
|
|
getDirectoryContents (dirContents);
|
|
screenOffset = 0;
|
|
fileOffset = 0;
|
|
} else {
|
|
FileOperation getOp = fileBrowse_A(entry, path);
|
|
if(getOp == FileOperation::bootFile) {
|
|
// Return the chosen file
|
|
return entry->name;
|
|
} else if (getOp == FileOperation::copySdOut
|
|
|| getOp == FileOperation::copyFatOut
|
|
|| (getOp == FileOperation::mountNitroFS && nitroMounted)
|
|
|| (getOp == FileOperation::mountImg && imgMounted)) {
|
|
getDirectoryContents(dirContents); // Refresh directory listing
|
|
if ((getOp == FileOperation::mountNitroFS && nitroMounted)
|
|
|| (getOp == FileOperation::mountImg && imgMounted)) {
|
|
screenOffset = 0;
|
|
fileOffset = 0;
|
|
}
|
|
} else if(getOp == FileOperation::showInfo) {
|
|
for (int i = 0; i < 15; i++) swiWaitForVBlank();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Directory options
|
|
if (entry->isDirectory && (held & KEY_R) && (pressed & KEY_A)) {
|
|
if (strcmp(entry->name.c_str(), "..") == 0) {
|
|
screenMode = 0;
|
|
return "null";
|
|
} else {
|
|
FileOperation getOp = fileBrowse_A(entry, path);
|
|
if (getOp == FileOperation::copySdOut || getOp == FileOperation::copyFatOut) {
|
|
getDirectoryContents (dirContents); // Refresh directory listing
|
|
} else if (getOp == FileOperation::showInfo) {
|
|
for (int i = 0; i < 15; i++) swiWaitForVBlank();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pressed & KEY_B) {
|
|
if (strcmp (path, getDrivePath()) == 0) {
|
|
screenMode = 0;
|
|
return "null";
|
|
}
|
|
// Go up a directory
|
|
chdir ("..");
|
|
getDirectoryContents (dirContents);
|
|
screenOffset = 0;
|
|
fileOffset = 0;
|
|
}
|
|
|
|
// Rename file/folder
|
|
if ((held & KEY_R) && (pressed & KEY_X) && (strcmp (entry->name.c_str(), "..") != 0) && (strncmp (path, "nitro:/", 7) != 0)) {
|
|
printf ("\x1b[0;27H");
|
|
printf (" "); // Clear time
|
|
pressed = 0;
|
|
consoleDemoInit();
|
|
Keyboard *kbd = keyboardDemoInit();
|
|
char newName[256];
|
|
kbd->OnKeyPressed = OnKeyPressed;
|
|
|
|
keyboardShow();
|
|
printf("Rename to: \n");
|
|
fgets(newName, 256, stdin);
|
|
newName[strlen(newName)-1] = 0;
|
|
keyboardHide();
|
|
consoleClear();
|
|
|
|
reinitConsoles();
|
|
|
|
if (newName[0] != '\0') {
|
|
// Check for unsupported characters
|
|
for (int i = 0; i < (int)sizeof(newName); i++) {
|
|
if (newName[i] == '>'
|
|
|| newName[i] == '<'
|
|
|| newName[i] == ':'
|
|
|| newName[i] == '"'
|
|
|| newName[i] == '/'
|
|
|| newName[i] == '\x5C'
|
|
|| newName[i] == '|'
|
|
|| newName[i] == '?'
|
|
|| newName[i] == '*')
|
|
{
|
|
newName[i] = '_'; // Remove unsupported character
|
|
}
|
|
}
|
|
if (rename(entry->name.c_str(), newName) == 0) {
|
|
getDirectoryContents (dirContents);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Delete file/folder
|
|
if ((pressed & KEY_X) && (strcmp (entry->name.c_str(), "..") != 0) && (strncmp (path, "nitro:/", 7) != 0)) {
|
|
consoleSelect(&bottomConsole);
|
|
consoleClear();
|
|
printf ("\x1B[47m"); // Print foreground white color
|
|
if (clipboardOn)
|
|
iprintf("Delete %d files?\n", clipboard.size());
|
|
else
|
|
iprintf("Delete \"%s\"?\n", entry->name.c_str());
|
|
printf ("(<A> yes, <B> no)");
|
|
consoleSelect(&topConsole);
|
|
printf ("\x1B[30m"); // Print black color for time text
|
|
while (true) {
|
|
// Move to right side of screen
|
|
printf ("\x1b[0;26H");
|
|
// Print time
|
|
printf (" %s" ,RetTime().c_str());
|
|
|
|
scanKeys();
|
|
pressed = keysDownRepeat();
|
|
swiWaitForVBlank();
|
|
if (pressed & KEY_A) {
|
|
consoleSelect(&bottomConsole);
|
|
consoleClear();
|
|
printf ("\x1B[47m"); // Print foreground white color
|
|
if (clipboardOn) {
|
|
printf ("Deleting files, please wait...");
|
|
struct stat st;
|
|
for (auto &file : clipboard) {
|
|
if (FAT_getAttr(entry->name.c_str()) & ATTR_READONLY)
|
|
continue;
|
|
stat(file.path, &st);
|
|
if (st.st_mode & S_IFDIR)
|
|
recRemove(file.path, dirContents);
|
|
else
|
|
remove(file.path);
|
|
}
|
|
clipboard.clear();
|
|
clipboardOn = clipboardUsed = false;
|
|
fileOffset = 0;
|
|
} else if (FAT_getAttr(entry->name.c_str()) & ATTR_READONLY) {
|
|
printf ("Failed deleting:\n");
|
|
printf (entry->name.c_str());
|
|
printf ("\n");
|
|
printf ("\n");
|
|
printf ("(<A> to continue)");
|
|
pressed = 0;
|
|
consoleSelect(&topConsole);
|
|
printf ("\x1B[30m"); // Print black color for time text
|
|
while (!(pressed & KEY_A)) {
|
|
// Move to right side of screen
|
|
printf ("\x1b[0;26H");
|
|
// Print time
|
|
printf (" %s" ,RetTime().c_str());
|
|
|
|
scanKeys();
|
|
pressed = keysDown();
|
|
swiWaitForVBlank();
|
|
}
|
|
for (int i = 0; i < 15; i++) swiWaitForVBlank();
|
|
} else {
|
|
if (entry->isDirectory) {
|
|
printf ("Deleting folder, please wait...");
|
|
recRemove(entry->name.c_str(), dirContents);
|
|
} else {
|
|
printf ("Deleting file, please wait...");
|
|
remove(entry->name.c_str());
|
|
}
|
|
fileOffset--;
|
|
}
|
|
getDirectoryContents (dirContents);
|
|
pressed = 0;
|
|
break;
|
|
}
|
|
if (pressed & KEY_B) {
|
|
pressed = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create new folder
|
|
if ((held & KEY_R) && (pressed & KEY_Y) && (strncmp (path, "nitro:/", 7) != 0)) {
|
|
printf ("\x1b[0;27H");
|
|
printf (" "); // Clear time
|
|
pressed = 0;
|
|
consoleDemoInit();
|
|
Keyboard *kbd = keyboardDemoInit();
|
|
char newName[256];
|
|
kbd->OnKeyPressed = OnKeyPressed;
|
|
|
|
keyboardShow();
|
|
printf("Name for new folder: \n");
|
|
fgets(newName, 256, stdin);
|
|
newName[strlen(newName)-1] = 0;
|
|
keyboardHide();
|
|
consoleClear();
|
|
|
|
reinitConsoles();
|
|
|
|
if (newName[0] != '\0') {
|
|
// Check for unsupported characters
|
|
for (int i = 0; i < (int)sizeof(newName); i++) {
|
|
if (newName[i] == '>'
|
|
|| newName[i] == '<'
|
|
|| newName[i] == ':'
|
|
|| newName[i] == '"'
|
|
|| newName[i] == '/'
|
|
|| newName[i] == '\x5C'
|
|
|| newName[i] == '|'
|
|
|| newName[i] == '?'
|
|
|| newName[i] == '*')
|
|
{
|
|
newName[i] = '_'; // Remove unsupported character
|
|
}
|
|
}
|
|
if (mkdir(newName, 0777) == 0) {
|
|
getDirectoryContents (dirContents);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copy
|
|
if (pressed & KEY_L && strcmp (entry->name.c_str(), "..") != 0) {
|
|
if (!clipboardOn)
|
|
clipboard.clear();
|
|
char filePath[256];
|
|
snprintf(filePath, sizeof(filePath), "%s%s", path, entry->name.c_str());
|
|
bool exists = false;
|
|
for (auto &file : clipboard) {
|
|
if (strcmp (file.path, filePath)) // Check if file already in clipboard
|
|
continue;
|
|
exists = true;
|
|
break;
|
|
}
|
|
if (!exists) {
|
|
clipboard.emplace_back(filePath, entry->name.c_str(), entry->isDirectory, currentDrive, !strncmp (path, "nitro:/", 7));
|
|
clipboardOn = clipboardUsed = true;
|
|
}
|
|
}
|
|
|
|
// Paste
|
|
if (pressed & KEY_Y && clipboardOn && strncmp (path, "nitro:/", 7) != 0 && fileBrowse_paste(path)) {
|
|
getDirectoryContents (dirContents);
|
|
}
|
|
|
|
if ((pressed & KEY_SELECT) && clipboardUsed) {
|
|
clipboardOn = !clipboardOn;
|
|
}
|
|
|
|
// Make a screenshot
|
|
if ((held & KEY_R) && (pressed & KEY_L)) {
|
|
if (sdMounted || flashcardMounted) {
|
|
if (access((sdMounted ? "sd:/gm9i" : "fat:/gm9i"), F_OK) != 0) {
|
|
mkdir((sdMounted ? "sd:/gm9i" : "fat:/gm9i"), 0777);
|
|
if (strcmp (path, (sdMounted ? "sd:/" : "fat:/")) == 0) {
|
|
getDirectoryContents (dirContents);
|
|
}
|
|
}
|
|
if (access((sdMounted ? "sd:/gm9i/out" : "fat:/gm9i/out"), F_OK) != 0) {
|
|
mkdir((sdMounted ? "sd:/gm9i/out" : "fat:/gm9i/out"), 0777);
|
|
if (strcmp (path, (sdMounted ? "sd:/gm9i/" : "fat:/gm9i/")) == 0) {
|
|
getDirectoryContents (dirContents);
|
|
}
|
|
}
|
|
char timeText[8];
|
|
snprintf(timeText, sizeof(timeText), "%s", RetTime().c_str());
|
|
char fileTimeText[8];
|
|
snprintf(fileTimeText, sizeof(fileTimeText), "%s", RetTimeForFilename().c_str());
|
|
char snapPath[40];
|
|
// Take top screenshot
|
|
snprintf(snapPath, sizeof(snapPath), "%s:/gm9i/out/snap_%s_top.bmp", (sdMounted ? "sd" : "fat"), fileTimeText);
|
|
screenshotbmp(snapPath);
|
|
// Seamlessly swap top and bottom screens
|
|
lcdMainOnBottom();
|
|
printBorderBottom();
|
|
consoleSelect(&bottomConsole);
|
|
showDirectoryContents (dirContents, fileOffset, screenOffset);
|
|
printf("\x1B[30m"); // Print black color for time text
|
|
printf ("\x1b[0;26H");
|
|
printf (" %s" ,timeText);
|
|
clearBorderTop();
|
|
consoleSelect(&topConsole);
|
|
fileBrowse_drawBottomScreen(entry);
|
|
// Take bottom screenshot
|
|
snprintf(snapPath, sizeof(snapPath), "%s:/gm9i/out/snap_%s_bot.bmp", (sdMounted ? "sd" : "fat"), fileTimeText);
|
|
screenshotbmp(snapPath);
|
|
if (strcmp (path, (sdMounted ? "sd:/gm9i/out/" : "fat:/gm9i/out/")) == 0) {
|
|
getDirectoryContents (dirContents);
|
|
}
|
|
lcdMainOnTop();
|
|
printBorderTop();
|
|
clearBorderBottom();
|
|
}
|
|
}
|
|
}
|
|
}
|