General code cleanup, update template.

This commit is contained in:
Steven Smith 2015-01-27 06:57:54 -08:00
parent e5d51d5c40
commit b626f6f396
8 changed files with 469 additions and 611 deletions

View File

@ -39,7 +39,7 @@ ICON := resources/icon.png
#--------------------------------------------------------------------------------- #---------------------------------------------------------------------------------
ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=softfp ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=softfp
CFLAGS := -g -Wall -O3 -mword-relocations \ CFLAGS := -g -Wall -Wno-strict-aliasing -O3 -mword-relocations \
-fomit-frame-pointer -ffast-math \ -fomit-frame-pointer -ffast-math \
$(ARCH) $(ARCH)
@ -155,7 +155,7 @@ banner.bnr: $(TOPDIR)/resources/banner.png $(TOPDIR)/resources/audio.wav
@echo "built ... banner" @echo "built ... banner"
icon.icn: $(TOPDIR)/resources/icon.png icon.icn: $(TOPDIR)/resources/icon.png
$(BANNERTOOL) makesmdh -s "$(APP_TITLE)" -l "$(APP_DESCRIPTION)" -p "$(APP_AUTHOR)" -i $(TOPDIR)/resources/icon.png -o icon.icn $(BANNERTOOL) makesmdh -s "$(APP_TITLE)" -l "$(APP_TITLE)" -p "$(APP_AUTHOR)" -i $(TOPDIR)/resources/icon.png -o icon.icn
@echo "built ... icon" @echo "built ... icon"
stripped.elf: $(OUTPUT).elf stripped.elf: $(OUTPUT).elf

View File

