- Consolidate development files into final implementation - Replace basic prototypes with full feature implementations - Add complete hash-based save data access system - Implement advanced map pin management algorithms - Add comprehensive error handling and user feedback - Complete Korok seed database with all 908 locations
272 lines
No EOL
8 KiB
C
272 lines
No EOL
8 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <switch.h>
|
|
#include "ui.h"
|
|
#include "save.h"
|
|
#include "map.h"
|
|
|
|
#define FB_MAX_COLS 80
|
|
#define FB_MAX_LINES 45
|
|
#define MSG_MAX_LINE_LEN 45
|
|
#define MSG_START_COL (FB_MAX_COLS-MSG_MAX_LINE_LEN)
|
|
#define MSG_MAX_LEN (MSG_MAX_LINE_LEN*FB_MAX_LINES)
|
|
#define FB_MIDDLE_LINE (FB_MAX_LINES/2)
|
|
|
|
// this converts to string
|
|
#define STR_(X) #X
|
|
// this makes sure the argument is expanded before converting to string
|
|
#define STR(X) STR_(X)
|
|
|
|
MenuItem main_menu_items[6] = {
|
|
{"Add Missing Koroks to Map", &sortKoroksByDistance},
|
|
{"Erase Completed Korok Pins", &eraseCompletedKorokPins},
|
|
{"Erase All Map Pins", &eraseMapPins},
|
|
{"Convert Invalid Map Pins", &convertInvalidMapPinIcons},
|
|
{"Save and Exit", &saveAndExitApp},
|
|
{"Exit w/o Saving", &exitApp}
|
|
};
|
|
Menu main_menu = {
|
|
.HeaderStr = "BOTW KOROK COMPLETER",
|
|
.ItemCount = 6,
|
|
.Menu_Items = main_menu_items
|
|
};
|
|
MenuItem confirm_menu_items[2] = {
|
|
{"No, cancel!", NULL},
|
|
{"Yes, do it!", NULL}
|
|
};
|
|
Menu confirm_menu = {
|
|
.HeaderStr = "ARE YOU SURE??",
|
|
.ItemCount = 2,
|
|
.Menu_Items = confirm_menu_items
|
|
};
|
|
Menu *current_menu;
|
|
Menu *previous_menu;
|
|
|
|
char MSG_BUFFER[FB_MAX_LINES][MSG_MAX_LINE_LEN+1];
|
|
u32 msg_buf_write_line = 0;
|
|
u32 msg_buf_line_count = 0;
|
|
|
|
bool exit_state = false;
|
|
bool confirmed = false;
|
|
|
|
void _clearScreen() {
|
|
printf(CONSOLE_ESC(2J)); //clear screen and reset home cursor
|
|
}
|
|
|
|
void loadMenu(Menu* menu) {
|
|
current_menu = menu;
|
|
current_menu->StartLine = FB_MIDDLE_LINE - (current_menu->ItemCount / 2);
|
|
current_menu->SelectedIndex = 0;
|
|
|
|
_clearScreen();
|
|
_drawMenu();
|
|
_drawMessagePane();
|
|
}
|
|
|
|
void _drawMenu() {
|
|
//new code
|
|
printfAtCursor(2, current_menu->StartLine-2, current_menu->HeaderStr);
|
|
printfAtCursor(2, current_menu->StartLine, ">");
|
|
for (int i = 0; i < current_menu->ItemCount; i++) {
|
|
printfAtCursor(4, current_menu->StartLine+i, current_menu->Menu_Items[i].ItemStr);
|
|
}
|
|
|
|
//end new code
|
|
/*/old code
|
|
printfAtCursor(2, MENU_START_COL-2, "BOTW KOROK COMPLETER");
|
|
printfAtCursor(2, MENU_START_COL, "> Add Missing Koroks to Map");
|
|
printfAtCursor(2, MENU_START_COL+1, " Erase all map pins");
|
|
printfAtCursor(2, MENU_START_COL+2, " Convert invalid map pins");
|
|
printfAtCursor(2, MENU_START_COL+3, " Save and Exit");
|
|
printfAtCursor(2, MENU_START_COL+4, " Exit w/o Saving");
|
|
//end old code*/
|
|
|
|
//print vertical line between menu and messages panel
|
|
for (int i = 1; i <= FB_MAX_LINES; i++) {
|
|
printfAtCursor(FB_MAX_COLS - MSG_MAX_LINE_LEN - 2, i, "|");
|
|
}
|
|
}
|
|
|
|
void initUI() {
|
|
loadMenu(&main_menu);
|
|
}
|
|
|
|
void exitApp() {
|
|
exit_state = true;
|
|
}
|
|
|
|
void saveAndExitApp() {
|
|
closeAndCommitSave();
|
|
exitApp();
|
|
}
|
|
|
|
void handleMenuButtonPress(u64 button) {
|
|
//clear menu cursor
|
|
printfAtCursor(2, current_menu->StartLine+current_menu->SelectedIndex, " ");
|
|
|
|
switch (button) {
|
|
case HidNpadButton_Down:
|
|
if (current_menu->SelectedIndex == current_menu->ItemCount - 1) {
|
|
current_menu->SelectedIndex = 0;
|
|
} else {
|
|
current_menu->SelectedIndex++;
|
|
}
|
|
break;
|
|
case HidNpadButton_Up:
|
|
if (current_menu->SelectedIndex == 0) {
|
|
current_menu->SelectedIndex = current_menu->ItemCount-1;
|
|
} else {
|
|
current_menu->SelectedIndex--;
|
|
}
|
|
break;
|
|
case HidNpadButton_A: //run menu function, using menu item index: position - start_line
|
|
current_menu->Menu_Items[current_menu->SelectedIndex].ItemFunc();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
//print menu cursor
|
|
printfAtCursor(2, current_menu->StartLine+current_menu->SelectedIndex, ">");
|
|
}
|
|
|
|
bool handleButtonPress(u64 button) {
|
|
if (button & HidNpadButton_Plus) { return true; } // exit if + button pressed
|
|
|
|
handleMenuButtonPress(button);
|
|
return exit_state;
|
|
}
|
|
/*u32 handleMenuButtonPress(u64 button) {
|
|
switch (button) {
|
|
case HidNpadButton_Down:
|
|
printf(CONSOLE_ESC(%d;2H) " ", menu_pos);
|
|
if (menu_pos == MENU_END_LINE) { menu_pos = MENU_START_LINE; }
|
|
else { menu_pos++; }
|
|
printf(CONSOLE_ESC(%d;2H) ">", menu_pos);
|
|
break;
|
|
case HidNpadButton_Up:
|
|
printf(CONSOLE_ESC(%d;2H) " ", menu_pos);
|
|
if (menu_pos == MENU_START_LINE) { menu_pos = MENU_END_LINE; }
|
|
else { menu_pos--; }
|
|
printf(CONSOLE_ESC(%d;2H) ">", menu_pos);
|
|
break;
|
|
case HidNpadButton_A:
|
|
return menu_pos;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return (enum menu_items)none;
|
|
}
|
|
|
|
bool handleButtonPress(u64 button) {
|
|
switch(handleMenuButtonPress(button)) {
|
|
case add_missing_koroks:
|
|
sortKoroksByDistance();
|
|
break;
|
|
case erase_all_map_pins:
|
|
eraseMapPins();
|
|
break;
|
|
case convert_invalid_map_pins:
|
|
convertInvalidMapPinIcons();
|
|
break;
|
|
case save_file:
|
|
writeSave();
|
|
closeAndCommitSave();
|
|
return true;
|
|
break;
|
|
case exit_app:
|
|
return true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}*/
|
|
|
|
void _confirm_no() {
|
|
loadMenu(previous_menu);
|
|
}
|
|
|
|
void confirmThenRunElse(func_t yes_func, func_t no_func) {
|
|
//save current menu
|
|
previous_menu = current_menu;
|
|
|
|
confirm_menu.Menu_Items[1].ItemFunc = yes_func;
|
|
confirm_menu.Menu_Items[0].ItemFunc = no_func;
|
|
loadMenu(&confirm_menu);
|
|
}
|
|
|
|
void confirmThenRun(func_t yes_func) {
|
|
confirmThenRunElse(yes_func, &_confirm_no);
|
|
}
|
|
|
|
void printfAtCursor(u32 x_pos, u32 y_pos, const char* format, ...) {
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
_setCursorPos(x_pos, y_pos);
|
|
vprintf(format, args);
|
|
|
|
va_end(args);
|
|
}
|
|
|
|
void _setCursorPos(u32 x_pos, u32 y_pos) {
|
|
if (x_pos >= 1 && x_pos <= FB_MAX_COLS && y_pos >= 1 && y_pos <= FB_MAX_LINES) {
|
|
printf(CONSOLE_ESC(%d;%dH) , y_pos, x_pos);
|
|
}
|
|
}
|
|
|
|
void printMessage(const char* format, ...) {
|
|
//get variable args
|
|
va_list args;
|
|
va_start(args, format);
|
|
|
|
//process the format string
|
|
char message[MSG_MAX_LEN-FB_MAX_LINES];
|
|
vsnprintf(message, MSG_MAX_LEN-FB_MAX_LINES, format, args);
|
|
va_end(args);
|
|
|
|
//find out how many lines we are about to add to the buffer/print
|
|
size_t length = strlen(message);
|
|
u32 num_of_lines = length / MSG_MAX_LINE_LEN;
|
|
if (length%MSG_MAX_LINE_LEN != 0) {num_of_lines++;}
|
|
|
|
char *line;
|
|
|
|
for (int i = 0; i < num_of_lines; i++) {
|
|
//extract the next set of characters and store in line
|
|
line = strndup(message + (MSG_MAX_LINE_LEN*i), MSG_MAX_LINE_LEN);
|
|
|
|
//clear previous line
|
|
memset(MSG_BUFFER[msg_buf_write_line], 0, MSG_MAX_LINE_LEN+1);
|
|
//copy line to MSG_BUFFER at write_ptr
|
|
strncpy(MSG_BUFFER[msg_buf_write_line], line, MSG_MAX_LINE_LEN);
|
|
MSG_BUFFER[msg_buf_write_line][MSG_MAX_LINE_LEN] = '\0';
|
|
msg_buf_write_line++;
|
|
|
|
//free original line after we've copied it
|
|
free(line);
|
|
|
|
//if count is below max lines, then increase count
|
|
if (msg_buf_line_count < FB_MAX_LINES) { msg_buf_line_count++; }
|
|
// if the next write line is past the last index, set the next index to 0.
|
|
if (msg_buf_write_line == FB_MAX_LINES) { msg_buf_write_line = 0; }
|
|
|
|
_drawMessagePane();
|
|
}
|
|
}
|
|
void _drawMessagePane() {
|
|
//print all lines in buffer, from top to bottom, oldest to newest
|
|
if (msg_buf_line_count > 0) {
|
|
u32 start_line = 0;
|
|
if (msg_buf_line_count == FB_MAX_LINES) {
|
|
start_line = msg_buf_write_line;
|
|
}
|
|
for (int i = 0; i < msg_buf_line_count; i++) {
|
|
_setCursorPos(MSG_START_COL, i+1);
|
|
printf("%-" STR(MSG_MAX_LINE_LEN) "s", MSG_BUFFER[(start_line + i) % FB_MAX_LINES]);
|
|
}
|
|
}
|
|
} |