Basic user interface done

This commit is contained in:
d0k3 2016-02-27 19:58:41 +01:00
parent 47a09f8353
commit c8765ca6c6
5 changed files with 243 additions and 65 deletions

View File

@ -9,7 +9,6 @@
#include "font.h"
#include "draw.h"
// #include "fs.h"
#include "hid.h"
void ClearScreen(u8* screen, int width, int color)
@ -22,7 +21,7 @@ void ClearScreen(u8* screen, int width, int color)
}
}
void ClearScreenFull(bool clear_top, bool clear_bottom, int color)
void ClearScreenF(bool clear_top, bool clear_bottom, int color)
{
if (clear_top) {
ClearScreen(TOP_SCREEN0, SCREEN_WIDTH_TOP, color);
@ -34,6 +33,32 @@ void ClearScreenFull(bool clear_top, bool clear_bottom, int color)
}
}
void DrawRectangle(u8* screen, int x, int y, int width, int height, int color)
{
for (int yy = 0; yy < height; yy++) {
int xDisplacement = (x * BYTES_PER_PIXEL * SCREEN_HEIGHT);
int yDisplacement = ((SCREEN_HEIGHT - (y + yy) - 1) * BYTES_PER_PIXEL);
u8* screenPos = screen + xDisplacement + yDisplacement;
for (int xx = width - 1; xx >= 0; xx--) {
*(screenPos + 0) = color >> 16; // B
*(screenPos + 1) = color >> 8; // G
*(screenPos + 2) = color & 0xFF; // R
screenPos += BYTES_PER_PIXEL * SCREEN_HEIGHT;
}
}
}
void DrawRectangleF(bool use_top, int x, int y, int width, int height, int color)
{
if (use_top) {
DrawRectangle(TOP_SCREEN0, x, y, width, height, color);
DrawRectangle(TOP_SCREEN1, x, y, width, height, color);
} else {
DrawRectangle(BOT_SCREEN0, x, y, width, height, color);
DrawRectangle(BOT_SCREEN1, x, y, width, height, color);
}
}
void DrawCharacter(u8* screen, int character, int x, int y, int color, int bgcolor)
{
for (int yy = 0; yy < 8; yy++) {
@ -83,19 +108,91 @@ void DrawStringF(bool use_top, int x, int y, int color, int bgcolor, const char
}
}
void ShowError(const char *format, ...)
u32 GetDrawStringHeight(const char* str) {
u32 height = 8;
for (char* lf = strchr(str, '\n'); (lf != NULL); lf = strchr(lf + 1, '\n'))
height += 10;
return height;
}
u32 GetDrawStringWidth(char* str) {
u32 width = 0;
char* old_lf = str;
for (char* lf = strchr(str, '\n'); (lf != NULL); lf = strchr(lf + 1, '\n')) {
if ((lf - old_lf) > width) width = lf - old_lf;
old_lf = lf;
}
if (old_lf == str)
width = strnlen(str, 256);
width *= 8;
return width;
}
void ResizeString(char* dest, const char* orig, int nsize, int tpos, bool align_right) {
int osize = strnlen(orig, 256);
if (nsize < osize) {
TruncateString(dest, orig, nsize, tpos);
} else if (!align_right) {
snprintf(dest, nsize + 1, "%-*.*s", nsize, nsize, orig);
} else {
snprintf(dest, nsize + 1, "%*.*s", nsize, nsize, orig);
}
}
void TruncateString(char* dest, const char* orig, int nsize, int tpos) {
int osize = strnlen(orig, 256);
if (nsize < 0) {
return;
} if (nsize <= 3) {
snprintf(dest, nsize, orig);
} else if (nsize >= osize) {
snprintf(dest, nsize + 1, orig);
} else {
if (tpos + 3 > nsize) tpos = nsize - 3;
snprintf(dest, nsize + 1, "%-.*s...%-.*s", tpos, orig, nsize - (3 + tpos), orig + osize - (nsize - (3 + tpos)));
}
}
void FormatBytes(char* str, u64 bytes) { // str should be 32 byte in size, just to be safe
const char* units[] = {" byte", "kB", "MB", "GB"};
if (bytes == (u64) -1) snprintf(str, 32, "INVALID");
else if (bytes < 1024) snprintf(str, 32, "%llu%s", bytes, units[0]);
else {
u32 scale = 1;
u64 bytes100 = (bytes * 100) >> 10;
for(; (bytes100 >= 1024*100) && (scale < 3); scale++, bytes100 >>= 10);
snprintf(str, 32, "%llu.%llu%s", bytes100 / 100, (bytes100 % 100) / 10, units[scale]);
}
}
bool ShowPrompt(bool ask, const char *format, ...)
{
char str[128] = {}; // 128 should be more than enough
u32 str_width, str_height;
u32 x, y;
char str[384] = {}; // 384 should be more than enough
va_list va;
va_start(va, format);
vsnprintf(str, 128, format, va);
vsnprintf(str, 384, format, va);
va_end(va);
ClearScreenFull(true, false, COLOR_BLACK);
DrawStringF(true, 80, 80, COLOR_WHITE, COLOR_BLACK, str);
str_width = GetDrawStringWidth(str);
str_height = GetDrawStringHeight(str) + (2 * 10);
x = (str_width >= SCREEN_WIDTH_TOP) ? 0 : (SCREEN_WIDTH_TOP - str_width) / 2;
y = (str_height >= SCREEN_HEIGHT) ? 0 : (SCREEN_HEIGHT - str_height) / 2;
// InputWait();
ClearScreenF(true, false, COLOR_STD_BG);
DrawStringF(true, x, y, COLOR_STD_FONT, COLOR_STD_BG, (ask) ? "%s\n\n(<A> to continue, <B> to cancel)" : "%s\n\n(<A> to continue)", str);
while (true) {
u32 pad_state = InputWait();
if (pad_state & BUTTON_A) break;
else if (pad_state & BUTTON_B) return false;
}
return true;
}
/*void ShowProgress(u64 current, u64 total)

View File

@ -15,9 +15,23 @@
#define COLOR_BLACK RGB(0x00, 0x00, 0x00)
#define COLOR_WHITE RGB(0xFF, 0xFF, 0xFF)
#define COLOR_GREY RGB(0x7F, 0x7F, 0x7F)
#define COLOR_GREY RGB(0x80, 0x80, 0x80)
#define COLOR_RED RGB(0xFF, 0x00, 0x00)
#define COLOR_GREEN RGB(0x00, 0xFF, 0x00)
#define COLOR_BLUE RGB(0xFF, 0x00, 0xFF)
#define COLOR_YELLOW RGB(0xFF, 0xFF, 0x00)
#define COLOR_CYAN RGB(0xFF, 0x00, 0xFF)
#define COLOR_TINTEDBLUE RGB(0x60, 0x60, 0x80)
#define COLOR_TINTEDYELLOW RGB(0xD0, 0xD0, 0x60)
#define COLOR_TINTEDGREEN RGB(0x70, 0x80, 0x70)
#define COLOR_TRANSPARENT RGB(0xFF, 0x00, 0xEF) // otherwise known as 'super fuchsia'
#define COLOR_STD_BG COLOR_BLACK
#define COLOR_STD_FONT COLOR_WHITE
#ifdef EXEC_GATEWAY
#define TOP_SCREEN0 (u8*)(*(u32*)((uint32_t)0x080FFFC0 + 4 * (*(u32*)0x080FFFD8 & 1)))
#define BOT_SCREEN0 (u8*)(*(u32*)((uint32_t)0x080FFFD0 + 4 * (*(u32*)0x080FFFDC & 1)))
@ -33,11 +47,21 @@
#endif
void ClearScreen(unsigned char *screen, int width, int color);
void ClearScreenFull(bool clear_top, bool clear_bottom, int color);
void ClearScreenF(bool clear_top, bool clear_bottom, int color);
void DrawRectangle(u8* screen, int x, int y, int width, int height, int color);
void DrawRectangleF(bool use_top, int x, int y, int width, int height, int color);
void DrawCharacter(unsigned char *screen, int character, int x, int y, int color, int bgcolor);
void DrawString(unsigned char *screen, const char *str, int x, int y, int color, int bgcolor);
void DrawStringF(bool use_top, int x, int y, int color, int bgcolor, const char *format, ...);
void ShowError(const char *format, ...);
u32 GetDrawStringHeight(const char* str);
u32 GetDrawStringWidth(char* str);
void ResizeString(char* dest, const char* orig, int nsize, int tpos, bool align_right);
void TruncateString(char* dest, const char* orig, int nsize, int tpos);
void FormatBytes(char* str, u64 bytes);
bool ShowPrompt(bool ask, const char *format, ...);
void ShowProgress(u64 current, u64 total);

View File

@ -18,18 +18,17 @@ bool InitFS()
*(u32*)0x10000020 = 0;
*(u32*)0x10000020 = 0x340;
#endif
for (numfs = 0; numfs < 16; numfs++) {
for (numfs = 0; numfs < 7; numfs++) {
char fsname[8];
snprintf(fsname, 8, "%lu:", numfs);
int res = f_mount(fs + numfs, fsname, 1);
if (res != FR_OK) {
if (numfs >= 4) break;
ShowError("Initialising failed! (%lu/%s/%i)", numfs, fsname, res);
ShowPrompt(false, "Initialising failed! (%lu/%s/%i)", numfs, fsname, res);
DeinitFS();
return false;
}
}
ShowError("Mounted: %i partitions", numfs);
return true;
}
@ -43,10 +42,6 @@ void DeinitFS()
numfs = 0;
}
bool FileExists(const char* path) {
return (f_stat(path, NULL) == FR_OK);
}
bool FileCreate(const char* path, u8* data, u32 size) {
FIL file;
UINT bytes_written = 0;
@ -72,7 +67,7 @@ void Screenshot()
for (; n < 1000; n++) {
snprintf(filename, 16, "0:/snap%03i.bmp", (int) n);
if (!FileExists(filename)) break;
if (f_stat(filename, NULL) == FR_OK) break;
}
if (n >= 1000) return;
@ -90,20 +85,18 @@ void Screenshot()
bool GetRootDirContentsWorker(DirStruct* contents) {
static const char* drvname[16] = {
"SDCARD",
"SYSCTRN", "SYSTWLN", "SYSTWLP",
"EMU0CTRN", "EMU0TWLN", "EMU0TWLP",
"EMU1CTRN", "EMU1TWLN", "EMU1TWLP",
"EMU2CTRN", "EMU2TWLN", "EMU2TWLP",
"EMU3CTRN", "EMU3TWLN", "EMU3TWLP"
"SYSNAND CTRNAND", "SYSNAND TWLN", "SYSNAND TWLP",
"EMUNAND CTRNAND", "EMUNAND TWLN", "EMUNAND TWLP"
};
for (u32 pdrv = 0; (pdrv < numfs) && (pdrv < MAX_ENTRIES); pdrv++) {
memset(contents->entry[pdrv].path, 0x00, 16);
snprintf(contents->entry[pdrv].path + 0, 4, "%lu:", pdrv);
snprintf(contents->entry[pdrv].path + 4, 16, "[%lu:] %s", pdrv, drvname[pdrv]);
snprintf(contents->entry[pdrv].path + 4, 32, "[%lu:] %s", pdrv, drvname[pdrv]);
contents->entry[pdrv].name = contents->entry[pdrv].path + 4;
contents->entry[pdrv].size = 0;
contents->entry[pdrv].type = T_FAT_DIR;
contents->entry[pdrv].size = GetTotalSpace(contents->entry[pdrv].path);
contents->entry[pdrv].type = T_FAT_ROOT;
contents->entry[pdrv].marked = 0;
}
contents->n_entries = numfs;
@ -141,6 +134,7 @@ bool GetDirContentsWorker(DirStruct* contents, char* fpath, int fsize, bool recu
entry->type = T_FAT_FILE;
entry->size = fno.fsize;
}
entry->marked = 0;
contents->n_entries++;
if (contents->n_entries >= MAX_ENTRIES)
break;
@ -178,20 +172,32 @@ DirStruct* GetDirContents(const char* path) {
#else
return sectors * _MAX_SS;
#endif
}
}*/
uint64_t RemainingStorageSpace()
uint64_t GetFreeSpace(const char* path)
{
DWORD free_clusters;
FATFS *fs2;
FRESULT res = f_getfree("0:", &free_clusters, &fs2);
if (res)
FATFS *fs_ptr;
char fsname[4] = { '\0' };
int fsnum = -1;
strncpy(fsname, path, 2);
fsnum = *fsname - (int) '0';
if ((fsnum < 0) || (fsnum >= 7) || (fsname[1] != ':'))
return -1;
if (f_getfree(fsname, &free_clusters, &fs_ptr) != FR_OK)
return -1;
return ClustersToBytes(&fs, free_clusters);
return (uint64_t) free_clusters * fs[fsnum].csize * _MAX_SS;
}
uint64_t TotalStorageSpace()
uint64_t GetTotalSpace(const char* path)
{
return ClustersToBytes(&fs, fs.n_fatent - 2);
}*/
int fsnum = -1;
fsnum = *path - (int) '0';
if ((fsnum < 0) || (fsnum >= 7) || (path[1] != ':'))
return -1;
return (uint64_t) (fs[fsnum].n_fatent - 2) * fs[fsnum].csize * _MAX_SS;
}

