From 4cee9159d0c33301a5adaa2fabdae2a1c9c0c692 Mon Sep 17 00:00:00 2001 From: Sorgelig Date: Fri, 26 Nov 2021 14:46:38 +0800 Subject: [PATCH] Support for loadable Shadow Masks. --- MiSTer.ini | 7 ++ cfg.cpp | 2 + cfg.h | 2 + file_io.cpp | 62 ++++++++++++ file_io.h | 14 +++ menu.cpp | 74 +++++++++++--- user_io.h | 1 + video.cpp | 276 ++++++++++++++++++++++++++++++++++++---------------- video.h | 5 + 9 files changed, 345 insertions(+), 98 deletions(-) diff --git a/MiSTer.ini b/MiSTer.ini index aa803ff..caafd42 100644 --- a/MiSTer.ini +++ b/MiSTer.ini @@ -177,3 +177,10 @@ bt_auto_disconnect=0 ; Some dongles (mostly CSR) have problem to pair with BLE if not reset in advance. ; Consequence of reset: some input devices get shutdown after reset. bt_reset_before_pair=0 + +;default Shadow Mask +;shmask_default=VGA.txt + +;default shadow mask mode: +; 0 - none, 1 - 1x, 2 - 2x, 3 - 1x Rotated, 4 - 2x Rotated +;shmask_mode_default=1 diff --git a/cfg.cpp b/cfg.cpp index 9161038..4347bbe 100644 --- a/cfg.cpp +++ b/cfg.cpp @@ -83,6 +83,8 @@ static const ini_var_t ini_vars[] = { "SPINNER_THROTTLE", (void*)(&(cfg.spinner_throttle)), INT32, -10000, 10000 }, { "AFILTER_DEFAULT", (void*)(&(cfg.afilter_default)), STRING, 0, sizeof(cfg.afilter_default) - 1 }, { "VFILTER_DEFAULT", (void*)(&(cfg.vfilter_default)), STRING, 0, sizeof(cfg.vfilter_default) - 1 }, + { "SHMASK_DEFAULT", (void*)(&(cfg.shmask_default)), STRING, 0, sizeof(cfg.shmask_default) - 1 }, + { "SHMASK_MODE_DEFAULT", (void*)(&(cfg.shmask_mode_default)), UINT8, 0, 255 }, { "LOG_FILE_ENTRY", (void*)(&(cfg.log_file_entry)), UINT8, 0, 1 }, { "BT_AUTO_DISCONNECT", (void*)(&(cfg.bt_auto_disconnect)), UINT32, 0, 180 }, { "BT_RESET_BEFORE_PAIR", (void*)(&(cfg.bt_reset_before_pair)), UINT8, 0, 1 }, diff --git a/cfg.h b/cfg.h index 4b07324..3975c05 100644 --- a/cfg.h +++ b/cfg.h @@ -53,6 +53,7 @@ typedef struct { uint8_t browse_expand; uint8_t logo; uint8_t log_file_entry; + uint8_t shmask_mode_default; int bt_auto_disconnect; int bt_reset_before_pair; char bootcore[256]; @@ -64,6 +65,7 @@ typedef struct { char custom_aspect_ratio[2][16]; char afilter_default[1023]; char vfilter_default[1023]; + char shmask_default[1023]; } cfg_t; extern cfg_t cfg; diff --git a/file_io.cpp b/file_io.cpp index 135e778..b647901 100644 --- a/file_io.cpp +++ b/file_io.cpp @@ -1769,3 +1769,65 @@ bool isMraName(char *path) return (spl && !strcmp(spl, ".mra")); } +fileTextReader::fileTextReader() +{ + buffer = nullptr; +} + +fileTextReader::~fileTextReader() +{ + if( buffer != nullptr ) + { + free(buffer); + } + buffer = nullptr; +} + +bool FileOpenTextReader( fileTextReader *reader, const char *filename ) +{ + fileTYPE f; + + // ensure buffer is freed if the reader is being reused + reader->~fileTextReader(); + + if (FileOpen(&f, filename)) + { + char *buf = (char*)malloc(f.size+1); + if (buf) + { + memset(buf, 0, f.size + 1); + int size; + if ((size = FileReadAdv(&f, buf, f.size))) + { + reader->size = f.size; + reader->buffer = buf; + reader->pos = reader->buffer; + return true; + } + } + } + return false; +} + +const char *FileReadLine(fileTextReader *reader) +{ + const char *end = reader->buffer + reader->size; + while (reader->pos < end) + { + char *st = reader->pos; + while ((reader->pos < end) && *reader->pos && (*reader->pos != 10)) + reader->pos++; + *reader->pos = 0; + while (*st == ' ' || *st == '\t' || *st == 13) + st++; + if (*st == '#' || *st == ';' || !*st) + { + reader->pos++; + } + else + { + return st; + } + } + return nullptr; +} \ No newline at end of file diff --git a/file_io.h b/file_io.h index 360bde0..562524c 100644 --- a/file_io.h +++ b/file_io.h @@ -34,6 +34,16 @@ struct direntext_t char altname[256]; }; +struct fileTextReader +{ + fileTextReader(); + ~fileTextReader(); + + size_t size; + char *buffer; + char *pos; +}; + int flist_nDirEntries(); int flist_iFirstEntry(); void flist_iFirstEntryInc(); @@ -120,11 +130,15 @@ const char *getFullPath(const char *name); uint32_t getFileType(const char *name); bool isMraName(char *path); +bool FileOpenTextReader(fileTextReader *reader, const char *path); +const char* FileReadLine(fileTextReader *reader); + #define LOADBUF_SZ (1024*1024) #define COEFF_DIR "filters" #define GAMMA_DIR "gamma" #define AFILTER_DIR "filters_audio" +#define SMASK_DIR "shadow_masks" #define GAMES_DIR "games" #define CIFS_DIR "cifs" diff --git a/menu.cpp b/menu.cpp index f93a378..3dd494c 100644 --- a/menu.cpp +++ b/menu.cpp @@ -132,6 +132,7 @@ enum MENU MENU_COEFF_FILE_SELECTED, MENU_GAMMA_FILE_SELECTED, MENU_AFILTER_FILE_SELECTED, + MENU_SMASK_FILE_SELECTED, // Generic MENU_GENERIC_MAIN1, @@ -212,6 +213,7 @@ const char *config_uart_msg[] = { " None", " PPP", " Console", " const char *config_midilink_mode[] = {"Local", "Local", " USB", " UDP", "-----", "-----", " USB" }; const char *config_scaler_msg[] = { "Internal","Custom" }; const char *config_afilter_msg[] = { "Internal","Custom" }; +const char *config_smask_msg[] = { "None", "1x", "2x", "1x Rotated", "2x Rotated" }; const char *config_gamma_msg[] = { "Off","On" }; const char *config_scale[] = { "Normal", "V-Integer", "HV-Integer-", "HV-Integer+", "HV-Integer", "???", "???", "???" }; @@ -2244,7 +2246,7 @@ void HandleUI(void) while(1) { n = 0; - menumask = 0xf80F; + menumask = 0x3800f; if (!menusub) firstmenu = 0; adjvisible = 0; @@ -2325,24 +2327,40 @@ void HandleUI(void) MenuWrite(n++, s, menusub == 10, !audio_filter_en() || !S_ISDIR(getFileType(AFILTER_DIR))); } - if (is_minimig() || is_st()) - { - menumask &= ~0x1800; - } - else + if (video_get_shadow_mask_mode() >= 0) { MenuWrite(n++); - MenuWrite(n++, " Reset settings", menusub == 11, is_archie()); - MenuWrite(n++, " Save settings", menusub == 12, 0); + menumask |= 0x1800; + sprintf(s, " Shadow Mask - %s", config_smask_msg[video_get_shadow_mask_mode()]); + MenuWrite(n++, s, menusub == 11); + + memset(s, 0, sizeof(s)); + s[0] = ' '; + if (strlen(video_get_shadow_mask())) strncpy(s + 1, video_get_shadow_mask(), 25); + else strcpy(s, " < none >"); + + while (strlen(s) < 26) strcat(s, " "); + strcat(s, " \x16 "); + + MenuWrite(n++, s, menusub == 12, !video_get_shadow_mask_mode() || !S_ISDIR(getFileType(SMASK_DIR))); + } + + + if (!is_minimig() && !is_st()) + { + menumask |= 0x6000; + MenuWrite(n++); + MenuWrite(n++, " Reset settings", menusub == 13, is_archie()); + MenuWrite(n++, " Save settings", menusub == 14, 0); } MenuWrite(n++); cr = n; - MenuWrite(n++, " Reboot (hold \x16 cold reboot)", menusub == 13); - MenuWrite(n++, " About", menusub == 14); + MenuWrite(n++, " Reboot (hold \x16 cold reboot)", menusub == 15); + MenuWrite(n++, " About", menusub == 16); while(n < OsdGetSize() - 1) MenuWrite(n++); - MenuWrite(n++, STD_EXIT, menusub == 15, 0, OSD_ARROW_LEFT); + MenuWrite(n++, STD_EXIT, menusub == 17, 0, OSD_ARROW_LEFT); sysinfo_timer = 0; if (!adjvisible) break; @@ -2466,6 +2484,18 @@ void HandleUI(void) } break; case 11: + video_set_shadow_mask_mode(video_get_shadow_mask_mode() + 1); + menustate = MENU_COMMON1; + break; + case 12: + if (video_get_shadow_mask_mode()) + { + snprintf(Selected_tmp, sizeof(Selected_tmp), SMASK_DIR"/%s", video_get_shadow_mask()); + if (!FileExists(Selected_tmp)) snprintf(Selected_tmp, sizeof(Selected_tmp), SMASK_DIR); + SelectFile(Selected_tmp, 0, SCANO_DIR | SCANO_TXT, MENU_SMASK_FILE_SELECTED, MENU_COMMON1); + } + break; + case 13: if (!is_archie()) { menustate = MENU_RESET1; @@ -2478,7 +2508,7 @@ void HandleUI(void) } break; - case 12: + case 14: // Save settings menustate = MENU_GENERIC_MAIN1; menusub = 0; @@ -2503,7 +2533,7 @@ void HandleUI(void) } break; - case 13: + case 15: { reboot_req = 1; @@ -2516,7 +2546,7 @@ void HandleUI(void) } break; - case 14: + case 16: menustate = MENU_ABOUT1; menusub = 0; break; @@ -3056,6 +3086,20 @@ void HandleUI(void) } break; + case MENU_SMASK_FILE_SELECTED: + { + char *p = strcasestr(selPath, SMASK_DIR"/"); + if (!p) video_set_shadow_mask(selPath); + else + { + p += strlen(SMASK_DIR); + while (*p == '/') p++; + video_set_shadow_mask(p); + } + menustate = MENU_COMMON1; + } + break; + case MENU_MISC1: OsdSetSize(16); helptext_idx = 0; @@ -3559,7 +3603,7 @@ void HandleUI(void) if (menu | select | left) { menustate = MENU_COMMON1; - menusub = 14; + menusub = 16; } break; diff --git a/user_io.h b/user_io.h index 1e5cd09..58eb975 100644 --- a/user_io.h +++ b/user_io.h @@ -69,6 +69,7 @@ #define UIO_SET_UART 0x3B #define UIO_CHK_UPLOAD 0x3C #define UIO_ASTICK_2 0x3D +#define UIO_SHADOWMASK 0x3E // codes as used by 8bit for file loading from OSD #define FIO_FILE_TX 0x53 diff --git a/video.cpp b/video.cpp index f5ae798..b60937d 100644 --- a/video.cpp +++ b/video.cpp @@ -203,7 +203,7 @@ static char new_scaler = 0; static void setScaler() { - fileTYPE f = {}; + fileTextReader reader = {}; static char filename[1024]; uint32_t arc[4] = {}; @@ -234,52 +234,32 @@ static void setScaler() DisableIO(); snprintf(filename, sizeof(filename), COEFF_DIR"/%s", scaler_flt_cfg + 1); - if (FileOpen(&f, filename)) + if (FileOpenTextReader(&reader, filename)) { //printf("Read scaler coefficients\n"); - char *buf = (char*)malloc(f.size+1); - if (buf) + spi_uio_cmd_cont(UIO_SET_FLTCOEF); + + int phase = 0; + const char *line; + while ((line = FileReadLine(&reader))) { - memset(buf, 0, f.size + 1); - int size; - if ((size = FileReadAdv(&f, buf, f.size))) + int c0, c1, c2, c3; + int n = sscanf(line, "%d,%d,%d,%d", &c0, &c1, &c2, &c3); + if (n == 4) { - spi_uio_cmd_cont(UIO_SET_FLTCOEF); + //printf(" phase %c-%02d: %4d,%4d,%4d,%4d\n", (phase >= 16) ? 'V' : 'H', phase % 16, c0, c1, c2, c3); + //printf("%03X: %03X %03X %03X %03X;\n",phase*4, c0 & 0x1FF, c1 & 0x1FF, c2 & 0x1FF, c3 & 0x1FF); - char *end = buf + size; - char *pos = buf; - int phase = 0; - while (pos < end) - { - char *st = pos; - while ((pos < end) && *pos && (*pos != 10)) pos++; - *pos = 0; - while (*st == ' ' || *st == '\t' || *st == 13) st++; - if (*st == '#' || *st == ';' || !*st) pos++; - else - { - int c0, c1, c2, c3; - int n = sscanf(st, "%d,%d,%d,%d", &c0, &c1, &c2, &c3); - if (n == 4) - { - //printf(" phase %c-%02d: %4d,%4d,%4d,%4d\n", (phase >= 16) ? 'V' : 'H', phase % 16, c0, c1, c2, c3); - //printf("%03X: %03X %03X %03X %03X;\n",phase*4, c0 & 0x1FF, c1 & 0x1FF, c2 & 0x1FF, c3 & 0x1FF); + spi_w((c0 & 0x1FF) | (((phase * 4) + 0) << 9)); + spi_w((c1 & 0x1FF) | (((phase * 4) + 1) << 9)); + spi_w((c2 & 0x1FF) | (((phase * 4) + 2) << 9)); + spi_w((c3 & 0x1FF) | (((phase * 4) + 3) << 9)); - spi_w((c0 & 0x1FF) | (((phase * 4) + 0) << 9)); - spi_w((c1 & 0x1FF) | (((phase * 4) + 1) << 9)); - spi_w((c2 & 0x1FF) | (((phase * 4) + 2) << 9)); - spi_w((c3 & 0x1FF) | (((phase * 4) + 3) << 9)); - - phase++; - if (phase >= 32) break; - } - } - } - DisableIO(); + phase++; + if (phase >= 32) break; } - - free(buf); } + DisableIO(); } } @@ -330,7 +310,7 @@ static char has_gamma = 0; static void setGamma() { - fileTYPE f = {}; + fileTextReader reader = {}; static char filename[1024]; if (!spi_uio_cmd_cont(UIO_SET_GAMMA)) @@ -344,57 +324,38 @@ static void setGamma() DisableIO(); snprintf(filename, sizeof(filename), GAMMA_DIR"/%s", gamma_cfg + 1); - if (FileOpen(&f, filename)) + if (FileOpenTextReader(&reader, filename)) { - char *buf = (char*)malloc(f.size+1); - if (buf) + spi_uio_cmd_cont(UIO_SET_GAMCURV); + + const char *line; + int index = 0; + while ((line = FileReadLine(&reader))) { - memset(buf, 0, f.size + 1); - int size; - if ((size = FileReadAdv(&f, buf, f.size))) + int c0, c1, c2; + int n = sscanf(line, "%d,%d,%d", &c0, &c1, &c2); + if (n == 1) { - spi_uio_cmd_cont(UIO_SET_GAMCURV); - - char *end = buf + size; - char *pos = buf; - int index = 0; - while (pos < end) - { - char *st = pos; - while ((pos < end) && *pos && (*pos != 10)) pos++; - *pos = 0; - while (*st == ' ' || *st == '\t' || *st == 13) st++; - if (*st == '#' || *st == ';' || !*st) pos++; - else - { - int c0, c1, c2; - int n = sscanf(st, "%d,%d,%d", &c0, &c1, &c2); - if (n == 1) - { - c1 = c0; - c2 = c0; - n = 3; - } - - if (n == 3) - { - spi_w((index << 8) | (c0 & 0xFF)); - spi_w((index << 8) | (c1 & 0xFF)); - spi_w((index << 8) | (c2 & 0xFF)); - - index++; - if (index >= 256) break; - } - } - } - DisableIO(); - spi_uio_cmd8(UIO_SET_GAMMA, gamma_cfg[0]); + c1 = c0; + c2 = c0; + n = 3; } - free(buf); + if (n == 3) + { + spi_w((index << 8) | (c0 & 0xFF)); + spi_w((index << 8) | (c1 & 0xFF)); + spi_w((index << 8) | (c2 & 0xFF)); + + index++; + if (index >= 256) break; + } } + DisableIO(); + spi_uio_cmd8(UIO_SET_GAMMA, gamma_cfg[0]); } } + int video_get_gamma_en() { return has_gamma ? gamma_cfg[0] : -1; @@ -431,6 +392,152 @@ static void loadGammaCfg() } } +static char shadow_mask_cfg[1024] = { 0 }; +static bool has_shadow_mask = false; + +#define SM_FLAG_ENABLED ( 1 << 0 ) +#define SM_FLAG_2X ( 1 << 1 ) +#define SM_FLAG_ROTATED ( 1 << 2 ) + +#define SM_FLAG(v) ( ( 0x0 << 13 ) | ( (v) & 0x7 ) ) +#define SM_VMAX(v) ( ( 0x1 << 13 ) | ( (v) & 0xf ) ) +#define SM_HMAX(v) ( ( 0x2 << 13 ) | ( (v) & 0xf ) ) +#define SM_LUT(o,v) ( ( 0x3 << 13 ) | ( ( (o) & 0x3f ) << 4 ) | ( (v) & 0x7 ) ) + +enum +{ + SM_MODE_NONE = 0, + SM_MODE_1X, + SM_MODE_2X, + SM_MODE_1X_ROTATED, + SM_MODE_2X_ROTATED, + SM_MODE_COUNT +}; + +static void setShadowMask() +{ + static char filename[1024]; + + if (!spi_uio_cmd_cont(UIO_SHADOWMASK)) + { + DisableIO(); + has_shadow_mask = false; + return; + } + + has_shadow_mask = true; + + switch( video_get_shadow_mask_mode() ) + { + default: spi_w(SM_FLAG(0)); break; + case SM_MODE_1X: spi_w(SM_FLAG(SM_FLAG_ENABLED)); break; + case SM_MODE_2X: spi_w(SM_FLAG(SM_FLAG_ENABLED | SM_FLAG_2X)); break; + case SM_MODE_1X_ROTATED: spi_w(SM_FLAG(SM_FLAG_ENABLED | SM_FLAG_ROTATED)); break; + case SM_MODE_2X_ROTATED: spi_w(SM_FLAG(SM_FLAG_ENABLED | SM_FLAG_ROTATED | SM_FLAG_2X)); break; + } + + snprintf(filename, sizeof(filename), SMASK_DIR"/%s", shadow_mask_cfg + 1); + + fileTextReader reader; + if( FileOpenTextReader( &reader, filename ) ) + { + int w = -1, h = -1; + int y = 0; + + const char *line; + while ((line = FileReadLine( &reader ))) + { + if( w == -1 ) + { + int n = sscanf(line, "%d,%d", &w, &h); + if( (n != 2) || (w <= 0) || ( w <= 0 ) ) + { + spi_w(SM_FLAG(0)); + break; + } + } + else + { + unsigned int p[8]; + int n = sscanf(line, "%u,%u,%u,%u,%u,%u,%u,%u", p+0, p+1, p+2, p+3, p+4, p+5, p+6, p+7); + if( n != w ) + { + spi_w(SM_FLAG(0)); + break; + } + + for( int x = 0; x < w; x++ ) + { + int offset = x + ( y * 8 ); + spi_w( SM_LUT(offset, p[x]) ); + } + + y += 1; + + if( y == h ) break; + } + } + + if( y == h ) + { + spi_w(SM_HMAX(w - 1)); + spi_w(SM_VMAX(h - 1)); + } + } + + DisableIO(); +} + +int video_get_shadow_mask_mode() +{ + return has_shadow_mask ? shadow_mask_cfg[0] : -1; +} + +char* video_get_shadow_mask() +{ + return shadow_mask_cfg + 1; +} + +static char shadow_mask_cfg_path[1024] = { 0 }; + +void video_set_shadow_mask_mode(int n) +{ + if( n >= SM_MODE_COUNT ) + { + n = 0; + } + + shadow_mask_cfg[0] = (char)n; + FileSaveConfig(shadow_mask_cfg_path, &shadow_mask_cfg, sizeof(shadow_mask_cfg)); + setShadowMask(); +} + +void video_set_shadow_mask(char *name) +{ + strcpy(shadow_mask_cfg + 1, name); + FileSaveConfig(shadow_mask_cfg_path, &shadow_mask_cfg, sizeof(shadow_mask_cfg)); + setShadowMask(); + user_io_send_buttons(1); +} + +static void loadShadowMaskCfg() +{ + sprintf(shadow_mask_cfg_path, "%s_shmask.cfg", user_io_get_core_name()); + if (!FileLoadConfig(shadow_mask_cfg_path, &shadow_mask_cfg, sizeof(shadow_mask_cfg) - 1)) + { + memset(shadow_mask_cfg, 0, sizeof(shadow_mask_cfg)); + if (cfg.shmask_default[0]) + { + strcpy(shadow_mask_cfg + 1, cfg.shmask_default); + shadow_mask_cfg[0] = cfg.shmask_mode_default; + } + } + + if( shadow_mask_cfg[0] >= SM_MODE_COUNT ) + { + shadow_mask_cfg[0] = 0; + } +} static char fb_reset_cmd[128] = {}; static void set_video(vmode_custom_t *v, double Fpix) @@ -441,6 +548,9 @@ static void set_video(vmode_custom_t *v, double Fpix) loadScalerCfg(); setScaler(); + loadShadowMaskCfg(); + setShadowMask(); + v_cur = *v; vmode_custom_t v_fix = v_cur; diff --git a/video.h b/video.h index 636b2e1..06f2c87 100644 --- a/video.h +++ b/video.h @@ -11,6 +11,11 @@ void video_set_gamma_en(int n); char* video_get_gamma_curve(); void video_set_gamma_curve(char *name); +int video_get_shadow_mask_mode(); +void video_set_shadow_mask_mode(int n); +char* video_get_shadow_mask(); +void video_set_shadow_mask(char *name); + void video_mode_load(); void video_mode_adjust();