@ -3,10 +3,16 @@
#include <sys/unistd.h> #include <sys/unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <sys/dirent.h>
#include <stack>
#include <algorithm>
#include <sstream>
#include <iomanip>
#include <3ds.h> #include <3ds.h>
#include "common.h" #include "common.hpp"
static unsigned char asciiData[128][8] = { static unsigned char asciiData[128][8] = {
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
@ -155,56 +161,16 @@ PAD_KEY buttonMap[13] = {
KEY_TOUCH KEY_TOUCH
}; };
const int defaultBufferSize = 100;
static char defaultBuffer[100];
char* sdprintf(const char* format, ...) {
va_list args;
va_start(args, format);
char* ret = vsdprintf(format, args);
va_end(args);
return ret;
}
char* vsdprintf(const char* format, va_list args) {
va_list copy;
va_copy(copy, args);
char* ret = NULL;
int len = vsnprintf(defaultBuffer, defaultBufferSize, format, args);
if(len >= defaultBufferSize) {
char* buffer = (char*) malloc(len * sizeof(char));
vsnprintf(buffer, (size_t) len, format, copy);
ret = buffer;
} else {
char* buffer = (char*) malloc(defaultBufferSize * sizeof(char));
strcpy(buffer, defaultBuffer);
ret = buffer;
}
va_end(copy);
return ret;
}
u8* fb = NULL; u8* fb = NULL;
u16 fbWidth = 0; u16 fbWidth = 0;
u16 fbHeight = 0; u16 fbHeight = 0;
bool screen_begin_draw() { bool screen_begin_draw(Screen screen) {
if(fb != NULL) { if(fb != NULL) {
return false; return false;
} }
fb = gfxGetFramebuffer(GFX_BOTTOM, GFX_LEFT, &fbWidth, &fbHeight); fb = gfxGetFramebuffer(screen == TOP_SCREEN ? GFX_TOP : GFX_BOTTOM, GFX_LEFT, &fbWidth, &fbHeight);
return true;
}
bool screen_begin_draw_info() {
if(fb != NULL) {
return false;
}
fb = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, &fbWidth, &fbHeight);
return true; return true;
} }
@ -236,11 +202,7 @@ int screen_get_width() {
return fbHeight; return fbHeight;
} }
int width = 0; return 0;
screen_begin_draw();
width = fbHeight;
screen_end_draw();
return width;
} }
int screen_get_height() { int screen_get_height() {
@ -249,16 +211,12 @@ int screen_get_height() {
return fbWidth; return fbWidth;
} }
int height = 0; return 0;
screen_begin_draw();
height = fbWidth;
screen_end_draw();
return height;
} }
void screen_take_screenshot() { void screen_take_screenshot() {
u32 imageSize = 400 * 480 * 3; u32 imageSize = 400 * 480 * 3;
u8* temp = (u8*) malloc(0x36 + imageSize); u8 temp[0x36 + imageSize];
memset(temp, 0, 0x36 + imageSize); memset(temp, 0, 0x36 + imageSize);
*(u16*) &temp[0x0] = 0x4D42; *(u16*) &temp[0x0] = 0x4D42;
@ -293,11 +251,10 @@ void screen_take_screenshot() {
} }
char file[256]; char file[256];
snprintf(file, 256, "sdmc:/wo3ds_screenshot_%08d.bmp", (int) (svcGetSystemTick() / 446872)); snprintf(file, 256, "sdmc:/screenshot_%08d.bmp", (int) (svcGetSystemTick() / 446872));
int fd = open(file, O_WRONLY | O_CREAT | O_SYNC); int fd = open(file, O_WRONLY | O_CREAT | O_SYNC);
write(fd, temp, 0x36 + imageSize); write(fd, temp, 0x36 + imageSize);
close(fd); close(fd);
free(temp);
} }
int screen_get_index(int x, int y) { int screen_get_index(int x, int y) {
@ -361,11 +318,11 @@ void screen_fill(int x, int y, int width, int height, u8 r, u8 g, u8 b) {
} }
} }
int screen_get_str_width(const char* str) { int screen_get_str_width(std::string str) {
return strlen(str) * 8; return str.length() * 8;
} }
int screen_get_str_height(const char* str) { int screen_get_str_height(std::string str) {
return 8; return 8;
} }
@ -385,16 +342,16 @@ void screen_draw_char(char c, int x, int y, u8 r, u8 g, u8 b) {
} }
} }
void screen_draw_string(const char* string, int x, int y, u8 r, u8 g, u8 b) { void screen_draw_string(std::string str, int x, int y, u8 r, u8 g, u8 b) {
if(fb == NULL) { if(fb == NULL) {
return; return;
} }
int len = (int) strlen(string); int len = str.length();
int cx = x; int cx = x;
int cy = y; int cy = y;
for(int i = 0; i < len; i++) { for(int i = 0; i < len; i++) {
char c = string[i]; char c = str[i];
if(c == '\n') { if(c == '\n') {
cx = x; cx = x;
cy += 8; cy += 8;
@ -409,18 +366,242 @@ void screen_clear(u8 r, u8 g, u8 b) {
screen_fill(0, 0, screen_get_width(), screen_get_height(), r, g, b); screen_fill(0, 0, screen_get_width(), screen_get_height(), r, g, b);
} }
void screen_clear_all() { typedef struct {
for(int i = 0; i < 2; i++) { std::string id;
screen_begin_draw(); std::string name;
screen_clear(0, 0, 0); std::vector<std::string> details;
screen_end_draw(); } SelectableElement;
screen_begin_draw_info(); SelectionResult ui_select(std::vector<SelectableElement> elements, SelectableElement* selected, bool enableBack, std::function<bool()> onLoop) {
screen_clear(0, 0, 0); u32 cursor = 0;
screen_end_draw(); u32 scroll = 0;
screen_swap_buffers(); u32 selectionScroll = 0;
} u64 selectionScrollEndTime = 0;
while(platform_is_running()) {
input_poll();
if(input_is_pressed(BUTTON_A)) {
*selected = elements.at(cursor);
return SELECTED;
}
if(enableBack && input_is_pressed(BUTTON_B)) {
return BACK;
}
if(input_is_pressed(BUTTON_DOWN) && cursor < elements.size() - 1) {
cursor++;
if(cursor >= scroll + 20) {
scroll++;
}
selectionScroll = 0;
selectionScrollEndTime = 0;
}
if(input_is_pressed(BUTTON_UP) && cursor > 0) {
cursor--;
if(cursor < scroll) {
scroll--;
}
selectionScroll = 0;
selectionScrollEndTime = 0;
}
screen_begin_draw(BOTTOM_SCREEN);
screen_clear(0, 0, 0);
u32 screenWidth = (u32) screen_get_width();
for(std::vector<SelectableElement>::iterator it = elements.begin() + scroll; it != elements.begin() + scroll + 20 && it != elements.end(); it++) {
SelectableElement element = *it;
u32 index = (u32) (it - elements.begin());
u8 color = 255;
int offset = 0;
if(index == cursor) {
color = 0;
screen_fill(0, (int) (index - scroll) * 12, (int) screenWidth, screen_get_str_height(element.name), 255, 255, 255);
u32 width = (u32) screen_get_str_width(element.name);
if(width > screenWidth) {
if(selectionScroll + screenWidth >= width) {
if(selectionScrollEndTime == 0) {
selectionScrollEndTime = platform_get_time();
} else if(platform_get_time() - selectionScrollEndTime >= 4000) {
selectionScroll = 0;
selectionScrollEndTime = 0;
}
} else {
selectionScroll++;
}
}
offset = -selectionScroll;
}
screen_draw_string(element.name, offset, (int) (index - scroll) * 12, color, color, color);
}
screen_end_draw();
screen_begin_draw(TOP_SCREEN);
screen_clear(0, 0, 0);
SelectableElement currSelected = elements.at(cursor);
if(currSelected.details.size() != 0) {
for(std::vector<std::string>::iterator it = currSelected.details.begin(); it != currSelected.details.end(); it++) {
std::string detail = *it;
u32 index = (u32) (it - currSelected.details.begin());
screen_draw_string(detail, 0, (int) index * 12, 255, 255, 255);
}
}
bool result = onLoop();
screen_end_draw();
screen_swap_buffers();
if(result) {
return MANUAL_BREAK;
}
}
return APP_CLOSING;
}
bool ui_is_directory(std::string path) {
DIR *dir = opendir(path.c_str());
if(!dir) {
return false;
}
closedir(dir);
return true;
}
struct ui_alphabetize {
inline bool operator() (SelectableElement a, SelectableElement b) {
return a.name.compare(b.name) < 0;
}
};
std::vector<SelectableElement> ui_get_dir_elements(std::string directory, std::string extension) {
std::vector<SelectableElement> elements;
elements.push_back({".", "."});
elements.push_back({"..", ".."});
DIR *dir = opendir(directory.c_str());
if(dir != NULL) {
while(true) {
struct dirent *ent = readdir(dir);
if(ent == NULL) {
break;
}
std::string dirName = std::string(ent->d_name);
std::string path = directory + "/" + dirName;
if(ui_is_directory(path)) {
elements.push_back({path, dirName});
} else {
std::string::size_type dotPos = path.rfind('.');
if(dotPos != std::string::npos && path.substr(dotPos + 1).compare(extension) == 0) {
struct stat st;
stat(path.c_str(), &st);
std::vector<std::string> info;
std::stringstream stream;
stream << "File Size: " << st.st_size << " bytes (" << std::fixed << std::setprecision(2) << st.st_size / 1024.0f / 1024.0f << "MB)";
info.push_back(stream.str());
elements.push_back({path, dirName, info});
}
}
}
closedir(dir);
}
std::sort(elements.begin(), elements.end(), ui_alphabetize());
return elements;
}
bool ui_select_file(std::string rootDirectory, std::string extension, std::string* selectedFile, std::function<bool()> onLoop) {
std::stack<std::string> directoryStack;
std::string currDirectory = rootDirectory;
while(platform_is_running()) {
SelectableElement selected;
std::vector<SelectableElement> contents = ui_get_dir_elements(currDirectory, extension);
SelectionResult result = ui_select(contents, &selected, !directoryStack.empty(), onLoop);
if(result == APP_CLOSING || result == MANUAL_BREAK) {
break;
} else if(result == BACK) {
currDirectory = directoryStack.top();
directoryStack.pop();
} else if(result == SELECTED) {
if(selected.name.compare(".") == 0) {
continue;
} else if(selected.name.compare("..") == 0) {
if(directoryStack.empty()) {
continue;
}
currDirectory = directoryStack.top();
directoryStack.pop();
} else {
if(ui_is_directory(selected.id)) {
directoryStack.push(currDirectory);
currDirectory = selected.id;
} else {
*selectedFile = selected.id;
return true;
}
}
}
}
return false;
}
bool ui_select_app(MediaType mediaType, App* selectedApp, std::function<bool()> onLoop) {
std::vector<App> apps = app_list(mediaType);
std::vector<SelectableElement> elements;
for(std::vector<App>::iterator it = apps.begin(); it != apps.end(); it++) {
App app = *it;
std::stringstream titleId;
titleId << std::setfill('0') << std::setw(16) << std::hex << app.titleId;
std::stringstream uniqueId;
uniqueId << std::setfill('0') << std::setw(8) << std::hex << app.uniqueId;
std::vector<std::string> details;
details.push_back("Title ID: " + titleId.str());
details.push_back("Unique ID: " + uniqueId.str());
details.push_back("Product Code: " + std::string(app.productCode));
details.push_back("Platform: " + app_get_platform_name(app.platform));
details.push_back("Category: " + app_get_category_name(app.category));
elements.push_back({titleId.str(), app.productCode, details});
}
if(elements.size() == 0) {
elements.push_back({"None", "None"});
}
std::sort(elements.begin(), elements.end(), ui_alphabetize());
SelectableElement selected;
SelectionResult result = ui_select(elements, &selected, false, onLoop);
if(result != APP_CLOSING && result != MANUAL_BREAK && selected.id.compare("None") != 0) {
for(std::vector<App>::iterator it = apps.begin(); it != apps.end(); it++) {
App app = *it;
if(app.titleId == (u64) strtoll(selected.id.c_str(), NULL, 16)) {
*selectedApp = app;
}
}
return true;
}
return false;
} }
void input_poll() { void input_poll() {
@ -509,7 +690,7 @@ AppCategory app_category_from_id(u16 id) {
return APP; return APP;
} }
const char* app_get_platform_name(AppPlatform platform) { std::string app_get_platform_name(AppPlatform platform) {
switch(platform) { switch(platform) {
case WII: case WII:
return "Wii"; return "Wii";
@ -524,7 +705,7 @@ const char* app_get_platform_name(AppPlatform platform) {
} }
} }
const char* app_get_category_name(AppCategory category) { std::string app_get_category_name(AppCategory category) {
switch(category) { switch(category) {
case APP: case APP:
return "App"; return "App";
@ -541,35 +722,23 @@ const char* app_get_category_name(AppCategory category) {
} }
} }
App* app_list(MediaType mediaType, u32* count) { std::vector<App> app_list(MediaType mediaType) {
std::vector<App> titles;
if(!am_prepare()) { if(!am_prepare()) {
return NULL; return titles;
} }
u32 titleCount; u32 titleCount;
if(AM_GetTitleCount(app_mediatype_to_byte(mediaType), &titleCount) != 0) { if(AM_GetTitleCount(app_mediatype_to_byte(mediaType), &titleCount) != 0) {
if(count != NULL) { return titles;
*count = 0;
}
return (App*) malloc(0);
}
if(count != NULL) {
*count = titleCount;
} }
u64 titleIds[titleCount]; u64 titleIds[titleCount];
if(AM_GetTitleList(app_mediatype_to_byte(mediaType), titleCount, titleIds) != 0) { if(AM_GetTitleList(app_mediatype_to_byte(mediaType), titleCount, titleIds) != 0) {
if(count != NULL) { return titles;
*count = 0;
}
return (App*) malloc(0);
} }
App* titles = (App*) malloc(titleCount * sizeof(App)); for(u32 i = 0; i < titleCount; i++) {
for(int i = 0; i < titleCount; i++) {
u64 titleId = titleIds[i]; u64 titleId = titleIds[i];
App app; App app;
app.titleId = titleId; app.titleId = titleId;
@ -583,18 +752,18 @@ App* app_list(MediaType mediaType, u32* count) {
app.platform = app_platform_from_id(((u16*) &titleId)[3]); app.platform = app_platform_from_id(((u16*) &titleId)[3]);
app.category = app_category_from_id(((u16*) &titleId)[2]); app.category = app_category_from_id(((u16*) &titleId)[2]);
titles[i] = app; titles.push_back(app);
} }
return titles; return titles;
} }
bool app_install(MediaType mediaType, const char* path, bool (*onProgress)(int progress)) { bool app_install(MediaType mediaType, std::string path, std::function<bool(int)> onProgress) {
if(!am_prepare()) { if(!am_prepare()) {
return false; return false;
} }
FILE* fd = fopen(path, "r"); FILE* fd = fopen(path.c_str(), "r");
if(!fd) { if(!fd) {
return false; return false;
} }
@ -615,7 +784,7 @@ bool app_install(MediaType mediaType, const char* path, bool (*onProgress)(int p
FSFILE_SetSize(ciaHandle, size); FSFILE_SetSize(ciaHandle, size);
u32 bufSize = 1024 * 256; // 256KB u32 bufSize = 1024 * 256; // 256KB
void* buf = malloc(bufSize); void* buf = malloc(bufSize);
bool cancelled = false; bool cancelled = false;
for(u64 pos = 0; pos < size; pos += bufSize) { for(u64 pos = 0; pos < size; pos += bufSize) {
if(onProgress != NULL && !onProgress((int) ((pos / (float) size) * 100))) { if(onProgress != NULL && !onProgress((int) ((pos / (float) size) * 100))) {
@ -628,7 +797,7 @@ bool app_install(MediaType mediaType, const char* path, bool (*onProgress)(int p
FSFILE_Write(ciaHandle, NULL, pos, buf, bytesRead, FS_WRITE_NOFLUSH); FSFILE_Write(ciaHandle, NULL, pos, buf, bytesRead, FS_WRITE_NOFLUSH);
} }
free(buf); free(buf);
fclose(fd); fclose(fd);
if(cancelled) { if(cancelled) {
@ -640,27 +809,27 @@ bool app_install(MediaType mediaType, const char* path, bool (*onProgress)(int p
} }
Result res = AM_FinishCiaInstall(app_mediatype_to_byte(mediaType), &ciaHandle); Result res = AM_FinishCiaInstall(app_mediatype_to_byte(mediaType), &ciaHandle);
if(res != 0 && res != 0xC8A044DC) { // Happens when already installed, but seems to have succeeded anyway... if(res != 0 && (u32) res != 0xC8A044DC) { // Happens when already installed, but seems to have succeeded anyway...
return false; return false;
} }
return true; return true;
} }
bool app_delete(MediaType mediaType, App app) { bool app_delete(App app) {
if(!am_prepare()) { if(!am_prepare()) {
return false; return false;
} }
return AM_DeleteAppTitle(app_mediatype_to_byte(mediaType), app.titleId) == 0; return AM_DeleteAppTitle(app_mediatype_to_byte(app.mediaType), app.titleId) == 0;
} }
bool app_launch(MediaType mediaType, App app) { bool app_launch(App app) {
if(!ns_prepare()) { if(!ns_prepare()) {
return false; return false;
} }
return NS_RebootToTitle(mediaType, app.titleId) == 0; return NS_RebootToTitle(app.mediaType, app.titleId) == 0;
} }
u64 fs_get_free_space(MediaType mediaType) { u64 fs_get_free_space(MediaType mediaType) {
@ -720,15 +889,18 @@ void platform_delay(int ms) {
svcSleepThread(ms * 1000000); svcSleepThread(ms * 1000000);
} }
void platform_print(const char* str) {
svcOutputDebugString(str, strlen(str));
}
void platform_printf(const char* format, ...) { void platform_printf(const char* format, ...) {
va_list args; va_list args;
va_start(args, format); va_start(args, format);
char* str = vsdprintf(format, args); int len = vsnprintf(NULL, 0, format, args);
va_end(args); va_end(args);
platform_print(str);
free(str); char str[len + 1];
va_list args2;
va_start(args2, format);
vsnprintf(str, (size_t) len + 1, format, args2);
va_end(args2);
svcOutputDebugString(str, strlen(str));
} }

View File

@ -1,14 +1,14 @@
#ifndef __COMMON_H__ #ifndef __COMMON_H__
#define __COMMON_H__ #define __COMMON_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <vector>
#include <string>
#include <functional>
#define printf platform_printf #define printf platform_printf
typedef uint8_t u8; typedef uint8_t u8;
@ -21,13 +21,25 @@ typedef int16_t s16;
typedef int32_t s32; typedef int32_t s32;
typedef int64_t s64; typedef int64_t s64;
typedef struct _color { typedef struct {
u8 r; u8 r;
u8 g; u8 g;
u8 b; u8 b;
} Color; } Color;
typedef enum _button { typedef enum {
TOP_SCREEN,
BOTTOM_SCREEN
} Screen;
typedef enum {
SELECTED,
BACK,
APP_CLOSING,
MANUAL_BREAK
} SelectionResult;
typedef enum {
BUTTON_A, BUTTON_A,
BUTTON_B, BUTTON_B,
BUTTON_X, BUTTON_X,
@ -43,17 +55,17 @@ typedef enum _button {
BUTTON_TOUCH BUTTON_TOUCH
} Button; } Button;
typedef struct _touch { typedef struct {
int x; int x;
int y; int y;
} Touch; } Touch;
typedef enum _media_type { typedef enum {
NAND, NAND,
SD SD
} MediaType; } MediaType;
typedef enum _app_platform { typedef enum {
WII, WII,
DSI, DSI,
THREEDS, THREEDS,
@ -62,7 +74,7 @@ typedef enum _app_platform {
} AppPlatform; } AppPlatform;
// TODO: verify categories. // TODO: verify categories.
typedef enum _app_category { typedef enum {
APP, APP,
DLC, DLC,
PATCH, PATCH,
@ -70,7 +82,7 @@ typedef enum _app_category {
TWL TWL
} AppCategory; } AppCategory;
typedef struct _app { typedef struct {
u64 titleId; u64 titleId;
u32 uniqueId; u32 uniqueId;
char productCode[16]; char productCode[16];
@ -79,11 +91,7 @@ typedef struct _app {
AppCategory category; AppCategory category;
} App; } App;
char* sdprintf(const char* format, ...); bool screen_begin_draw(Screen screen);
char* vsdprintf(const char* format, va_list args);
bool screen_begin_draw();
bool screen_begin_draw_info();
bool screen_end_draw(); bool screen_end_draw();
void screen_swap_buffers_quick(); void screen_swap_buffers_quick();
void screen_swap_buffers(); void screen_swap_buffers();
@ -92,11 +100,13 @@ int screen_get_width();
int screen_get_height(); int screen_get_height();
void screen_draw(int x, int y, u8 r, u8 g, u8 b); void screen_draw(int x, int y, u8 r, u8 g, u8 b);
void screen_fill(int x, int y, int width, int height, u8 r, u8 g, u8 b); void screen_fill(int x, int y, int width, int height, u8 r, u8 g, u8 b);
int screen_get_str_width(const char* str); int screen_get_str_width(std::string str);
int screen_get_str_height(const char* str); int screen_get_str_height(std::string str);
void screen_draw_string(const char* string, int x, int y, u8 r, u8 g, u8 b); void screen_draw_string(std::string str, int x, int y, u8 r, u8 g, u8 b);
void screen_clear(u8 r, u8 g, u8 b); void screen_clear(u8 r, u8 g, u8 b);
void screen_clear_all();
bool ui_select_file(std::string rootDirectory, std::string extension, std::string* selectedFile, std::function<bool()> onLoop);
bool ui_select_app(MediaType mediaType, App* selectedApp, std::function<bool()> onLoop);
void input_poll(); void input_poll();
bool input_is_released(Button button); bool input_is_released(Button button);
@ -104,12 +114,12 @@ bool input_is_pressed(Button button);
bool input_is_held(Button button); bool input_is_held(Button button);
Touch input_get_touch(); Touch input_get_touch();
const char* app_get_platform_name(AppPlatform platform); std::string app_get_platform_name(AppPlatform platform);
const char* app_get_category_name(AppCategory category); std::string app_get_category_name(AppCategory category);
App* app_list(MediaType mediaType, u32* count); std::vector<App> app_list(MediaType mediaType);
bool app_install(MediaType mediaType, const char* path, bool (*onProgress)(int progress)); bool app_install(MediaType mediaType, std::string path, std::function<bool(int)> onProgress);
bool app_delete(MediaType mediaType, App app); bool app_delete(App app);
bool app_launch(MediaType mediaType, App app); bool app_launch(App app);
u64 fs_get_free_space(MediaType mediaType); u64 fs_get_free_space(MediaType mediaType);
@ -118,11 +128,6 @@ void platform_cleanup();
bool platform_is_running(); bool platform_is_running();
u64 platform_get_time(); u64 platform_get_time();
void platform_delay(int ms); void platform_delay(int ms);
void platform_print(const char* str);
void platform_printf(const char* format, ...); void platform_printf(const char* format, ...);
#ifdef __cplusplus
}
#endif
#endif #endif

View File

@ -1,7 +1,96 @@
#include <stdlib.h> #include "common.hpp"
#include "common.h" #include <sstream>
#include "ui.h" #include <iomanip>
typedef enum {
INSTALL,
DELETE
} Mode;
bool ui_display_install_progress(int progress) {
std::stringstream stream;
stream << "Installing: [";
int progressBars = progress / 4;
for(int i = 0; i < 25; i++) {
if(i < progressBars) {
stream << '|';
} else {
stream << ' ';
}
}
std::ios state(NULL);
state.copyfmt(stream);
stream << "] " << std::setfill('0') << std::setw(3) << progress;
stream.copyfmt(state);
stream << "%";
std::string msg = stream.str();
std::string cancel = "Press B to cancel.";
screen_begin_draw(TOP_SCREEN);
screen_clear(0, 0, 0);
screen_draw_string(msg, (screen_get_width() - screen_get_str_width(msg)) / 2, (screen_get_height() - screen_get_str_height(msg)) / 2 - screen_get_str_height(msg), 255, 255, 255);
screen_draw_string(cancel, (screen_get_width() - screen_get_str_width(cancel)) / 2, (screen_get_height() - screen_get_str_height(cancel)) / 2 + screen_get_str_height(cancel), 255, 255, 255);
screen_end_draw();
screen_swap_buffers_quick();;
input_poll();
return !input_is_pressed(BUTTON_B);
}
void ui_display_deleting() {
std::string msg = "Deleting title...";
screen_begin_draw(TOP_SCREEN);
screen_clear(0, 0, 0);
screen_draw_string(msg, (screen_get_width() - screen_get_str_width(msg)) / 2, (screen_get_height() - screen_get_str_height(msg)) / 2, 255, 255, 255);
screen_end_draw();
screen_swap_buffers();
}
void ui_display_result(bool install, bool state) {
std::string msg = install ? (state ? "Install succeeded! Press start." : "Install failed! Press start.") : (state ? "Delete succeeded! Press start." : "Delete failed! Press start.");
while(platform_is_running()) {
input_poll();
if(input_is_pressed(BUTTON_START)) {
break;
}
screen_begin_draw(TOP_SCREEN);
screen_clear(0, 0, 0);
screen_draw_string(msg, (screen_get_width() - screen_get_str_width(msg)) / 2, (screen_get_height() - screen_get_str_height(msg)) / 2, 255, 255, 255);
screen_end_draw();
screen_swap_buffers();
}
}
bool ui_prompt_operation(Mode mode, std::string name) {
std::stringstream stream;
stream << (mode == INSTALL ? "Install" : "Delete") << " the selected title?";
std::string msg = stream.str();
std::string prompt = "Press A to confirm, B to cancel.";
while(platform_is_running()) {
input_poll();
if(input_is_pressed(BUTTON_A)) {
return true;
}
if(input_is_pressed(BUTTON_B)) {
return false;
}
screen_begin_draw(TOP_SCREEN);
screen_clear(0, 0, 0);
screen_draw_string(msg, (screen_get_width() - screen_get_str_width(msg)) / 2, (screen_get_height() - screen_get_str_height(msg)) / 2 - screen_get_str_height(msg), 255, 255, 255);
screen_draw_string(name, (screen_get_width() - screen_get_str_width(name)) / 2, (screen_get_height() - screen_get_str_height(name)) / 2, 255, 255, 255);
screen_draw_string(prompt, (screen_get_width() - screen_get_str_width(prompt)) / 2, (screen_get_height() - screen_get_str_height(prompt)) / 2 + screen_get_str_height(prompt), 255, 255, 255);
screen_end_draw();
screen_swap_buffers();
}
return false;
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
if(!platform_init()) { if(!platform_init()) {
@ -10,41 +99,68 @@ int main(int argc, char **argv) {
MediaType destination = SD; MediaType destination = SD;
Mode mode = INSTALL; Mode mode = INSTALL;
u64 freeSpace = fs_get_free_space(destination);
auto onLoop = [&]() {
bool breakLoop = false;
if(input_is_pressed(BUTTON_L)) {
if(destination == SD) {
destination = NAND;
} else {
destination = SD;
}
freeSpace = fs_get_free_space(destination);
if(mode == DELETE) {
breakLoop = true;
}
}
if(input_is_pressed(BUTTON_R)) {
if(mode == INSTALL) {
mode = DELETE;
} else {
mode = INSTALL;
}
breakLoop = true;
}
std::stringstream stream;
stream << "Free Space: " << freeSpace << " bytes (" << std::fixed << std::setprecision(2) << freeSpace / 1024.0f / 1024.0f << "MB)";
std::string space = stream.str();
std::string status = std::string("Destination: ") + (destination == NAND ? "NAND" : "SD") + ", Mode: " + (mode == INSTALL ? "Install" : "Delete");
std::string msg = "L - Switch Destination, R - Switch Mode";
screen_draw_string(space, (screen_get_width() - screen_get_str_width(space)) / 2, screen_get_height() - 4 - screen_get_str_height(msg) - screen_get_str_height(status) - screen_get_str_height(space), 255, 255, 255);
screen_draw_string(status, (screen_get_width() - screen_get_str_width(status)) / 2, screen_get_height() - 4 - screen_get_str_height(msg) - screen_get_str_height(status), 255, 255, 255);
screen_draw_string(msg, (screen_get_width() - screen_get_str_width(msg)) / 2, screen_get_height() - 4 - screen_get_str_height(msg), 255, 255, 255);
return breakLoop;
};
while(platform_is_running()) { while(platform_is_running()) {
char* targetInstall = NULL; std::string targetInstall;
App targetDelete; App targetDelete;
UIResult result; bool obtained = false;
if(mode == INSTALL) { if(mode == INSTALL) {
result = uiSelectFile(&targetInstall, "sdmc:", "cia", &destination, &mode); obtained = ui_select_file("sdmc:", "cia", &targetInstall, onLoop);
} else { } else if(mode == DELETE) {
result = uiSelectTitle(&targetDelete, &destination, &mode); obtained = ui_select_app(destination, &targetDelete, onLoop);
} }
if(result == EXIT_APP) { if(obtained) {
break; if(mode == INSTALL) {
} else if(result == SWITCH_MODE) { if(ui_prompt_operation(mode, targetInstall)) {
if(mode == INSTALL) { ui_display_result(true, app_install(destination, targetInstall, &ui_display_install_progress));
mode = DELETE; }
} else { } else if(mode == DELETE) {
mode = INSTALL; if(ui_prompt_operation(mode, targetDelete.productCode)) {
} ui_display_deleting();
} else { ui_display_result(false, app_delete(targetDelete));
if(mode == INSTALL) { }
if(uiPromptOperation(mode)) { }
uiDisplayResult(true, app_install(destination, targetInstall, &uiDisplayInstallProgress)); }
}
free(targetInstall);
} else {
char* str = sdprintf("%08lx - %s, %s, %s", targetDelete.uniqueId, targetDelete.productCode, app_get_platform_name(targetDelete.platform), app_get_category_name(targetDelete.category));
if(uiPromptOperation(mode)) {
uiDisplayDeleting();
uiDisplayResult(false, app_delete(destination, targetDelete));
}
free(str);
}
}
} }
platform_cleanup(); platform_cleanup();

View File

@ -1,409 +0,0 @@
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <sys/dirent.h>
#include <vector>
#include <algorithm>
#include "ui.h"
#include "common.h"
struct ui_alphabetize {
inline bool operator() (char* a, char* b) {
return strcasecmp(a, b) < 0;
}
};
bool uiIsDirectory(char* path) {
DIR *dir = opendir(path);
if(!dir) {
return false;
}
closedir(dir);
return true;
}
std::vector<char*>* uiGetDirectoryContents(const char* directory, const char* extensionFilter) {
std::vector<char*>* contents = new std::vector<char*>();
char slash[strlen(directory) + 2];
snprintf(slash, sizeof(slash), "%s/", directory);
DIR *dir = opendir(slash);
if(dir != NULL) {
while(true) {
struct dirent *ent = readdir(dir);
if(ent == NULL) {
break;
}
char path[strlen(directory) + strlen(ent->d_name) + 2];
snprintf(path, strlen(directory) + strlen(ent->d_name) + 2, "%s/%s", directory, ent->d_name);
if(uiIsDirectory(path)) {
contents->push_back(strdup(ent->d_name));
} else {
const char *dot = strrchr(path, '.');
if(dot && dot != path && strcmp(dot + 1, extensionFilter) == 0) {
contents->push_back(strdup(ent->d_name));
}
}
}
closedir(dir);
contents->push_back(strdup("."));
contents->push_back(strdup(".."));
std::sort(contents->begin(), contents->end(), ui_alphabetize());
} else {
return NULL;
}
return contents;
}
std::vector<char*>* uiTitlesToVector(App* apps, u32 count) {
std::vector<char*>* contents = new std::vector<char*>();
if(count == 0) {
contents->push_back(strdup("None"));
} else {
for(u32 title = 0; title < count; title++) {
contents->push_back(sdprintf("%08lx - %s, %s, %s", apps[title].uniqueId, apps[title].productCode, app_get_platform_name(apps[title].platform), app_get_category_name(apps[title].category)));
}
}
std::sort(contents->begin(), contents->end(), ui_alphabetize());
return contents;
}
void uiFreeVectorContents(std::vector<char*>* contents) {
for(std::vector<char*>::iterator it = contents->begin(); it != contents->end(); it++) {
free((char*) *it);
}
}
UIResult uiDisplaySelector(char** selected, std::vector<char*>* contents, const char* currDir, MediaType destination, Mode mode) {
const char* selectCia = mode == INSTALL ? "Select a CIA to install." : "Select a CIA to delete.";
const char* pressL = "Press L to switch destinations.";
const char* pressR = "Press R to switch between installing and deleting.";
const char* destString = destination == NAND ? "Destination: NAND" : "Destination: SD";
const char* modeString = mode == INSTALL ? "Mode: Install" : "Mode: Delete";
char* freeSpace = sdprintf("Free space: %llu bytes", fs_get_free_space(destination));
char* requiredSpace = NULL;
unsigned int cursor = 0;
unsigned int scroll = 0;
int horizScroll = 0;
u64 horizEndTime = 0;
UIResult result = SUCCESS;
while(platform_is_running()) {
input_poll();
if(input_is_pressed(BUTTON_A)) {
*selected = contents->at(cursor);
result = SUCCESS;
break;
}
if(input_is_pressed(BUTTON_B)) {
result = BACK;
break;
}
if(input_is_pressed(BUTTON_L)) {
result = SWITCH_DEST;
break;
}
if(input_is_pressed(BUTTON_R)) {
result = SWITCH_MODE;
break;
}
bool cursorChanged = false;
if(input_is_pressed(BUTTON_DOWN) && cursor < contents->size() - 1) {
cursor++;
int diff = cursor - scroll;
if(diff >= 20) {
scroll++;
}
horizScroll = 0;
horizEndTime = 0;
cursorChanged = true;
}
if(input_is_pressed(BUTTON_UP) && cursor > 0) {
cursor--;
int diff = cursor - scroll;
if(diff < 0) {
scroll--;
}
horizScroll = 0;
horizEndTime = 0;
cursorChanged = true;
}
if(cursorChanged && currDir != NULL) {
char* currSelection = contents->at(cursor);
char* path = sdprintf("%s/%s", currDir, currSelection);
if(strcmp(currSelection, ".") != 0 && strcmp(currSelection, "..") != 0 && !uiIsDirectory(path)) {
struct stat st;
stat(path, &st);
requiredSpace = sdprintf("Required space: %lu bytes", st.st_size);
} else if(requiredSpace != NULL) {
free(requiredSpace);
requiredSpace = NULL;
}
free(path);
}
screen_begin_draw();
screen_clear(0, 0, 0);
int screenWidth = screen_get_width();
int i = 0;
for(std::vector<char*>::iterator it = contents->begin() + scroll; it != contents->end(); it++) {
u8 color = 255;
int offset = 0;
if(i + scroll == cursor) {
screen_fill(0, i * 12, screenWidth, 8, 255, 255, 255);
color = 0;
int width = strlen(*it) * 8;
if(width > screenWidth) {
if(-horizScroll + screenWidth >= width) {
if(horizEndTime == 0) {
horizEndTime = platform_get_time();
} else if(platform_get_time() - horizEndTime >= 4000) {
horizScroll = 0;
horizEndTime = 0;
}
} else {
horizScroll -= 1;
}
}
offset = horizScroll;
}
screen_draw_string(*it, offset, i * 12, color, color, color);
i++;
if(i >= 20) {
break;
}
}
screen_end_draw();
screen_begin_draw_info();
screen_clear(0, 0, 0);
screen_draw_string(selectCia, (screen_get_width() - screen_get_str_width(selectCia)) / 2, (screen_get_height() - screen_get_str_height(pressL)) / 2 - screen_get_str_height(selectCia), 255, 255, 255);
screen_draw_string(pressL, (screen_get_width() - screen_get_str_width(pressL)) / 2, (screen_get_height() - screen_get_str_height(pressL)) / 2, 255, 255, 255);
screen_draw_string(pressR, (screen_get_width() - screen_get_str_width(pressR)) / 2, (screen_get_height() - screen_get_str_height(pressL)) / 2 + screen_get_str_height(pressL), 255, 255, 255);
screen_draw_string(freeSpace, (screen_get_width() - screen_get_str_width(freeSpace)) / 2, (screen_get_height() - screen_get_str_height(pressL)) / 2 + screen_get_str_height(pressL) * 4, 255, 255, 255);
if(requiredSpace != NULL) {
screen_draw_string(requiredSpace, (screen_get_width() - screen_get_str_width(requiredSpace)) / 2, (screen_get_height() - screen_get_str_height(pressL)) / 2 + screen_get_str_height(pressL) * 5, 255, 255, 255);
}
screen_draw_string(destString, 0, screen_get_height() - screen_get_str_height(destString), 255, 255, 255);
screen_draw_string(modeString, screen_get_width() - screen_get_str_width(modeString), screen_get_height() - screen_get_str_height(modeString), 255, 255, 255);
screen_end_draw();
screen_swap_buffers();
}
free(freeSpace);
if(requiredSpace != NULL) {
free(requiredSpace);
}
if(!platform_is_running()) {
result = EXIT_APP;
}
return result;
}
UIResult uiSelectFile(char** selected, const char* directory, const char* extension, MediaType* destination, Mode* mode) {
std::vector<char*>* contents = uiGetDirectoryContents(directory, extension);
UIResult result;
while(true) {
char* selectedEntry = NULL;
UIResult res = uiDisplaySelector(&selectedEntry, contents, directory, *destination, *mode);
if(res == SWITCH_DEST) {
if(*destination == NAND) {
*destination = SD;
} else {
*destination = NAND;
}
continue;
} else if(res == BACK || (selectedEntry != NULL && strcmp(selectedEntry, "..") == 0)) {
if(strcmp(directory, "sdmc:") != 0) {
result = BACK;
break;
} else {
continue;
}
} else if(res != SUCCESS) {
result = res;
break;
}
if(strcmp(selectedEntry, ".") == 0) {
continue;
}
char* path = sdprintf("%s/%s", directory, selectedEntry);
if(uiIsDirectory(path)) {
char *select;
UIResult dirRes = uiSelectFile(&select, path, extension, destination, mode);
free(path);
if(dirRes == BACK) {
continue;
}
result = dirRes;
*selected = select;
break;
} else {
result = SUCCESS;
*selected = path;
break;
}
}
uiFreeVectorContents(contents);
delete(contents);
return result;
}
UIResult uiSelectTitle(App* selected, MediaType* destination, Mode* mode) {
u32 appCount;
App* apps = app_list(*destination, &appCount);
std::vector<char*>* contents = uiTitlesToVector(apps, appCount);
UIResult result;
while(true) {
char* selectedEntry = NULL;
UIResult res = uiDisplaySelector(&selectedEntry, contents, NULL, *destination, *mode);
if(selectedEntry != NULL && strcmp(selectedEntry, "None") == 0) {
continue;
}
if(res == BACK) {
continue;
} else if(res == SWITCH_DEST) {
if(*destination == NAND) {
*destination = SD;
} else {
*destination = NAND;
}
uiFreeVectorContents(contents);
delete(contents);
free(apps);
apps = app_list(*destination, &appCount);
contents = uiTitlesToVector(apps, appCount);
continue;
} else if(res != SUCCESS) {
result = res;
break;
}
for(u32 i = 0; i < appCount; i++) {
char* data = sdprintf("%08lx - %s, %s, %s", apps[i].uniqueId, apps[i].productCode, app_get_platform_name(apps[i].platform), app_get_category_name(apps[i].category));
if(strcmp(selectedEntry, data) == 0) {
*selected = apps[i];
free(data);
break;
}
free(data);
}
if(selected == NULL) {
continue;
}
result = SUCCESS;
break;
}
uiFreeVectorContents(contents);
delete(contents);
free(apps);
return result;
}
bool uiDisplayInstallProgress(int progress) {
char* msg = sdprintf("Installing: [ ] %03d%%", progress);
const char* cancel = "Press B to cancel.";
for(int pos = 13; pos < 13 + (progress / 4); pos++) {
msg[pos] = '|';
}
screen_begin_draw_info();
screen_clear(0, 0, 0);
screen_draw_string(msg, (screen_get_width() - screen_get_str_width(msg)) / 2, (screen_get_height() - screen_get_str_height(msg)) / 2 - screen_get_str_height(msg), 255, 255, 255);
screen_draw_string(cancel, (screen_get_width() - screen_get_str_width(cancel)) / 2, (screen_get_height() - screen_get_str_height(cancel)) / 2 + screen_get_str_height(cancel), 255, 255, 255);
screen_end_draw();
screen_swap_buffers_quick();
free(msg);
input_poll();
return !input_is_pressed(BUTTON_B);
}
void uiDisplayDeleting() {
const char* msg = "Deleting title...";
screen_begin_draw_info();
screen_clear(0, 0, 0);
screen_draw_string(msg, (screen_get_width() - screen_get_str_width(msg)) / 2, (screen_get_height() - screen_get_str_height(msg)) / 2, 255, 255, 255);
screen_end_draw();
screen_swap_buffers();
}
void uiDisplayResult(bool install, bool state) {
const char* msg = install ? (state ? "Install succeeded! Press start." : "Install failed! Press start.") : (state ? "Delete succeeded! Press start." : "Delete failed! Press start.");
while(platform_is_running()) {
input_poll();
if(input_is_pressed(BUTTON_START)) {
break;
}
screen_begin_draw_info();
screen_clear(0, 0, 0);
screen_draw_string(msg, (screen_get_width() - screen_get_str_width(msg)) / 2, (screen_get_height() - screen_get_str_height(msg)) / 2, 255, 255, 255);
screen_end_draw();
screen_swap_buffers();
}
}
bool uiPromptOperation(Mode mode) {
char* msg = sdprintf("%s the selected title?", mode == INSTALL ? "Install" : "Delete");
const char* prompt = "Press A to confirm, B to cancel.";
while(platform_is_running()) {
input_poll();
if(input_is_pressed(BUTTON_A)) {
free(msg);
return true;
}
if(input_is_pressed(BUTTON_B)) {
free(msg);
return false;
}
screen_begin_draw_info();
screen_clear(0, 0, 0);
screen_draw_string(msg, (screen_get_width() - screen_get_str_width(msg)) / 2, (screen_get_height() - screen_get_str_height(msg)) / 2 - screen_get_str_height(msg), 255, 255, 255);
screen_draw_string(prompt, (screen_get_width() - screen_get_str_width(prompt)) / 2, (screen_get_height() - screen_get_str_height(prompt)) / 2 + screen_get_str_height(prompt), 255, 255, 255);
screen_end_draw();
screen_swap_buffers();
}
free(msg);
return false;
}

View File

@ -1,26 +0,0 @@
#ifndef __UI_H__
#define __UI_H__
#include "common.h"
typedef enum _ui_result {
SUCCESS,
BACK,
SWITCH_DEST,
SWITCH_MODE,
EXIT_APP
} UIResult;
typedef enum _mode {
INSTALL,
DELETE
} Mode;
UIResult uiSelectFile(char** selected, const char* directory, const char* extension, MediaType* destination, Mode* mode);
UIResult uiSelectTitle(App* selected, MediaType* destination, Mode* mode);
bool uiDisplayInstallProgress(int progress);
void uiDisplayDeleting();
void uiDisplayResult(bool install, bool state);
bool uiPromptOperation(Mode mode);
#endif

Binary file not shown.

Binary file not shown.