// // Copyright (C) 2015-2023 Markus Hiienkari // // This file is part of Open Source Scan Converter project. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include #include #include "menu.h" #include "av_controller.h" #include "firmware.h" #include "userdata.h" #include "controls.h" #include "lcd.h" #include "sdcard.h" #include "tvp7002.h" #include "ff.h" #include "file.h" #define OPT_NOWRAP 0 #define OPT_WRAP 1 extern char row1[LCD_ROW_LEN+1], row2[LCD_ROW_LEN+1], menu_row1[LCD_ROW_LEN+1], menu_row2[LCD_ROW_LEN+1]; char func_ret_status[LCD_ROW_LEN+1]; extern avmode_t cm; extern avconfig_t tc; extern settings_t ts; extern mode_data_t video_modes_plm[]; extern alt_u32 remote_code; extern alt_u16 rc_keymap[REMOTE_MAX_KEYS]; extern alt_u8 vm_sel, profile_sel_menu, sd_profile_sel_menu, lt_sel; extern alt_u8 update_cur_vm; extern uint8_t sl_def_iv_x, sl_def_iv_y; extern char target_profile_name[USERDATA_NAME_LEN+1]; extern volatile osd_regs *osd; extern const int num_video_modes_plm; extern c_shmask_t c_shmask; extern c_lc_palette_set_t c_lc_palette_set; extern SD_DEV sdcard_dev; extern int shmask_loaded_array; extern int loaded_lc_palette; alt_u16 tc_h_samplerate, tc_h_samplerate_adj, tc_h_synclen, tc_h_bporch, tc_h_active, tc_v_synclen, tc_v_bporch, tc_v_active, tc_sampler_phase, tc_h_mask, tc_v_mask; alt_u8 menu_active; alt_u8 vm_sel, vm_edit, palset_type_sel; static const char* const off_on_desc[] = { LNG("Off","オフ"), LNG("On","オン") }; static const char* const video_lpf_desc[] = { LNG("Auto","オート"), LNG("Off","オフ"), "95MHz (HDTV II)", "35MHz (HDTV I)", "16MHz (EDTV)", "9MHz (SDTV)" }; static const char* const ypbpr_cs_desc[] = { "Rec. 601", "Rec. 709", "Auto" }; static const char* const s480p_mode_desc[] = { LNG("Auto","オート"), "DTV 480p", "VESA 640x480@60", "PSP 480x272" }; static const char* const s400p_mode_desc[] = { "VGA 640x400@70", "VGA 720x400@70" }; static const char* const sync_lpf_desc[] = { LNG("2.5MHz (max)","2.5MHz (サイダイ)"), LNG("10MHz (med)","10MHz (チュウイ)"), LNG("33MHz (min)","33MHz (サイショウ)"), LNG("Off","オフ") }; static const char* const stc_lpf_desc[] = { "4.8MHz (HDTV/PC)", "0.5MHz (SDTV)", "1.7MHz (EDTV)" }; static const char* const pt_mode_desc[] = {"Normal", "High samplerate", LNG("512x240 optim.","512x240 サイテキカ."), LNG("384x240 optim.","384x240 サイテキカ."), LNG("320x240 optim.","320x240 サイテキカ."), LNG("256x240 optim.","256x240 サイテキカ.") }; static const char* const l3_mode_desc[] = { LNG("Generic 16:9","ジェネリック 16:9"), LNG("Generic 4:3","ジェネリック 4:3"), LNG("512x240 optim.","512x240 サイテキカ."), LNG("384x240 optim.","384x240 サイテキカ."), LNG("320x240 optim.","320x240 サイテキカ."), LNG("256x240 optim.","256x240 サイテキカ.") }; static const char* const l2l4l5l6_mode_desc[] = { LNG("Generic 4:3","ジェネリック 4:3"), LNG("512x240 optim.","512x240 サイテキカ."), LNG("384x240 optim.","384x240 サイテキカ."), LNG("320x240 optim.","320x240 サイテキカ."), LNG("256x240 optim.","256x240 サイテキカ.") }; static const char* const l5_fmt_desc[] = { "1920x1080", "1600x1200", "1920x1200" }; static const char* const pm_240p_desc[] = { LNG("Passthru","パススルー"), "Line2x", "Line3x", "Line4x", "Line5x", "Line6x" }; static const char* const pm_480i_desc[] = { LNG("Passthru","パススルー"), "Line2x (bob)", "Line3x (laced)", "Line4x (bob)" }; static const char* const pm_384p_desc[] = { LNG("Passthru","パススルー"), "Line2x", "Line3x Generic", "Line2x 240x360", "Line3x 240x360" }; static const char* const pm_480p_desc[] = { LNG("Passthru","パススルー"), "Line2x", "Line3x Generic" }; static const char* const pm_1080i_desc[] = { LNG("Passthru","パススルー"), "Line2x (bob)" }; static const char* const ar_256col_desc[] = { "Pseudo 4:3 DAR", "1:1 PAR" }; static const char* const tx_mode_desc[] = { "HDMI (RGB)", "HDMI (YCbCr444)", "DVI" }; static const char* const sl_mode_desc[] = { LNG("Off","オフ"), LNG("Auto","オート"), LNG("On","オン") }; static const char* const sl_method_desc[] = { LNG("Multiplication","Multiplication"), LNG("Subtraction","Subtraction") }; static const char* const sl_type_desc[] = { LNG("Horizontal","ヨコ"), LNG("Vertical","タテ"), "Horiz. + Vert.", "Custom" }; static const char* const sl_id_desc[] = { LNG("Top","ウエ"), LNG("Bottom","シタ") }; static const char* const audio_dw_sampl_desc[] = { LNG("Off (fs = 96kHz)","オフ (fs = 96kHz)"), "2x (fs = 48kHz)" }; static const char* const lt_desc[] = { "Top-left", "Center", "Bottom-right" }; static const char* const lcd_bl_timeout_desc[] = { "Off", "3s", "10s", "30s" }; static const char* const osd_enable_desc[] = { "Off", "Full", "Simple" }; static const char* const osd_status_desc[] = { "2s", "5s", "10s", "Off" }; static const char* const osd_color_desc[] = { "Green", "Cyan", "Red", "Magenta", "Yellow" }; static const char* const rgsb_ypbpr_desc[] = { "RGsB", "YPbPr" }; static const char* const auto_input_desc[] = { "Off", "Current input", "All inputs" }; static const char* const mask_color_desc[] = { "Black", "Blue", "Green", "Cyan", "Red", "Magenta", "Yellow", "White" }; static const char* const av3_alt_rgb_desc[] = { "Off", "AV1", "AV2" }; static const char* const shmask_mode_desc[] = { "Off", "A-Grille", "TV", "PVM", "PVM-2530", "XC-3315C", "C-1084", "JVC", "VGA", c_shmask.name }; static const char* const lumacode_mode_desc[] = { "Off", "VIC20", "C64", "Spectrum", "Coleco/MSX", "Intellivision", "G7000", "MC6847", "Master System", "NES", "Atari GTIA", "Atari VCS", "Atari 7800" }; static const char* const lumacode_pal_desc[] = { "PAL", "NTSC", c_lc_palette_set.name }; static const char* const adc_pll_bw_desc[] = { "High", "Medium", "Low", "Ultra low" }; static const char* const fpga_pll_bw_desc[] = { "High", "Low" }; static const char* const palset_type_arr[] = { "Custom .txt", "NES .pal" }; static void sync_vth_disp(alt_u8 v) { sniprintf(menu_row2, LCD_ROW_LEN+1, "%d mV", (v*1127)/100); } static void intclks_to_time_disp(alt_u8 v) { sniprintf(menu_row2, LCD_ROW_LEN+1, "%u.%.2u us", (unsigned)(((1000000U*v)/(TVP_INTCLK_HZ/1000))/1000), (unsigned)((((1000000U*v)/(TVP_INTCLK_HZ/1000))%1000)/10)); } static void extclks_to_time_disp(alt_u8 v) { sniprintf(menu_row2, LCD_ROW_LEN+1, "%u.%.2u us", (unsigned)(((1000000U*v)/(TVP_EXTCLK_HZ/1000))/1000), (unsigned)((((1000000U*v)/(TVP_EXTCLK_HZ/1000))%1000)/10)); } static void sl_str_disp(alt_u8 v) { sniprintf(menu_row2, LCD_ROW_LEN+1, "%u%%", ((v+1)*625)/100); } static void sl_cust_str_disp(alt_u8 v) { sniprintf(menu_row2, LCD_ROW_LEN+1, "%u%%", ((v)*625)/100); } static void sl_cust_iv_x_disp(uint8_t v) { sniprintf(menu_row2, LCD_ROW_LEN+1, "%s%u%s", (v ? "" : "Auto ("), (v ? v : sl_def_iv_x)+1, (v ? "" : ")")); } static void sl_cust_iv_y_disp(uint8_t v) { sniprintf(menu_row2, LCD_ROW_LEN+1, "%s%u%s", (v ? "" : "Auto ("), (v ? v : sl_def_iv_y)+1, (v ? "" : ")")); } static void sl_hybr_str_disp(alt_u8 v) { sniprintf(menu_row2, LCD_ROW_LEN+1, "%u%%", (v*625)/100); } static void lines_disp(alt_u8 v) { sniprintf(menu_row2, LCD_ROW_LEN+1, LNG("%u lines","%u ライン"), v); } static void pixels_disp(alt_u8 v) { sniprintf(menu_row2, LCD_ROW_LEN+1, LNG("%u pixels","%u ドット"), v); } static void value_disp(alt_u8 v) { sniprintf(menu_row2, LCD_ROW_LEN+1, "%u", v); } static void signed_disp(alt_u8 v) { sniprintf(menu_row2, LCD_ROW_LEN+1, "%d", (alt_8)(v-SIGNED_NUMVAL_ZERO)); } static void lt_disp(alt_u8 v) { strncpy(menu_row2, lt_desc[v], LCD_ROW_LEN+1); } static void aud_db_disp(alt_u8 v) { sniprintf(menu_row2, LCD_ROW_LEN+1, "%d dB", ((alt_8)v-AUDIO_GAIN_0DB)); } static void vm_display_name (alt_u8 v) { strncpy(menu_row2, video_modes_plm[v].name, LCD_ROW_LEN+1); } static void link_av_desc (avinput_t v) { strncpy(menu_row2, v == AV_LAST ? "No link" : avinput_str[v], LCD_ROW_LEN+1); } static void profile_disp(uint8_t v) { sniprintf(menu_row2, LCD_ROW_LEN+1, "%u: %s", v, (read_userdata(v, 1) != 0) ? "" : target_profile_name); } static void sd_profile_disp(uint8_t v) { sniprintf(menu_row2, LCD_ROW_LEN+1, "%u: %s", v, (read_userdata_sd(v, 1) != 0) ? "" : target_profile_name); } static void alc_v_filter_disp(alt_u8 v) { sniprintf(menu_row2, LCD_ROW_LEN+1, LNG("%u lines","%u ライン"), (1<","<カクシュタイミング>"), OPT_SUBMENU, { .sub = { &menu_advtiming, &vm_arg_info, vm_select } } }, })) MENU(menu_output, P99_PROTECT({ \ { LNG("TX mode","TXモード"), OPT_AVCONFIG_SELECTION, { .sel = { &tc.tx_mode, OPT_WRAP, SETTING_ITEM(tx_mode_desc) } } }, { "HDMI ITC", OPT_AVCONFIG_SELECTION, { .sel = { &tc.hdmi_itc, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, { "HDMI HDR flag", OPT_AVCONFIG_SELECTION, { .sel = { &tc.hdmi_hdr, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, { "HDMI VRR flag", OPT_AVCONFIG_SELECTION, { .sel = { &tc.hdmi_vrr, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, { "480p/576p pbox", OPT_AVCONFIG_SELECTION, { .sel = { &tc.o480p_pbox, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, })) MENU(menu_scanlines, P99_PROTECT({ \ { LNG("Scanlines","スキャンライン"), OPT_AVCONFIG_SELECTION, { .sel = { &tc.sl_mode, OPT_WRAP, SETTING_ITEM(sl_mode_desc) } } }, { LNG("Sl. strength","スキャンラインツヨサ"), OPT_AVCONFIG_NUMVALUE, { .num = { &tc.sl_str, OPT_NOWRAP, 0, SCANLINESTR_MAX, sl_str_disp } } }, { "Sl. hybrid str.", OPT_AVCONFIG_NUMVALUE, { .num = { &tc.sl_hybr_str, OPT_NOWRAP, 0, SL_HYBRIDSTR_MAX, sl_hybr_str_disp } } }, { "Sl. method", OPT_AVCONFIG_SELECTION, { .sel = { &tc.sl_method, OPT_WRAP, SETTING_ITEM(sl_method_desc) } } }, { "Sl. alternating", OPT_AVCONFIG_SELECTION, { .sel = { &tc.sl_altern, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, { LNG("Sl. alignment","スキャンラインポジション"), OPT_AVCONFIG_SELECTION, { .sel = { &tc.sl_id, OPT_WRAP, SETTING_ITEM(sl_id_desc) } } }, { LNG("Sl. type","スキャンラインルイ"), OPT_AVCONFIG_SELECTION, { .sel = { &tc.sl_type, OPT_WRAP, SETTING_ITEM(sl_type_desc) } } }, { "< Custom Sl. >", OPT_SUBMENU, { .sub = { &menu_cust_sl, NULL, NULL } } }, })) MENU(menu_postproc, P99_PROTECT({ \ { "Shadow mask", OPT_AVCONFIG_SELECTION, { .sel = { &tc.shmask_mode, OPT_WRAP, SETTING_ITEM(shmask_mode_desc) } } }, { "", OPT_CUSTOMMENU, { .cstm = { &cstm_shmask_load } } }, { "Sh. mask strength", OPT_AVCONFIG_NUMVALUE, { .num = { &tc.shmask_str, OPT_NOWRAP, 0, SCANLINESTR_MAX, sl_str_disp } } }, { "Border color", OPT_AVCONFIG_SELECTION, { .sel = { &tc.mask_color, OPT_NOWRAP, SETTING_ITEM(mask_color_desc) } } }, { LNG("Border brightn.","マスクアカルサ"), OPT_AVCONFIG_NUMVALUE, { .num = { &tc.mask_br, OPT_NOWRAP, 0, HV_MASK_MAX_BR, value_disp } } }, { LNG("","DIYチエンテスト"), OPT_FUNC_CALL, { .fun = { latency_test, <_arg_info } } }, })) MENU(menu_compatibility, P99_PROTECT({ \ { LNG("Full TX setup","フルTXセットアップ"), OPT_AVCONFIG_SELECTION, { .sel = { &tc.full_tx_setup, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, { LNG("Allow TVP HPLL2x","TVP HPLL2xキョヨウ"), OPT_AVCONFIG_SELECTION, { .sel = { &tc.tvp_hpll2x, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, { "AV3 use alt. RGB", OPT_AVCONFIG_SELECTION, { .sel = { &tc.av3_alt_rgb, OPT_WRAP, SETTING_ITEM(av3_alt_rgb_desc) } } }, { "Full VSYNC bypas", OPT_AVCONFIG_SELECTION, { .sel = { &tc.full_vs_bypass, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, { "Default HDMI VIC", OPT_AVCONFIG_NUMVALUE, { .num = { &tc.default_vic, OPT_NOWRAP, 0, HDMI_1080p50, value_disp } } }, { "Panasonic hack", OPT_AVCONFIG_SELECTION, { .sel = { &tc.panasonic_hack, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, })) MENU(menu_audio, P99_PROTECT({ \ { LNG("Down-sampling","ダウンサンプリング"), OPT_AVCONFIG_SELECTION, { .sel = { &tc.audio_dw_sampl, OPT_WRAP, SETTING_ITEM(audio_dw_sampl_desc) } } }, { LNG("Swap left/right","ヒダリ/ミギスワップ"), OPT_AVCONFIG_SELECTION, { .sel = { &tc.audio_swap_lr, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, { "Mono mode", OPT_AVCONFIG_SELECTION, { .sel = { &tc.audio_mono, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, { "Pre-ADC gain", OPT_AVCONFIG_NUMVALUE, { .num = { &tc.audio_gain, OPT_NOWRAP, 0, AUDIO_GAIN_MAX, aud_db_disp } } }, })) #define AUDIO_MENU { LNG("Audio options >","オーディオオプション >"), OPT_SUBMENU, { .sub = { &menu_audio, NULL, NULL } } }, MENU(menu_settings, P99_PROTECT({ \ { LNG("Link prof->input","Link prof->input"), OPT_AVCONFIG_NUMVALUE, { .num = { &tc.link_av, OPT_WRAP, AV1_RGBs, AV_LAST, link_av_desc } } }, { LNG("Link input->prof","Link input->prof"), OPT_AVCONFIG_SELECTION, { .sel = { &ts.profile_link, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, { LNG("Initial input","ショキニュウリョク"), OPT_AVCONFIG_SELECTION, { .sel = { &ts.def_input, OPT_WRAP, SETTING_ITEM(avinput_str) } } }, { "Autodetect input", OPT_AVCONFIG_SELECTION, { .sel = { &ts.auto_input, OPT_WRAP, SETTING_ITEM(auto_input_desc) } } }, { "Auto AV1 Y/Gs", OPT_AVCONFIG_SELECTION, { .sel = { &ts.auto_av1_ypbpr, OPT_WRAP, SETTING_ITEM(rgsb_ypbpr_desc) } } }, { "Auto AV2 Y/Gs", OPT_AVCONFIG_SELECTION, { .sel = { &ts.auto_av2_ypbpr, OPT_WRAP, SETTING_ITEM(rgsb_ypbpr_desc) } } }, { "Auto AV3 Y/Gs", OPT_AVCONFIG_SELECTION, { .sel = { &ts.auto_av3_ypbpr, OPT_WRAP, SETTING_ITEM(rgsb_ypbpr_desc) } } }, { "LCD BL timeout", OPT_AVCONFIG_SELECTION, { .sel = { &ts.lcd_bl_timeout, OPT_WRAP, SETTING_ITEM(lcd_bl_timeout_desc) } } }, { "OSD", OPT_AVCONFIG_SELECTION, { .sel = { &ts.osd_enable, OPT_WRAP, SETTING_ITEM(osd_enable_desc) } } }, { "OSD status disp.", OPT_AVCONFIG_SELECTION, { .sel = { &ts.osd_status_timeout, OPT_WRAP, SETTING_ITEM(osd_status_desc) } } }, { "OSD cursor color", OPT_AVCONFIG_SELECTION, { .sel = { &ts.osd_highlight_color, OPT_WRAP, SETTING_ITEM(osd_color_desc) } } }, { "Phase hotkey", OPT_AVCONFIG_SELECTION, { .sel = { &ts.phase_hotkey_enable, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, { LNG("","<プロファイルロード >"), OPT_FUNC_CALL, { .fun = { load_profile, &profile_arg_info } } }, { LNG("","<プロファイルセーブ >"), OPT_FUNC_CALL, { .fun = { save_profile, &profile_arg_info } } }, { "" , OPT_FUNC_CALL, { .fun = { load_profile_sd, &sd_profile_arg_info } } }, { "" , OPT_FUNC_CALL, { .fun = { save_profile_sd, &sd_profile_arg_info } } }, { LNG("","<セッテイヲショキカ >"), OPT_FUNC_CALL, { .fun = { reset_profile, NULL } } }, //{ LNG("","<セッテイヨミコミ >"), OPT_FUNC_CALL, { .fun = { import_userdata, NULL } } }, //{ LNG("","<セッテイカキコミ >"), OPT_FUNC_CALL, { .fun = { export_userdata, NULL } } }, { LNG("","<ファームウェアアップデート>"), OPT_CUSTOMMENU, { .cstm = { &cstm_fw_update } } }, { "", OPT_FUNC_CALL, { .fun = { fw_init_secondary, NULL } } }, })) MENU(menu_main, P99_PROTECT({ \ { LNG("Video in opt. >","タイオウエイゾウ >"), OPT_SUBMENU, { .sub = { &menu_vinput_opt, NULL, NULL } } }, { LNG("Sync opt. >","ドウキオプション >"), OPT_SUBMENU, { .sub = { &menu_sync, NULL, NULL } } }, { "Line mult opt. >", OPT_SUBMENU, { .sub = { &menu_lm, NULL, NULL } } }, { LNG("Output opt. >","シュツリョクオプション >"), OPT_SUBMENU, { .sub = { &menu_output, NULL, NULL } } }, { LNG("Scanline opt. >","スキャンラインオプション >"), OPT_SUBMENU, { .sub = { &menu_scanlines, NULL, NULL } } }, { LNG("Post-proc. >","アトショリ >"), OPT_SUBMENU, { .sub = { &menu_postproc, NULL, NULL } } }, { LNG("Compatibility >","ゴカンセイ >"), OPT_SUBMENU, { .sub = { &menu_compatibility, NULL, NULL } } }, AUDIO_MENU { LNG("Settings opt >","セッテイカンリ >"), OPT_SUBMENU, { .sub = { &menu_settings, NULL, NULL } } }, })) // Max 3 levels currently menunavi navi[] = {{&menu_main, 0}, {NULL, 0}, {NULL, 0}}; alt_u8 navlvl = 0; static int osd_list_iter; char tmpbuf[SD_BLK_SIZE]; // 512 byte temp buffer for data // Pointer to custom menu display function void (*cstm_f)(menucode_id, int); menunavi* get_current_menunavi() { return &navi[navlvl]; } void init_menu() { // Set max ids for adv timing vm_arg_info.max = num_video_modes_plm-1; // Setup OSD osd->osd_config.x_size = 0; osd->osd_config.y_size = 0; osd->osd_config.x_offset = 3; osd->osd_config.y_offset = 3; osd->osd_config.enable = 1; osd->osd_config.status_timeout = 1; osd->osd_config.border_color = 1; } void write_option_value(const menuitem_t *item, int func_called, int retval) { switch (item->type) { case OPT_AVCONFIG_SELECTION: strncpy(menu_row2, item->sel.setting_str[*(item->sel.data)], LCD_ROW_LEN+1); break; case OPT_AVCONFIG_NUMVALUE: item->num.df(*(item->num.data)); break; case OPT_AVCONFIG_NUMVAL_U16: item->num_u16.df(item->num_u16.data); break; case OPT_SUBMENU: case OPT_CUSTOMMENU: case OPT_FUNC_CALL: if (func_called) { if (retval == 0) strncpy(menu_row2, "Done", LCD_ROW_LEN+1); else if (retval < 0) sniprintf(menu_row2, LCD_ROW_LEN+1, "Failed (%d)", retval); else strlcpy(menu_row2, func_ret_status, LCD_ROW_LEN+1); } else if (item->fun.arg_info) { item->fun.arg_info->df(*item->fun.arg_info->data); } else { menu_row2[0] = 0; } break; default: break; } } void render_osd_page() { int i; const menuitem_t *item; uint32_t row_mask[2] = {0, 0}; if (!menu_active || (ts.osd_enable != 1)) return; for (i=0; i < navi[navlvl].m->num_items; i++) { item = &navi[navlvl].m->items[i]; strncpy((char*)osd->osd_array.data[i][0], item->name, OSD_CHAR_COLS); row_mask[0] |= (1<type != OPT_SUBMENU) && (item->type != OPT_CUSTOMMENU) && (item->type != OPT_FUNC_CALL)) { write_option_value(item, 0, 0); strncpy((char*)osd->osd_array.data[i][1], menu_row2, OSD_CHAR_COLS); row_mask[1] |= (1<osd_sec_enable[0].mask = row_mask[0]; osd->osd_sec_enable[1].mask = row_mask[1]; } void display_menu(alt_u8 forcedisp) { menucode_id code = NO_ACTION; const menuitem_t *item; alt_u8 *val, val_wrap, val_min, val_max; alt_u16 *val_u16, val_u16_min, val_u16_max; int i, func_called = 0, retval = 0; for (i=RC_OK; i < RC_INFO; i++) { if (remote_code == rc_keymap[i]) { code = i; break; } } if (!forcedisp && !remote_code) return; // Custom menu function if ((cstm_f != NULL) && (code != PREV_MENU)) { cstm_f(code, 0); return; } item = &navi[navlvl].m->items[navi[navlvl].mp]; // Parse menu control switch (code) { case PREV_PAGE: case NEXT_PAGE: if ((item->type == OPT_FUNC_CALL) || (item->type == OPT_SUBMENU) || (item->type == OPT_CUSTOMMENU)) osd->osd_sec_enable[1].mask &= ~(1<num_items-1 : (navi[navlvl].mp-1); else navi[navlvl].mp = (navi[navlvl].mp+1) % navi[navlvl].m->num_items; break; case PREV_MENU: if (navlvl > 0) { if (cstm_f != NULL) cstm_f = NULL; else navlvl--; render_osd_page(); } else { menu_active = 0; osd->osd_config.menu_active = 0; cstm_f = NULL; ui_disp_status(0); return; } break; case OPT_SELECT: switch (item->type) { case OPT_SUBMENU: if (item->sub.arg_f) item->sub.arg_f(); if (navi[navlvl+1].m != item->sub.menu) navi[navlvl+1].mp = 0; navi[navlvl+1].m = item->sub.menu; navlvl++; render_osd_page(); break; case OPT_CUSTOMMENU: enter_cstm(item, 0); return; break; case OPT_FUNC_CALL: retval = item->fun.f(); func_called = 1; break; default: break; } break; case VAL_MINUS: case VAL_PLUS: switch (item->type) { case OPT_AVCONFIG_SELECTION: case OPT_AVCONFIG_NUMVALUE: val = item->sel.data; val_wrap = item->sel.wrap_cfg; val_min = item->sel.min; val_max = item->sel.max; if (code == VAL_MINUS) *val = (*val > val_min) ? (*val-1) : (val_wrap ? val_max : val_min); else *val = (*val < val_max) ? (*val+1) : (val_wrap ? val_min : val_max); break; case OPT_AVCONFIG_NUMVAL_U16: val_u16 = item->num_u16.data; val_u16_min = item->num_u16.min; val_u16_max = item->num_u16.max; val_wrap = (val_u16_min == 0); if (code == VAL_MINUS) *val_u16 = (*val_u16 > val_u16_min) ? (*val_u16-1) : (val_wrap ? val_u16_max : val_u16_min); else *val_u16 = (*val_u16 < val_u16_max) ? (*val_u16+1) : (val_wrap ? val_u16_min : val_u16_max); break; case OPT_SUBMENU: case OPT_CUSTOMMENU: case OPT_FUNC_CALL: if (item->sub.arg_info) { val = item->sub.arg_info->data; val_max = item->sub.arg_info->max; if (code == VAL_MINUS) *val = (*val > 0) ? (*val-1) : 0; else *val = (*val < val_max) ? (*val+1) : val_max; } break; default: break; } break; default: break; } // Generate menu text item = &navi[navlvl].m->items[navi[navlvl].mp]; strncpy(menu_row1, item->name, LCD_ROW_LEN+1); write_option_value(item, func_called, retval); strncpy((char*)osd->osd_array.data[navi[navlvl].mp][1], menu_row2, OSD_CHAR_COLS); osd->osd_row_color.mask = (1<type >= OPT_SUBMENU) && (item->type <= OPT_FUNC_CALL) && item->fun.arg_info != NULL)) osd->osd_sec_enable[1].mask |= (1<timings.v_active / 700; uint8_t par_x4 = (((400*vm_out->timings.h_active*vm_out->ar.v)/((vm_out->timings.v_active<timings.interlaced)*vm_out->ar.h))+50)/100; int8_t xadj_log2 = -2; while (par_x4 > 1) { par_x4 >>= 1; xadj_log2++; } osd->osd_config.x_size = ((osd_size + vm_out->timings.interlaced + xadj_log2) >= 0) ? (osd_size + vm_out->timings.interlaced + xadj_log2) : 0; osd->osd_config.y_size = osd_size; } void refresh_osd() { if (menu_active && (cstm_f == NULL)) { remote_code = 0; render_osd_page(); display_menu(1); } } void osd_array_fname_fill(FILINFO *fno) { // Buffer has space for up to 21 strings if (osd_list_iter < (sizeof(tmpbuf)/24) ) { sniprintf(&tmpbuf[osd_list_iter*24], 24, "%s", fno->fname); strncpy((char*)osd->osd_array.data[osd_list_iter+2][0], &tmpbuf[osd_list_iter*24], OSD_CHAR_COLS); } osd_list_iter++; } int load_shmask(char *dirname, char *filename) { FIL f_shmask; char dirname_root[10]; int arr_size_loaded=0; int v0=0,v1=0; int p; if (!sdcard_dev.mount) { if (file_mount() != 0) { printf("SD card not detected\n"); return -1; } } sniprintf(dirname_root, sizeof(dirname_root), "/%s", dirname); f_chdir(dirname_root); if (!file_open(&f_shmask, filename)) { while (file_get_string(&f_shmask, tmpbuf, sizeof(tmpbuf))) { // strip CR / CRLF tmpbuf[strcspn(tmpbuf, "\r\n")] = 0; // Skip empty / comment lines if ((tmpbuf[0] == 0) || (tmpbuf[0] == '#')) continue; if (!arr_size_loaded && (sscanf(tmpbuf, "%d,%d", &v0, &v1) == 2)) { c_shmask.arr.iv_x = v0-1; c_shmask.arr.iv_y = v1-1; arr_size_loaded = 1; } else if (arr_size_loaded && (v1 > 0)) { p = c_shmask.arr.iv_y+1-v1; if (sscanf(tmpbuf, "%hx,%hx,%hx,%hx,%hx,%hx,%hx,%hx,%hx,%hx,%hx,%hx,%hx,%hx,%hx,%hx", &c_shmask.arr.v[p][0], &c_shmask.arr.v[p][1], &c_shmask.arr.v[p][2], &c_shmask.arr.v[p][3], &c_shmask.arr.v[p][4], &c_shmask.arr.v[p][5], &c_shmask.arr.v[p][6], &c_shmask.arr.v[p][7], &c_shmask.arr.v[p][8], &c_shmask.arr.v[p][9], &c_shmask.arr.v[p][10], &c_shmask.arr.v[p][11], &c_shmask.arr.v[p][12], &c_shmask.arr.v[p][13], &c_shmask.arr.v[p][14], &c_shmask.arr.v[p][15]) == v0) v1--; } } file_close(&f_shmask); } f_chdir("/"); sniprintf(c_shmask.name, sizeof(c_shmask.name), "C: %s", filename); shmask_loaded_array = -1; update_sc_config(); return 0; } int load_lc_nes_pal(char *dirname, char *filename) { FIL f_nes_pal; int i; char dirname_root[10]; unsigned bytes_read; if (!sdcard_dev.mount) { if (file_mount() != 0) { printf("SD card not detected\n"); return -1; } } sniprintf(dirname_root, sizeof(dirname_root), "/%s", dirname); f_chdir(dirname_root); if (!file_open(&f_nes_pal, filename)) { if (f_read(&f_nes_pal, tmpbuf, 192, &bytes_read) == FR_OK) { memset(c_lc_palette_set.pal.nes_pal, 0x00, sizeof(c_lc_palette_set.pal.nes_pal)); for (i=0x00; i<=0x0C; i++) c_lc_palette_set.pal.nes_pal[i+8] = (tmpbuf[3*i]<<16) | (tmpbuf[3*i+1]<<8) | tmpbuf[3*i+2]; for (i=0x10; i<=0x1C; i++) c_lc_palette_set.pal.nes_pal[i+6] = (tmpbuf[3*i]<<16) | (tmpbuf[3*i+1]<<8) | tmpbuf[3*i+2]; for (i=0x20; i<=0x2D; i++) c_lc_palette_set.pal.nes_pal[i+4] = (tmpbuf[3*i]<<16) | (tmpbuf[3*i+1]<<8) | tmpbuf[3*i+2]; for (i=0x30; i<=0x3D; i++) c_lc_palette_set.pal.nes_pal[i+2] = (tmpbuf[3*i]<<16) | (tmpbuf[3*i+1]<<8) | tmpbuf[3*i+2]; } file_close(&f_nes_pal); } f_chdir("/"); sniprintf(c_lc_palette_set.name, sizeof(c_lc_palette_set.name), "C: %s", filename); loaded_lc_palette = -1; update_sc_config(); return 0; } int load_lc_palette_set(char *dirname, char *filename) { FIL f_lc_palset; char dirname_root[10]; int entries_remaining=0; int offset; if (!sdcard_dev.mount) { if (file_mount() != 0) { printf("SD card not detected\n"); return -1; } } sniprintf(dirname_root, sizeof(dirname_root), "/%s", dirname); f_chdir(dirname_root); if (!file_open(&f_lc_palset, filename)) { while (file_get_string(&f_lc_palset, tmpbuf, sizeof(tmpbuf))) { // strip CR / CRLF tmpbuf[strcspn(tmpbuf, "\r\n")] = 0; // Skip empty / comment lines if ((tmpbuf[0] == 0) || (tmpbuf[0] == '#')) continue; if (!entries_remaining) { if (strncmp(tmpbuf, "c64_pal", 10) == 0) { offset = offsetof(lc_palette_set, c64_pal)/4; entries_remaining = 16; } else if (strncmp(tmpbuf, "zx_pal", 10) == 0) { offset = offsetof(lc_palette_set, zx_pal)/4; entries_remaining = 16; } else if (strncmp(tmpbuf, "msx_pal", 10) == 0) { offset = offsetof(lc_palette_set, msx_pal)/4; entries_remaining = 16; } else if (strncmp(tmpbuf, "intv_pal", 10) == 0) { offset = offsetof(lc_palette_set, intv_pal)/4; entries_remaining = 16; } else if (strncmp(tmpbuf, "nes_pal", 10) == 0) { offset = offsetof(lc_palette_set, nes_pal)/4; entries_remaining = 64; } else if (strncmp(tmpbuf, "tia_pal", 10) == 0) { offset = offsetof(lc_palette_set, tia_pal)/4; entries_remaining = 128; } else if (strncmp(tmpbuf, "gtia_pal", 10) == 0) { offset = offsetof(lc_palette_set, gtia_pal)/4; entries_remaining = 256; } else if (strncmp(tmpbuf, "maria_pal", 10) == 0) { offset = offsetof(lc_palette_set, maria_pal)/4; entries_remaining = 256; } else if (strncmp(tmpbuf, "gtia_pal", 10) == 0) { offset = offsetof(lc_palette_set, gtia_pal)/4; entries_remaining = 128; } else if (strncmp(tmpbuf, "sms_pal", 10) == 0) { offset = offsetof(lc_palette_set, sms_pal)/4; entries_remaining = 64; } else if (strncmp(tmpbuf, "vic20_pal", 10) == 0) { offset = offsetof(lc_palette_set, vic20_pal)/4; entries_remaining = 16; } else if (strncmp(tmpbuf, "g7000_pal", 10) == 0) { offset = offsetof(lc_palette_set, g7000_pal)/4; entries_remaining = 16; } else if (strncmp(tmpbuf, "mc6847_pal", 10) == 0) { offset = offsetof(lc_palette_set, mc6847_pal)/4; entries_remaining = 16; } } else if (sscanf(tmpbuf, "%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx,%lx", &c_lc_palette_set.pal.data[offset], &c_lc_palette_set.pal.data[offset+1], &c_lc_palette_set.pal.data[offset+2], &c_lc_palette_set.pal.data[offset+3], &c_lc_palette_set.pal.data[offset+4], &c_lc_palette_set.pal.data[offset+5], &c_lc_palette_set.pal.data[offset+6], &c_lc_palette_set.pal.data[offset+7], &c_lc_palette_set.pal.data[offset+8], &c_lc_palette_set.pal.data[offset+9], &c_lc_palette_set.pal.data[offset+10], &c_lc_palette_set.pal.data[offset+11], &c_lc_palette_set.pal.data[offset+12], &c_lc_palette_set.pal.data[offset+13], &c_lc_palette_set.pal.data[offset+14], &c_lc_palette_set.pal.data[offset+15]) == 16) { offset += 16; entries_remaining -= 16; } } file_close(&f_lc_palset); } f_chdir("/"); sniprintf(c_lc_palette_set.name, sizeof(c_lc_palette_set.name), "C: %s", filename); loaded_lc_palette = -1; update_sc_config(); return 0; } void cstm_file_load(menucode_id code, int setup_disp, char *dir, char *pattern, load_func load_f) { uint32_t row_mask[2] = {0x03, 0x00}; int i, retval, items_curpage; static int file_load_nav = 0; static int file_load_page = 0; static int files_found; FILINFO fno; if (!sdcard_dev.mount) { if (file_mount() != 0) { sniprintf(menu_row1, LCD_ROW_LEN+1, "No SD card"); sniprintf(menu_row2, LCD_ROW_LEN+1, "detected"); ui_disp_menu(1); return; } } if (setup_disp) { files_found = find_files_exec(dir, pattern, &fno, 0, 99, NULL); printf("%d %s files found\n", files_found, dir); if (file_load_page*20+file_load_nav > files_found) { file_load_page = 0; file_load_nav = 0; } } if (files_found == 0) { sniprintf(menu_row1, LCD_ROW_LEN+1, "No %s/%s", dir, pattern); sniprintf(menu_row2, LCD_ROW_LEN+1, "files found"); ui_disp_menu(1); return; } // Parse menu control switch (code) { case PREV_PAGE: file_load_nav--; break; case NEXT_PAGE: file_load_nav++; break; case VAL_MINUS: file_load_page = (file_load_page == 0) ? ((files_found-1)/20) : (file_load_page - 1); setup_disp = 1; break; case VAL_PLUS: file_load_page = (file_load_page + 1) % (((files_found-1)/20)+1); setup_disp = 1; break; case OPT_SELECT: find_files_exec(dir, pattern, &fno, 0, file_load_page*20+file_load_nav, NULL); retval = load_f(dir, fno.fname); setup_disp = 1; break; default: break; } if (file_load_page > (files_found/20)) file_load_page = 0; items_curpage = files_found-file_load_page*20; if (items_curpage > 20) items_curpage = 20; if (file_load_nav < 0) file_load_nav = items_curpage-1; else if (file_load_nav >= items_curpage) file_load_nav = 0; if (setup_disp) { memset((void*)osd->osd_array.data, 0, sizeof(osd_char_array)); sniprintf(menu_row1, LCD_ROW_LEN+1, "%s p. %d/%d", dir, file_load_page+1, ((files_found-1)/20)+1); strncpy((char*)osd->osd_array.data[0][0], menu_row1, OSD_CHAR_COLS); for (i=0; iosd_array.data[1][0][i] = '-'; osd_list_iter = 0; find_files_exec(dir, pattern, &fno, file_load_page*20, file_load_page*20+items_curpage-1, osd_array_fname_fill); for (i=0; iosd_array.data[items_curpage+3][0], OSD_CHAR_COLS, "< Prev Next >"); row_mask[0] |= (3<<(items_curpage+2)); if (code == OPT_SELECT) { if (retval == 0) strlcpy(menu_row2, "Done", LCD_ROW_LEN+1); else if (retval < 0) sniprintf(menu_row2, LCD_ROW_LEN+1, "Failed (%d)", retval); else strlcpy(menu_row2, func_ret_status, LCD_ROW_LEN+1); strncpy((char*)osd->osd_array.data[file_load_nav+2][1], menu_row2, OSD_CHAR_COLS); row_mask[1] = (1<<(file_load_nav+2)); } osd->osd_sec_enable[0].mask = row_mask[0]; osd->osd_sec_enable[1].mask = row_mask[1]; } osd->osd_row_color.mask = (1<<(file_load_nav+2)); if (code != OPT_SELECT) sniprintf(menu_row2, LCD_ROW_LEN+1, "%s", &tmpbuf[file_load_nav*24]); ui_disp_menu(0); } void cstm_shmask_load(menucode_id code, int setup_disp) { cstm_file_load(code, setup_disp, "shmask", "*.txt", load_shmask); } void cstm_lc_palette_set_load(menucode_id code, int setup_disp) { switch (palset_type_sel) { case 0: cstm_file_load(code, setup_disp, "lumacode", "*.txt", load_lc_palette_set); break; case 1: default: cstm_file_load(code, setup_disp, "lumacode", "*.pal", load_lc_nes_pal); break; } } void cstm_fw_update(menucode_id code, int setup_disp) { cstm_file_load(code, setup_disp, "fw", "*.bin", fw_update); } void enter_cstm(const menuitem_t *item, int detached_mode) { if (detached_mode) { navlvl = 0; menu_active = 1; osd->osd_config.menu_active = menu_active; } /*if (item->type == OPT_AVCONFIG_SELECTION) { lw_item = item; cstm_f = cstm_listview; } else {*/ cstm_f = item->cstm.cstm_f; //} cstm_f(NO_ACTION, 1); } static void vm_select() { vm_edit = vm_sel; tc_h_samplerate = video_modes_plm[vm_edit].timings.h_total; tc_h_samplerate_adj = (uint16_t)video_modes_plm[vm_edit].timings.h_total_adj; tc_h_synclen = (uint16_t)video_modes_plm[vm_edit].timings.h_synclen; tc_h_bporch = video_modes_plm[vm_edit].timings.h_backporch; tc_h_active = video_modes_plm[vm_edit].timings.h_active; tc_v_synclen = (uint16_t)video_modes_plm[vm_edit].timings.v_synclen; tc_v_bporch = video_modes_plm[vm_edit].timings.v_backporch; tc_v_active = video_modes_plm[vm_edit].timings.v_active; tc_h_mask = (uint16_t)video_modes_plm[vm_edit].mask.h; tc_v_mask = (uint16_t)video_modes_plm[vm_edit].mask.v; tc_sampler_phase = video_modes_plm[vm_edit].sampler_phase; } static void vm_tweak(uint16_t *v) { int active_mode = (cm.sync_active && (cm.id == vm_edit)); if (active_mode) { if ((video_modes_plm[cm.id].timings.h_total != tc_h_samplerate) || (video_modes_plm[cm.id].timings.h_total_adj != (uint8_t)tc_h_samplerate_adj) || (video_modes_plm[cm.id].timings.h_synclen != (uint8_t)tc_h_synclen) || (video_modes_plm[cm.id].timings.h_backporch != tc_h_bporch) || (video_modes_plm[cm.id].timings.h_active != tc_h_active) || (video_modes_plm[cm.id].timings.v_synclen != (uint8_t)tc_v_synclen) || (video_modes_plm[cm.id].timings.v_backporch != tc_v_bporch) || (video_modes_plm[cm.id].timings.v_active != tc_v_active) || (video_modes_plm[cm.id].mask.h != tc_h_mask) || (video_modes_plm[cm.id].mask.v != tc_v_mask)) update_cur_vm = 1; if (video_modes_plm[cm.id].sampler_phase != tc_sampler_phase) set_sampler_phase(tc_sampler_phase, 1); } video_modes_plm[vm_edit].timings.h_total = tc_h_samplerate; video_modes_plm[vm_edit].timings.h_total_adj = (uint8_t)tc_h_samplerate_adj; video_modes_plm[vm_edit].timings.h_synclen = (uint8_t)tc_h_synclen; video_modes_plm[vm_edit].timings.h_backporch = tc_h_bporch; video_modes_plm[vm_edit].timings.h_active = tc_h_active; video_modes_plm[vm_edit].timings.v_synclen = (uint8_t)tc_v_synclen; video_modes_plm[vm_edit].timings.v_backporch = tc_v_bporch; video_modes_plm[vm_edit].timings.v_active = tc_v_active; video_modes_plm[vm_edit].mask.h = tc_h_mask; video_modes_plm[vm_edit].mask.v = tc_v_mask; video_modes_plm[vm_edit].sampler_phase = tc_sampler_phase; if (v == &tc_sampler_phase) sampler_phase_disp(*v); else if ((v == &tc_h_samplerate) || (v == &tc_h_samplerate_adj)) sniprintf(menu_row2, LCD_ROW_LEN+1, "%u.%.2u", video_modes_plm[vm_edit].timings.h_total, video_modes_plm[vm_edit].timings.h_total_adj*5); else sniprintf(menu_row2, LCD_ROW_LEN+1, "%u", *v); }