Files
2023-03-13 09:05:51 +00:00

473 lines
12 KiB
C

#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include "ff.h"
#include "config.h"
#include "comms.h"
#include "memstrings.h"
#include "fileutils.h"
#include "tapuino.h"
#include "lcd_interface.h"
#include "lcdutils.h"
#define MODE_PLAY 0
#define MODE_RECORD 1
#define MODE_OPTIONS 2
#define REC_MODE_MANUAL 0
#define REC_MODE_AUTO 1
#define OPTION_MACHINE_TYPE 0
#define OPTION_VIDEO_MODE 1
#define OPTION_SIGNAL 2
#define OPTION_KEY_REPEAT 3
#define OPTION_TICKER_SPEED 4
#define OPTION_TICKER_HOLD 5
#define OPTION_REC_FINALIZE 6
#define OPTION_REC_AUTO_FINALIZE 7
#define SELECT_MODE_EXIT 0xFF
int g_num_files = 0;
int g_cur_file_index = 0;
uint8_t get_cur_command() {
// this order of operations is very important
// first get an 'atomic' read of g_cur_command into a local
uint8_t cur_command = g_cur_command;
// then compare the _local_ against a non-IDLE command
if (cur_command != COMMAND_IDLE) {
// and clear the global i.e. only if the global was non-idle at the time of read
// this prevents clearing the global as a key is pressed and missing it
g_cur_command = COMMAND_IDLE;
}
return cur_command;
}
uint8_t handle_select_mode(const char* ptitle, const char** ppitems, uint8_t max, uint8_t cur_mode) {
uint8_t prev_mode = (cur_mode + 1) % max;
lcd_title_P(ptitle);
while (1) {
if (prev_mode != cur_mode) {
lcd_status_P(ppitems[cur_mode]);
prev_mode = cur_mode;
}
switch(get_cur_command()) {
case COMMAND_SELECT:
return cur_mode;
break;
case COMMAND_ABORT:
return SELECT_MODE_EXIT;
break;
case COMMAND_NEXT:
if (cur_mode == (max - 1)) {
cur_mode = 0;
} else {
cur_mode++;
}
break;
case COMMAND_PREVIOUS:
if (cur_mode == 0) {
cur_mode = max -1;
} else {
cur_mode--;
}
break;
}
}
}
void handle_play_mode(FILINFO* pfile_info) {
// reset to the root after a possible record operation
change_dir("/");
// refresh the file list to avoid blank entries bug
if ((g_num_files = get_num_files(pfile_info)) == 0) {
lcd_title_P(S_NO_FILES_FOUND);
lcd_busy_spinner();
return;
}
g_cur_file_index = 0;
if (!get_file_at_index(pfile_info, g_cur_file_index)) {
// shouldn't happen...
lcd_title_P(S_NO_FILES_FOUND);
lcd_busy_spinner();
return;
}
lcd_title_P(S_SELECT_FILE);
display_filename(pfile_info);
while (1) {
switch(get_cur_command()) {
case COMMAND_SELECT:
if (pfile_info->fattrib & AM_DIR) {
if (change_dir(pfile_info->fname) == FR_OK) {
g_num_files = get_num_files(pfile_info);
g_cur_file_index = 0;
get_file_at_index(pfile_info, g_cur_file_index);
display_filename(pfile_info);
} else {
lcd_status_P(S_CHDIR_FAILED);
}
} else {
display_filename(pfile_info);
play_file(pfile_info);
lcd_title_P(S_SELECT_FILE);
// buffer is used so get the file again
get_file_at_index(pfile_info, g_cur_file_index);
display_filename(pfile_info);
}
break;
case COMMAND_ABORT:
if (g_fs.cdir != 0) {
if (change_dir("..") == FR_OK) {
g_num_files = get_num_files(pfile_info);
g_cur_file_index = 0;
get_file_at_index(pfile_info, g_cur_file_index);
display_filename(pfile_info);
} else {
lcd_status_P(S_CHDIR_FAILED);
}
} else {
// back to main menu
return;
}
break;
case COMMAND_NEXT:
if (++g_cur_file_index >= g_num_files) {
g_cur_file_index = 0;
}
get_file_at_index(pfile_info, g_cur_file_index);
display_filename(pfile_info);
break;
case COMMAND_PREVIOUS:
if (--g_cur_file_index < 0) {
g_cur_file_index = g_num_files - 1;
}
get_file_at_index(pfile_info, g_cur_file_index);
display_filename(pfile_info);
break;
}
filename_ticker(pfile_info, get_timer_tick());
}
}
void handle_record_mode_ready(char* pfile_name) {
lcd_title_P(S_READY_RECORD);
lcd_status_P(S_PRESS_START);
while (1) {
switch(get_cur_command()) {
case COMMAND_SELECT:
record_file(pfile_name);
return;
break;
case COMMAND_ABORT:
// back to main menu
return;
break;
}
}
}
uint8_t handle_manual_filename(FILINFO* pfile_info) {
uint8_t cur_char_pos = 0;
uint8_t cursor_pos = 0;
uint8_t max_chars = strlen_P(S_FILENAME_CHARS);
uint8_t cur_char = 0;
lcd_title_P(S_ENTER_FILENAME);
lcd_status("");
lcd_cursor();
lcd_setCursor(0, 1);
// start with a nicely terminated string!
memset(pfile_info->lfname, 0, pfile_info->lfsize);
while (1) {
switch(get_cur_command()) {
case COMMAND_SELECT:
if (cursor_pos < (MAX_LCD_LINE_LEN - 1)) {
cur_char = pgm_read_byte(S_FILENAME_CHARS + cur_char_pos);
pfile_info->lfname[cursor_pos] = cur_char;
cursor_pos++;
lcd_setCursor(cursor_pos, 1);
cur_char_pos = 0;
}
break;
case COMMAND_SELECT_LONG:
strcat(pfile_info->lfname, ".tap");
lcd_noCursor();
// exit to previous menu, with accept
return 1;
break;
case COMMAND_ABORT:
if (cursor_pos != 0) {
pfile_info->lfname[cursor_pos] = 0;
lcd_setCursor(cursor_pos, 1);
lcd_write(' ');
cursor_pos--;
lcd_setCursor(cursor_pos, 1);
cur_char_pos = 0;
cur_char = pfile_info->lfname[cursor_pos];
while (pgm_read_byte(S_FILENAME_CHARS + cur_char_pos) != cur_char) {
cur_char_pos++;
}
}
break;
case COMMAND_ABORT_LONG:
lcd_title_P(S_OPERATION_ABORTED);
lcd_noCursor();
lcd_busy_spinner();
// exit to previous menu, with cancel
return 0;
break;
case COMMAND_NEXT:
cur_char_pos = (cur_char_pos + 1) % max_chars;
cur_char = pgm_read_byte(S_FILENAME_CHARS + cur_char_pos);
lcd_write(cur_char);
lcd_setCursor(cursor_pos, 1);
pfile_info->lfname[cursor_pos] = cur_char;
break;
case COMMAND_PREVIOUS:
if (cur_char_pos == 0) {
cur_char_pos = max_chars;
}
cur_char_pos--;
cur_char = pgm_read_byte(S_FILENAME_CHARS + cur_char_pos);
lcd_write(cur_char);
lcd_setCursor(cursor_pos, 1);
pfile_info->lfname[cursor_pos] = cur_char;
break;
}
}
}
void handle_record_mode(FILINFO* pfile_info) {
const char* ppitems[] = {S_REC_MODE_MANUAL, S_REC_MODE_AUTO};
uint8_t cur_mode = 0;
lcd_title_P(S_SELECT_RECORD_MODE);
// attempt to open the recording dir
strcpy_P((char*)g_fat_buffer, S_DEFAULT_RECORD_DIR);
// change to the recording dir
if (change_dir((char*)g_fat_buffer) != FR_OK) {
lcd_status_P(S_CHDIR_FAILED);
lcd_busy_spinner();
return;
}
while (1) {
cur_mode = handle_select_mode(S_SELECT_RECORD_MODE, ppitems, 2, cur_mode);
switch (cur_mode) {
case REC_MODE_AUTO:
handle_record_mode_ready(NULL);
return;
break;
case REC_MODE_MANUAL:
if (handle_manual_filename(pfile_info)) {
handle_record_mode_ready(pfile_info->lfname);
return;
}
break;
case SELECT_MODE_EXIT:
return;
break;
}
}
}
uint8_t handle_option_value(const char* poption, uint16_t* pcur_value, uint16_t min_value, uint16_t max_value, uint16_t step_value) {
char buffer[MAX_LCD_LINE_LEN + 1];
int32_t cur_value = *pcur_value;
if (cur_value < min_value || cur_value > max_value) {
cur_value = min_value;
}
lcd_title_P(poption);
utoa((uint16_t)cur_value, buffer, 10);
lcd_status(buffer);
while (1) {
switch(get_cur_command()) {
case COMMAND_SELECT:
*pcur_value = (uint16_t) cur_value;
return 1;
break;
case COMMAND_ABORT:
return 0;
break;
case COMMAND_NEXT:
cur_value += step_value;
if (cur_value > max_value) {
cur_value = max_value;
}
utoa((uint16_t)cur_value, buffer, 10);
lcd_status(buffer);
break;
case COMMAND_PREVIOUS:
cur_value -= step_value;
if (cur_value < min_value) {
cur_value = min_value;
}
utoa((uint16_t)cur_value, buffer, 10);
lcd_status(buffer);
break;
}
}
}
uint8_t handle_option_enum(const char* poption, uint16_t* pcur_value, uint16_t max_items, const char* ppitems[]) {
int32_t cur_value = *pcur_value;
lcd_title_P(poption);
lcd_status_P(ppitems[cur_value]);
while (1) {
switch(get_cur_command()) {
case COMMAND_SELECT:
*pcur_value = (uint16_t) cur_value;
return 1;
break;
case COMMAND_ABORT:
return 0;
break;
case COMMAND_NEXT:
cur_value++;
if (cur_value >= max_items) {
cur_value = 0;
}
lcd_status_P(ppitems[cur_value]);
break;
case COMMAND_PREVIOUS:
cur_value--;
if (cur_value < 0) {
cur_value = max_items - 1;
}
lcd_status_P(ppitems[cur_value]);
break;
}
}
}
void handle_mode_options() {
const char* ppitems[] = {S_OPTION_MACHINE_TYPE, S_OPTION_VIDEO_MODE, S_OPTION_SIGNAL, S_OPTION_KEY_REPEAT, S_OPTION_TICKER_SPEED, S_OPTION_TICKER_HOLD, S_OPTION_REC_FINALIZE, S_OPTION_REC_AUTO_FINALIZE};
uint16_t value = 0;
uint8_t save = 0;
uint8_t cur_mode = 0;
while (1) {
cur_mode = handle_select_mode(S_MODE_OPTIONS, ppitems, 8, cur_mode);
switch (cur_mode) {
case OPTION_MACHINE_TYPE:
{
const char* ppenum[] = {S_C64, S_VIC, S_C16};
value = g_machine_type;
if (handle_option_enum(S_OPTION_MACHINE_TYPE, &value, 3, ppenum)) {
save = 1;
g_machine_type = value;
}
}
break;
case OPTION_VIDEO_MODE:
{
const char* ppenum[] = {S_PAL, S_NTSC};
value = g_video_mode;
if (handle_option_enum(S_OPTION_VIDEO_MODE, &value, 2, ppenum)) {
save = 1;
g_video_mode = value;
}
}
break;
case OPTION_SIGNAL:
value = g_invert_signal;
if (handle_option_value(S_OPTION_SIGNAL, &value, 0, 1, 1)) {
g_invert_signal = value;
if (value) {
CONTROL_SET_BUS1();
} else {
CONTROL_SET_BUS0();
}
save = 1;
}
break;
case OPTION_KEY_REPEAT:
value = g_key_repeat_next * 10;
if (handle_option_value(S_OPTION_KEY_REPEAT, &value, 50, 500, 50)) {
g_key_repeat_next = value / 10;
save = 1;
}
break;
case OPTION_TICKER_SPEED:
value = g_ticker_rate * 10;
if (handle_option_value(S_OPTION_TICKER_SPEED, &value, 50, 500, 50)) {
g_ticker_rate = value / 10;
save = 1;
}
break;
case OPTION_TICKER_HOLD:
value = g_ticker_hold_rate * 10;
if (handle_option_value(S_OPTION_TICKER_HOLD, &value, 250, 2500, 250)) {
g_ticker_hold_rate = value / 10;
save = 1;
}
break;
case OPTION_REC_FINALIZE:
value = g_rec_finalize_time * 10;
if (handle_option_value(S_OPTION_REC_FINALIZE, &value, 500, 2500, 500)) {
g_rec_finalize_time = value / 10;
save = 1;
}
break;
case OPTION_REC_AUTO_FINALIZE:
value = g_rec_auto_finalize;
if (handle_option_value(S_OPTION_REC_AUTO_FINALIZE, &value, 0, 1, 1)) {
g_rec_auto_finalize = value;
save = 1;
}
break;
case SELECT_MODE_EXIT:
if (save) {
save_eeprom_data();
}
return;
break;
}
}
}
void main_menu(FILINFO* pfile_info) {
const char* ppitems[] = {S_MODE_PLAY, S_MODE_RECORD, S_MODE_OPTIONS};
uint8_t cur_mode = 0;
if ((g_num_files = get_num_files(pfile_info)) == 0) {
lcd_title_P(S_NO_FILES_FOUND);
return;
}
while (1) {
cur_mode = handle_select_mode(S_SELECT_MODE, ppitems, 3, cur_mode);
switch (cur_mode) {
case MODE_PLAY:
handle_play_mode(pfile_info);
break;
case MODE_RECORD:
handle_record_mode(pfile_info);
break;
case MODE_OPTIONS:
handle_mode_options();
break;
case SELECT_MODE_EXIT:
cur_mode = 0;
break;
}
}
}