diff --git a/Makefile b/Makefile index e63663a..a2aac68 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ include $(DEVKITARM)/3ds_rules # Your values. APP_TITLE := WifiManager APP_DESCRIPTION := Backup and restore your WiFi slots! -APP_AUTHOR := LiquidFenrir +APP_AUTHOR := LiquidFenrir and Zakary2841 TARGET := $(subst $e ,_,$(notdir $(APP_TITLE))) @@ -87,7 +87,7 @@ ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft CFLAGS := -g -Wall -Wextra -O2 -mword-relocations \ -ffunction-sections \ - $(ARCH) + $(ARCH) -Wno-cpp CFLAGS += $(INCLUDE) -DARM11 -D_3DS -D_GNU_SOURCE -DTITLE="\"$(APP_TITLE)\"" diff --git a/source/blocks.cpp b/source/blocks.cpp index 775187d..6b3541e 100644 --- a/source/blocks.cpp +++ b/source/blocks.cpp @@ -10,6 +10,7 @@ extern "C" { #include "keyboard.h" } #include "drawing.h" +bool confirm_dialog(const char* message); wifi_slot::wifi_slot() { @@ -83,7 +84,19 @@ Result wifi_slot::read_slot(void) { this->password = "(not available)"; } - } + if(this->id == -1) + { + size_t lastSlash = this->path.find_last_of("/\\"); + if (lastSlash != std::string::npos) { + std::string fullFilename = this->path.substr(lastSlash + 1); + if (fullFilename.length() > 4 && fullFilename.substr(fullFilename.length() - 4) == ".bin") { + this->filename = fullFilename.substr(0, fullFilename.length() - 4); + } else { + this->filename = fullFilename; + } + } + } +} return ret; } @@ -164,7 +177,44 @@ void wifi_slot::draw_info(bool to_the_right, bool hide_password) if(hide_password) pp2d_draw_text(text_x, text_y, text_scale_x, text_scale_y, COLOR_BLACK, "Password:\n(hidden)"); else + { pp2d_draw_textf(text_x, text_y, text_scale_x, text_scale_y, COLOR_BLACK, "Password:\n%s", this->password.c_str()); + } + + text_y += 40; + if(this->slot_data.enable_autoDNS) + { + pp2d_draw_text(text_x, text_y, text_scale_x, text_scale_y, COLOR_BLACK, "DNS:\nAuto"); + } + else +{ + char primary_ip[16]; + char secondary_ip[16]; + + snprintf(primary_ip, 16, "%u.%u.%u.%u", + this->slot_data.dns_primary[0], + this->slot_data.dns_primary[1], + this->slot_data.dns_primary[2], + this->slot_data.dns_primary[3]); + + snprintf(secondary_ip, 16, "%u.%u.%u.%u", + this->slot_data.dns_secondary[0], + this->slot_data.dns_secondary[1], + this->slot_data.dns_secondary[2], + this->slot_data.dns_secondary[3]); + + pp2d_draw_textf( + text_x, + text_y, + text_scale_x, + text_scale_y, + COLOR_BLACK, + "DNS:\nPri: %s\nSec: %s", + primary_ip, + secondary_ip + ); +} + } slots_list::slots_list() @@ -239,6 +289,7 @@ void slots_list::draw_top(void) } void slots_list::draw_list(void) { + pp2d_set_screen_color(GFX_BOTTOM, COLOR_WHITE); pp2d_draw_on(GFX_BOTTOM, GFX_LEFT); int x_offset = 15; int y_offset = 81; @@ -283,10 +334,26 @@ void slots_list::draw_list(void) current_slot = this->backups[i-1]; } - if(i == this->selected_backup) - pp2d_draw_rectangle(x_offset+2, text_y-2, bg_width-4, pp2d_get_text_height(current_slot.name.c_str(), 0.6, 0.6)+2, COLOR_CURSOR); - pp2d_draw_text(text_x, text_y, 0.6, 0.6, COLOR_BLACK, current_slot.name.c_str()); + std::string label = (!current_slot.filename.empty()) ? current_slot.filename : current_slot.name; + + float scale = 0.6; + float max_width = bg_width - 10; + + while (pp2d_get_text_width(label.c_str(), scale, scale) > max_width && label.length() > 4) { + label = label.substr(0, label.length() - 1); + } + if (label != current_slot.name && label != current_slot.filename) { + label = label.substr(0, label.length() - 2) + "..."; + } + if(i == this->selected_backup) + pp2d_draw_rectangle(x_offset+2, text_y-2, bg_width-4, pp2d_get_text_height(label.c_str(), 0.6, 0.6)+2, COLOR_CURSOR); + pp2d_draw_text(text_x, text_y, 0.6, 0.6, COLOR_BLACK, label.c_str()); } + float scale = 0.5f; + float text_width = pp2d_get_text_width("Press START to exit", scale, scale); + float x_pos = (320.0f / 2.0f) - (text_width / 2.0f); + pp2d_draw_text(x_pos, 225, scale, scale, COLOR_BLACK, "Press START to exit"); + } void slots_list::draw_interface(void) { @@ -295,6 +362,11 @@ void slots_list::draw_interface(void) this->backups[this->selected_backup-1].draw_info(true, this->passwords_hidden); this->draw_top(); this->draw_list(); + pp2d_draw_on(GFX_TOP, GFX_LEFT); + float scale = 0.5f; + float text_width = pp2d_get_text_width("Hold SELECT for instructions.", scale, scale); + float x_pos = (400.0f / 2.0f) - (text_width / 2.0f); + pp2d_draw_text(x_pos, 225, scale, scale, COLOR_BLACK, "Hold SELECT for instructions."); } void slots_list::next_slot_right(void) @@ -332,8 +404,17 @@ void slots_list::select_slot(int id) void slots_list::write_to(int id) { - if(this->selected_backup != 0) + if(this->selected_backup == 0) return; + { + std::string targetSSID = this->slots[id].name; + std::string sourceProfile = this->backups[this->selected_backup-1].filename; + if (sourceProfile.empty()) sourceProfile = this->backups[this->selected_backup-1].name; + char msg[256]; + snprintf(msg, sizeof(msg), "Overwrite Slot %i?\n%s\nwith %s?", + id+1, targetSSID.c_str(), sourceProfile.c_str()); + if(!confirm_dialog(msg)) return; this->slots[id].copy_slot(this->backups[this->selected_backup-1]); + } } void slots_list::save_from(int id) { @@ -363,7 +444,14 @@ void slots_list::save_from(int id) free(file_name); } else + { + std::string targetProfile = this->backups[this->selected_backup-1].filename; + if (targetProfile.empty()) targetProfile = this->backups[this->selected_backup-1].name; + char msg[256]; + snprintf(msg, sizeof(msg), "Overwrite backup:\n%s?", targetProfile.c_str()); + if(!confirm_dialog(msg)) return; this->backups[this->selected_backup-1].copy_slot(this->slots[id]); + } } } @@ -378,11 +466,14 @@ void slots_list::save_from_selected(void) void slots_list::delete_selected_backup(void) { - if(this->selected_backup != 0) - { - this->backups[this->selected_backup-1].delete_slot(); - this->backups.erase(this->backups.begin()+(--this->selected_backup)); - } + if(this->selected_backup == 0) return; + wifi_slot slot = this->backups[this->selected_backup - 1]; + std::string target = slot.filename.empty() ? slot.name : slot.filename; + char msg[128]; + snprintf(msg, sizeof(msg), "Delete \"%s\" backup?", target.c_str()); + if(!confirm_dialog(msg)) return; + this->backups[this->selected_backup - 1].delete_slot(); + this->backups.erase(this->backups.begin() + (--this->selected_backup)); } void slots_list::toggle_password_visibility(void) diff --git a/source/blocks.hpp b/source/blocks.hpp index d28a3af..551d795 100644 --- a/source/blocks.hpp +++ b/source/blocks.hpp @@ -94,6 +94,7 @@ class wifi_slot bool exists; std::string name; + std::string filename; std::string password; wifi_encryption_type encryption; diff --git a/source/main.cpp b/source/main.cpp index 64e5480..7c0f97b 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -3,10 +3,15 @@ #include "drawing.h" +#include +#include +#include + FS_Archive sdmcArchive; touchPosition touch; slots_list list; +const u32 COLOR_GRAY = 0xFF888888; void draw_instructions(void) { @@ -25,6 +30,63 @@ void draw_instructions(void) pp2d_draw_text_center(GFX_TOP, y_offset, 0.6, 0.6, COLOR_BLACK, "Press \uE002 to delete the highlighted backup."); y_offset += 20; pp2d_draw_text_center(GFX_TOP, y_offset, 0.6, 0.6, COLOR_BLACK, "Press \uE003 to toggle the passwords visibility."); + y_offset += 20; + pp2d_draw_text_center(GFX_TOP, y_offset, 0.6, 0.6, COLOR_BLACK, "Press START to exit."); +} + +bool confirm_dialog(const char* message) +{ + std::vector lines; + std::string current_line; + std::stringstream ss(message); + std::string word; + float max_width = 260.0f; + float scale = 0.5f; + while(ss >> word) + { + std::string test_line = current_line.empty() ? word : current_line + " " + word; + float test_width = pp2d_get_text_width(test_line.c_str(), scale, scale); + + if(test_width > max_width) + { + if(current_line.empty()) lines.push_back(word); + else { + lines.push_back(current_line); + current_line = word; + } + } + else + { + current_line = test_line; + } + } + if(!current_line.empty()) lines.push_back(current_line); + + while(aptMainLoop()) + { + hidScanInput(); + u32 kDown = hidKeysDown(); + + pp2d_begin_draw(GFX_BOTTOM, GFX_LEFT); + pp2d_set_screen_color(GFX_BOTTOM, COLOR_WHITE); + pp2d_draw_rectangle(20, 80, 280, 80, COLOR_GRAY); + + int current_y = 85; + for(auto &line : lines) + { + float text_width = pp2d_get_text_width(line.c_str(), scale, scale); + pp2d_draw_text(160 - (text_width / 2), current_y, 0.5, 0.5, COLOR_WHITE, line.c_str()); + current_y += 12; + } + + pp2d_draw_text(40, 130, 0.5, 0.5, COLOR_WHITE, "A : Yes B : No"); + + pp2d_end_draw(); + + if(kDown & KEY_A) return true; + if(kDown & KEY_B) return false; + } + return false; } int main(int argc, char ** argv) @@ -33,7 +95,7 @@ int main(int argc, char ** argv) romfsInit(); pp2d_init(); consoleDebugInit(debugDevice_SVC); - + aptSetHomeAllowed(false); pp2d_set_screen_color(GFX_TOP, COLOR_BACKGROUND); pp2d_set_screen_color(GFX_BOTTOM, COLOR_BACKGROUND); pp2d_load_texture_png(TEXTURE_SAVE, "romfs:/save.png"); @@ -56,6 +118,7 @@ int main(int argc, char ** argv) u32 kHeld = hidKeysHeld(); pp2d_begin_draw(GFX_TOP, GFX_LEFT); + pp2d_set_screen_color(GFX_TOP, COLOR_WHITE); if(kHeld & KEY_SELECT) draw_instructions(); else diff --git a/source/pp2d/pp2d.c b/source/pp2d/pp2d.c index 85acf06..a505882 100644 --- a/source/pp2d/pp2d.c +++ b/source/pp2d/pp2d.c @@ -46,6 +46,7 @@ static int textVtxArrayPos; static C3D_RenderTarget* topLeft; static C3D_RenderTarget* topRight; static C3D_RenderTarget* bot; +static CFNT_s* g_font; static struct { GPU_TEXTURE_FILTER_PARAM magFilter; @@ -114,7 +115,8 @@ void pp2d_draw_rectangle(int x, int y, int width, int height, u32 color) { C3D_TexEnv* env = C3D_GetTexEnv(0); C3D_TexEnvSrc(env, C3D_Both, GPU_CONSTANT, GPU_CONSTANT, 0); - C3D_TexEnvOp(env, C3D_Both, 0, 0, 0); + C3D_TexEnvOpRgb(env, 0, 0, 0); + C3D_TexEnvOpAlpha(env, 0, 0, 0); C3D_TexEnvFunc(env, C3D_RGB, GPU_INTERPOLATE); C3D_TexEnvColor(env, color); @@ -166,17 +168,17 @@ void pp2d_draw_text_wrap(float x, float y, float scaleX, float scaleY, u32 color if (units == -1) break; p += units; - if (code == '\n' || (wrapX != -1 && x + scaleX * fontGetCharWidthInfo(fontGlyphIndexFromCodePoint(code))->charWidth >= firstX + wrapX)) + if (code == '\n' || (wrapX != -1 && x + scaleX * fontGetCharWidthInfo(g_font,fontGlyphIndexFromCodePoint(g_font,code))->charWidth >= firstX + wrapX)) { x = firstX; - y += scaleY*fontGetInfo()->lineFeed; + y += scaleY*fontGetInfo(g_font)->lineFeed; p -= code == '\n' ? 0 : 1; } else if (code > 0) { - int glyphIdx = fontGlyphIndexFromCodePoint(code); + int glyphIdx = fontGlyphIndexFromCodePoint(g_font,code); fontGlyphPos_s data; - fontCalcGlyphPos(&data, glyphIdx, GLYPH_POS_CALC_VTXCOORD, scaleX, scaleY); + fontCalcGlyphPos(&data, g_font, glyphIdx, GLYPH_POS_CALC_VTXCOORD, scaleX, scaleY); if (data.sheetIndex != lastSheet) { @@ -327,20 +329,18 @@ void pp2d_free_texture(size_t id) Result pp2d_init(void) { Result res = 0; + g_font = fontGetSystemFont(); gfxInitDefault(); C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); topLeft = C3D_RenderTargetCreate(SCREEN_HEIGHT, TOP_WIDTH, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); - C3D_RenderTargetSetClear(topLeft, C3D_CLEAR_ALL, BACKGROUND_COLOR, 0); C3D_RenderTargetSetOutput(topLeft, GFX_TOP, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); topRight = C3D_RenderTargetCreate(SCREEN_HEIGHT, TOP_WIDTH, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); - C3D_RenderTargetSetClear(topRight, C3D_CLEAR_ALL, BACKGROUND_COLOR, 0); C3D_RenderTargetSetOutput(topRight, GFX_TOP, GFX_RIGHT, DISPLAY_TRANSFER_FLAGS); bot = C3D_RenderTargetCreate(SCREEN_HEIGHT, BOTTOM_WIDTH, GPU_RB_RGBA8, GPU_RB_DEPTH24_STENCIL8); - C3D_RenderTargetSetClear(bot, C3D_CLEAR_ALL, BACKGROUND_COLOR, 0); C3D_RenderTargetSetOutput(bot, GFX_BOTTOM, GFX_LEFT, DISPLAY_TRANSFER_FLAGS); res = fontEnsureMapped(); @@ -373,12 +373,12 @@ Result pp2d_init(void) C3D_DepthTest(true, GPU_GEQUAL, GPU_WRITE_ALL); int i; - TGLP_s* glyphInfo = fontGetGlyphInfo(); + TGLP_s* glyphInfo = fontGetGlyphInfo(g_font); glyphSheets = malloc(sizeof(C3D_Tex)*glyphInfo->nSheets); for (i = 0; i < glyphInfo->nSheets; i ++) { C3D_Tex* tex = &glyphSheets[i]; - tex->data = fontGetGlyphSheetTex(i); + tex->data = fontGetGlyphSheetTex(g_font,i); tex->fmt = glyphInfo->sheetFmt; tex->size = glyphInfo->sheetSize; tex->width = glyphInfo->sheetWidth; @@ -447,10 +447,10 @@ static void pp2d_get_text_size_internal(float* width, float* height, float scale if (units == -1) break; p += units; - if (code == '\n' || (wrapX != -1 && x + scaleX * fontGetCharWidthInfo(fontGlyphIndexFromCodePoint(code))->charWidth >= firstX + wrapX)) + if (code == '\n' || (wrapX != -1 && x + scaleX * fontGetCharWidthInfo(g_font,fontGlyphIndexFromCodePoint(g_font,code))->charWidth >= firstX + wrapX)) { x = firstX; - h += scaleY*fontGetInfo()->lineFeed; + h += scaleY*fontGetInfo(g_font)->lineFeed; p -= code == '\n' ? 0 : 1; if (w > maxW) maxW = w; @@ -458,7 +458,7 @@ static void pp2d_get_text_size_internal(float* width, float* height, float scale } else if (code > 0) { - float len = (scaleX * fontGetCharWidthInfo(fontGlyphIndexFromCodePoint(code))->charWidth); + float len = (scaleX * fontGetCharWidthInfo(g_font,fontGlyphIndexFromCodePoint(g_font,code))->charWidth); w += len; x += len; } @@ -471,7 +471,7 @@ static void pp2d_get_text_size_internal(float* width, float* height, float scale if (height) { - h += scaleY*fontGetInfo()->lineFeed; + h += scaleY*fontGetInfo(g_font)->lineFeed; *height = h; } } @@ -622,15 +622,17 @@ void pp2d_set_screen_color(gfxScreen_t target, u32 color) { if (target == GFX_TOP) { - C3D_RenderTargetSetClear(topLeft, C3D_CLEAR_ALL, color, 0); - C3D_RenderTargetSetClear(topRight, C3D_CLEAR_ALL, color, 0); + C3D_RenderTargetClear(topLeft, C3D_CLEAR_ALL, color, 0); + C3D_RenderTargetClear(topRight, C3D_CLEAR_ALL, color, 0); } else { - C3D_RenderTargetSetClear(bot, C3D_CLEAR_ALL, color, 0); + C3D_RenderTargetClear(bot, C3D_CLEAR_ALL, color, 0); } } + + void pp2d_set_texture_filter(GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter) { textureFilters.magFilter = magFilter; @@ -642,7 +644,8 @@ static void pp2d_set_text_color(u32 color) C3D_TexEnv* env = C3D_GetTexEnv(0); C3D_TexEnvSrc(env, C3D_RGB, GPU_CONSTANT, 0, 0); C3D_TexEnvSrc(env, C3D_Alpha, GPU_TEXTURE0, GPU_CONSTANT, 0); - C3D_TexEnvOp(env, C3D_Both, 0, 0, 0); + C3D_TexEnvOpRgb(env, 0, 0, 0); + C3D_TexEnvOpAlpha(env, 0, 0, 0); C3D_TexEnvFunc(env, C3D_RGB, GPU_REPLACE); C3D_TexEnvFunc(env, C3D_Alpha, GPU_MODULATE); C3D_TexEnvColor(env, color); @@ -791,7 +794,8 @@ void pp2d_texture_draw(void) C3D_TexBind(0, &textures[id].tex); C3D_TexEnv* env = C3D_GetTexEnv(0); C3D_TexEnvSrc(env, C3D_Both, GPU_TEXTURE0, GPU_CONSTANT, 0); - C3D_TexEnvOp(env, C3D_Both, 0, 0, 0); + C3D_TexEnvOpRgb(env, 0, 0, 0); + C3D_TexEnvOpAlpha(env, 0, 0, 0); C3D_TexEnvFunc(env, C3D_Both, GPU_MODULATE); C3D_TexEnvColor(env, textureData.color);