View File

@ -3,8 +3,6 @@
#include "common.h"
typedef enum {
T_NAND_BASE, // might not be needed
T_NONFAT_ROOT, // might not be needed
T_FAT_ROOT,
T_FAT_FILE,
T_FAT_DIR
@ -15,8 +13,9 @@ typedef enum {
typedef struct {
char* name; // should point to the correct portion of the path
char path[256];
u32 size;
u64 size;
EntryType type;
u8 marked;
} DirEntry;
typedef struct {
@ -27,9 +26,6 @@ typedef struct {
bool InitFS();
void DeinitFS();
/** Check if file exists **/
bool FileExists(const char* path);
/** Create / overwrite file and write the provided data to it **/
bool FileCreate(const char* path, u8* data, u32 size);
@ -39,8 +35,8 @@ void Screenshot();
/** Get directory content under a given path **/
DirStruct* GetDirContents(const char* path);
/** Gets remaining space on SD card in bytes */
uint64_t RemainingStorageSpace();
/** Gets remaining space in filesystem in bytes */
uint64_t GetFreeSpace(const char* path);
/** Gets total space on SD card in bytes */
uint64_t TotalStorageSpace();
/** Gets total spacein filesystem in bytes */
uint64_t GetTotalSpace(const char* path);

View File

@ -3,24 +3,72 @@
#include "hid.h"
#include "fs.h"
void DrawDirContents(DirStruct* contents, u32* offset, u32 cursor) {
#define COLOR_TOP_BAR COLOR_WHITE
#define COLOR_MARKED COLOR_TINTEDYELLOW
#define COLOR_FILE COLOR_TINTEDGREEN
#define COLOR_DIR COLOR_TINTEDBLUE
#define COLOR_ROOT COLOR_GREY
void DrawUserInterface(const char* curr_path, DirEntry* curr_entry) {
const u32 info_start = 16;
char bytestr0[32];
char bytestr1[32];
char tempstr[64];
// top bar - current path & free/total storage
DrawRectangleF(true, 0, 0, SCREEN_WIDTH_TOP, 12, COLOR_TOP_BAR);
if (strncmp(curr_path, "", 256) != 0) {
TruncateString(tempstr, curr_path, 30, 8);
DrawStringF(true, 2, 2, COLOR_STD_BG, COLOR_TOP_BAR, tempstr);
DrawStringF(true, 30 * 8 + 4, 2, COLOR_STD_BG, COLOR_TOP_BAR, "%19.19s", "LOADING...");
FormatBytes(bytestr0, GetFreeSpace(curr_path));
FormatBytes(bytestr1, GetTotalSpace(curr_path));
snprintf(tempstr, 64, "%s/%s", bytestr0, bytestr1);
DrawStringF(true, 30 * 8 + 4, 2, COLOR_STD_BG, COLOR_TOP_BAR, "%19.19s", tempstr);
} else {
DrawStringF(true, 2, 2, COLOR_STD_BG, COLOR_TOP_BAR, "[root]");
DrawStringF(true, 30 * 8 + 6, 2, COLOR_STD_BG, COLOR_TOP_BAR, "%19.19s", "GodMode9");
}
// left top - current file info
ResizeString(tempstr, curr_entry->name, 20, 8, false);
DrawStringF(true, 2, info_start, (curr_entry->marked) ? COLOR_MARKED : COLOR_STD_FONT, COLOR_STD_BG, "%s", tempstr);
if (curr_entry->type == T_FAT_DIR) {
ResizeString(tempstr, "(dir)", 20, 8, false);
DrawStringF(true, 4, info_start + 10, COLOR_DIR, COLOR_STD_BG, tempstr);
} else {
FormatBytes(bytestr0, curr_entry->size);
ResizeString(tempstr, bytestr0, 20, 8, false);
DrawStringF(true, 4, info_start + 10, COLOR_FILE, COLOR_STD_BG, tempstr);
}
// bottom: inctruction block
char* instr = "GodMode 9 v0.0.1\n<A>/<B>/<\x18\x19\x1A\x1B> - Navigation\n<L> - Mark (multiple) file(s)\n<X> - Make a Screenshot\n<START/+\x1B> - Reboot / Power off";
DrawStringF(true, (SCREEN_WIDTH_TOP - GetDrawStringWidth(instr)) / 2, SCREEN_HEIGHT - 2 - GetDrawStringHeight(instr), COLOR_STD_FONT, COLOR_STD_BG, instr);
}
void DrawDirContents(DirStruct* contents, u32 cursor) {
static u32 offset_disp = 0;
const int str_width = 40;
const u32 start_y = 2;
const u32 stp_y = 12;
const u32 pos_x = 0;
u32 pos_y = 2;
const u32 lines = (SCREEN_HEIGHT-start_y+stp_y-1) / stp_y;
u32 pos_y = start_y;
if (offset_disp > cursor) offset_disp = cursor;
else if (offset_disp + lines <= cursor) offset_disp = cursor - lines + 1;
for (u32 i = 0; pos_y < SCREEN_HEIGHT; i++) {
char tempstr[str_width + 1];
u32 offset_i = *offset + i;
u32 offset_i = offset_disp + i;
u32 color_bg = COLOR_STD_BG;
u32 color_font;
u32 color_bg;
if (offset_i < contents->n_entries) {
if (cursor != offset_i) {
color_font = COLOR_GREY;
color_bg = COLOR_BLACK;
color_font = (contents->entry[offset_i].marked) ? COLOR_MARKED : (contents->entry[offset_i].type == T_FAT_DIR) ? COLOR_DIR : (contents->entry[offset_i].type == T_FAT_FILE) ? COLOR_FILE : COLOR_ROOT;
} else {
color_font = COLOR_WHITE;
color_bg = COLOR_BLACK;
color_font = COLOR_STD_FONT;
}
snprintf(tempstr, str_width + 1, "%-*.*s", str_width, str_width, contents->entry[offset_i].name);
} else {
@ -34,21 +82,19 @@ void DrawDirContents(DirStruct* contents, u32* offset, u32 cursor) {
}
u32 GodMode() {
static const u32 quick_stp = 20;
u32 exit_mode = GODMODE_EXIT_REBOOT;
char current_path[256] = { 0x00 };
DirStruct* contents;
u32 cursor = 0;
u32 offset_disp = 0;
ClearScreenFull(true, true, COLOR_BLACK);
if (!InitFS()) {
InputWait();
return exit_mode;
}
ClearScreenF(true, true, COLOR_BLACK);
if (!InitFS()) return exit_mode;
contents = GetDirContents("");
while (true) { // this is the main loop
DrawDirContents(contents, &offset_disp, cursor);
DrawUserInterface(current_path, &contents->entry[cursor]);
DrawDirContents(contents, cursor);
u32 pad_state = InputWait();
if (pad_state & BUTTON_DOWN) {
cursor++;
@ -56,17 +102,26 @@ u32 GodMode() {
cursor = contents->n_entries - 1;
} else if ((pad_state & BUTTON_UP) && cursor) {
cursor--;
} else if ((pad_state & BUTTON_A) && (contents->entry[cursor].type == T_FAT_DIR)) {
} else if (pad_state & BUTTON_RIGHT) {
cursor += quick_stp;
if (cursor >= contents->n_entries)
cursor = contents->n_entries - 1;
} else if (pad_state & BUTTON_LEFT) {
cursor = (cursor >= quick_stp) ? cursor - quick_stp : 0;
} else if ((pad_state & BUTTON_L1) && *current_path) {
contents->entry[cursor].marked ^= 0x1;
} else if ((pad_state & BUTTON_A) && (contents->entry[cursor].type != T_FAT_FILE)) {
strncpy(current_path, contents->entry[cursor].path, 256);
contents = GetDirContents(current_path);
cursor = offset_disp = 0;
ShowError(current_path);
cursor = 0;
ClearScreenF(true, true, COLOR_STD_BG);
} else if (pad_state & BUTTON_B) {
char* last_slash = strrchr(current_path, '/');
if (last_slash) *last_slash = '\0';
else *current_path = '\0';
contents = GetDirContents(current_path);
cursor = offset_disp = 0;
cursor = 0;
ClearScreenF(true, true, COLOR_STD_BG);
} else if (pad_state & BUTTON_X) {
Screenshot();
}