diff --git a/MiSTer.vcxproj b/MiSTer.vcxproj index 0fe5bc9..f22e153 100644 --- a/MiSTer.vcxproj +++ b/MiSTer.vcxproj @@ -53,6 +53,7 @@ + @@ -90,6 +91,7 @@ + diff --git a/MiSTer.vcxproj.filters b/MiSTer.vcxproj.filters index e783c6e..577c994 100644 --- a/MiSTer.vcxproj.filters +++ b/MiSTer.vcxproj.filters @@ -133,6 +133,9 @@ Source Files + + Source Files + @@ -267,5 +270,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/cheats.cpp b/cheats.cpp new file mode 100644 index 0000000..9e0c55a --- /dev/null +++ b/cheats.cpp @@ -0,0 +1,322 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hardware.h" +#include "file_io.h" +#include "user_io.h" +#include "fpga_io.h" +#include "miniz_zip.h" +#include "osd.h" +#include "cheats.h" + +struct cheat_rec_t +{ + char enabled; + char name[256]; +}; + +typedef std::vector CheatVector; +static CheatVector cheats; + +static int iSelectedEntry = 0; +static int iFirstEntry = 0; + +struct CheatComp +{ + bool operator()(const cheat_rec_t& ce1, const cheat_rec_t& ce2) + { + int len1 = strlen(ce1.name); + int len2 = strlen(ce2.name); + + int len = (len1 < len2) ? len1 : len2; + int ret = strncasecmp(ce1.name, ce2.name, len); + if (!ret) + { + return len1 < len2; + } + + return ret < 0; + } +}; + +static char cheat_zip[1024] = {}; + +void cheats_init(char *rom_path) +{ + cheats.clear(); + cheat_zip[0] = 0; + + if (!strcasestr(rom_path, ".zip")) + { + sprintf(cheat_zip, "%s/%s", getRootDir(), rom_path); + char *p = strrchr(cheat_zip, '.'); + if (p) *p = 0; + strcat(cheat_zip, ".zip"); + } + + mz_zip_archive _z = {}; + if (!mz_zip_reader_init_file(&_z, cheat_zip, 0)) + { + memset(&_z, 0, sizeof(_z)); + + char *rom_name = strrchr(rom_path, '/'); + if (!rom_name) return; + + sprintf(cheat_zip, "%s/cheats/%s%s", getRootDir(), HomeDir, rom_name); + char *p = strrchr(cheat_zip, '.'); + if (p) *p = 0; + strcat(cheat_zip, ".zip"); + + if (!mz_zip_reader_init_file(&_z, cheat_zip, 0)) + { + printf("no cheat file %s\n", cheat_zip); + return; + } + } + + mz_zip_archive *z = new mz_zip_archive(_z); + for (size_t i = 0; i < mz_zip_reader_get_num_files(z); i++) + { + cheat_rec_t ch = {}; + mz_zip_reader_get_filename(z, i, ch.name, sizeof(ch.name)); + + if (mz_zip_reader_is_file_a_directory(z, i)) + { + continue; + } + + cheats.push_back(ch); + } + + mz_zip_reader_end(z); + delete z; + + std::sort(cheats.begin(), cheats.end(), CheatComp()); + + printf("cheats: %d\n", cheats_available()); + cheats_scan(SCANF_INIT); +} + +int cheats_available() +{ + return cheats.size(); +} + +void cheats_scan(int mode) +{ + if (mode == SCANF_INIT) + { + iFirstEntry = 0; + iSelectedEntry = 0; + } + else + { + if (!cheats_available()) return; + + if (mode == SCANF_END) + { + iSelectedEntry = cheats_available() - 1; + iFirstEntry = iSelectedEntry - OsdGetSize() + 1; + if (iFirstEntry < 0) iFirstEntry = 0; + } + else if (mode == SCANF_NEXT) + { + if (iSelectedEntry + 1 < cheats_available()) // scroll within visible items + { + iSelectedEntry++; + if (iSelectedEntry > iFirstEntry + OsdGetSize() - 1) iFirstEntry = iSelectedEntry - OsdGetSize() + 1; + } + } + else if (mode == SCANF_PREV) + { + if (iSelectedEntry > 0) // scroll within visible items + { + iSelectedEntry--; + if (iSelectedEntry < iFirstEntry) iFirstEntry = iSelectedEntry; + } + } + else if (mode == SCANF_NEXT_PAGE) + { + if (iSelectedEntry < iFirstEntry + OsdGetSize() - 1) + { + iSelectedEntry = iFirstEntry + OsdGetSize() - 1; + if (iSelectedEntry >= cheats_available()) iSelectedEntry = cheats_available() - 1; + } + else + { + iSelectedEntry += OsdGetSize(); + iFirstEntry += OsdGetSize(); + if (iSelectedEntry >= cheats_available()) + { + iSelectedEntry = cheats_available() - 1; + iFirstEntry = iSelectedEntry - OsdGetSize() + 1; + if (iFirstEntry < 0) iFirstEntry = 0; + } + else if (iFirstEntry + OsdGetSize() > cheats_available()) + { + iFirstEntry = cheats_available() - OsdGetSize(); + } + } + } + else if (mode == SCANF_PREV_PAGE) + { + if (iSelectedEntry != iFirstEntry) + { + iSelectedEntry = iFirstEntry; + } + else + { + iFirstEntry -= OsdGetSize(); + if (iFirstEntry < 0) iFirstEntry = 0; + iSelectedEntry = iFirstEntry; + } + } + } +} + +void cheats_scroll_name() +{ + // this function is called periodically when file selection window is displayed + // it checks if predefined period of time has elapsed and scrolls the name if necessary + int len; + int max_len; + static char name[256 + 4]; + + name[0] = 32; + name[1] = cheats[iSelectedEntry].enabled ? 0x1a : 0x1b; + name[2] = 32; + strcpy(name + 3, cheats[iSelectedEntry].name); + + len = strlen(name); // get name length + if (len > 3 && !strncasecmp(name + len - 3, ".gg", 3)) len -= 3; + + max_len = 30; + ScrollText(iSelectedEntry - iFirstEntry, name, 3, len, max_len, 1); +} + +void cheats_print() +{ + int k; + int len; + + static char s[256+4]; + + ScrollReset(); + + for (int i = 0; i < OsdGetSize(); i++) + { + char leftchar = 0; + if (i < cheats_available()) + { + k = iFirstEntry + i; + + s[0] = 32; + s[1] = cheats[k].enabled ? 0x1a : 0x1b; + s[2] = 32; + strcpy(s + 3, cheats[k].name); + + len = strlen(s); // get name length + if (len > 3 && !strncasecmp(s + len - 3, ".gg", 3)) len -= 3; + s[len] = 0; + + if (len > 28) + { + len = 27; // trim display length if longer than 30 characters + s[28] = 22; + } + + s[29] = 0; + + if (!i && k) leftchar = 17; + if ((i == OsdGetSize() - 1) && (k < cheats_available() - 1)) leftchar = 16; + } + else + { + memset(s, ' ', 32); + } + + OsdWriteOffset(i, s, i == (iSelectedEntry - iFirstEntry), 0, 0, leftchar); + } +} + +#define CHEAT_SIZE (128*16) // 128 codes max + +static void cheats_send() +{ + static char filename[1024]; + static uint8_t buff[CHEAT_SIZE]; + int pos = 0; + for (int i = 0; i < cheats_available(); i++) + { + fileTYPE f = {}; + if (cheats[i].enabled) + { + sprintf(filename, "%s/%s", cheat_zip, cheats[i].name); + if (FileOpen(&f, filename)) + { + int len = f.size; + if (!len || (len & 15)) + { + printf("Cheat file %s has incorrect length %d -> skipping.", filename, len); + } + else + { + if (len + pos > CHEAT_SIZE) + { + len = CHEAT_SIZE - pos; + } + + if (FileReadAdv(&f, buff + pos, len) == len) + { + pos += len; + } + else + { + printf("Cannot read cheat file %s.", filename); + } + } + FileClose(&f); + } + else + { + printf("Cannot open cheat file %s.", filename); + } + } + + if (pos >= CHEAT_SIZE) break; + } + + printf("Cheat codes: %d\n", pos/16); + + user_io_set_index(255); + + // prepare transmission + EnableFpga(); + spi8(UIO_FILE_TX); + spi8(0xff); + DisableFpga(); + + EnableFpga(); + spi8(UIO_FILE_TX_DAT); + spi_write(buff, pos ? pos : 2, fpga_get_fio_size()); + DisableFpga(); + + // signal end of transmission + EnableFpga(); + spi8(UIO_FILE_TX); + spi8(0x00); + DisableFpga(); +} + +void cheats_toggle() +{ + cheats[iSelectedEntry].enabled = !cheats[iSelectedEntry].enabled; + cheats_send(); +} diff --git a/cheats.h b/cheats.h new file mode 100644 index 0000000..5534692 --- /dev/null +++ b/cheats.h @@ -0,0 +1,12 @@ +#ifndef CHEATS_H +#define CHEATS_H + +void cheats_init(char *rom_path); +int cheats_available(); +void cheats_scan(int mode); +void cheats_scroll_name(); +void cheats_print(); +void cheats_toggle(); + + +#endif diff --git a/file_io.cpp b/file_io.cpp index 5432a1e..cc449d5 100644 --- a/file_io.cpp +++ b/file_io.cpp @@ -33,8 +33,8 @@ typedef std::vector DirentVector; static const size_t YieldIterations = 128; DirentVector DirItem; -int iSelectedEntry = 0; // selected entry index -int iFirstEntry = 0; +static int iSelectedEntry = 0; // selected entry index +static int iFirstEntry = 0; static char full_path[2100]; diff --git a/menu.cpp b/menu.cpp index f69cee4..b52283e 100644 --- a/menu.cpp +++ b/menu.cpp @@ -48,6 +48,7 @@ along with this program. If not, see . #include "input.h" #include "battery.h" #include "bootcore.h" +#include "cheats.h" #include "support.h" @@ -110,6 +111,8 @@ enum MENU MENU_BTPAIR, MENU_WMPAIR, MENU_WMPAIR1, + MENU_CHEATS1, + MENU_CHEATS2, // Mist/atari specific pages MENU_MIST_MAIN1, @@ -761,6 +764,7 @@ void HandleUI(void) static char drive_num = 0; static char flag; static int cr = 0; + static uint32_t cheatsub = 0; static char cp_MenuCancel; @@ -1160,6 +1164,27 @@ void HandleUI(void) selentry++; } + // check for 'C'heats + if (p && (p[0] == 'C')) + { + substrcpy(s, p, 1); + if (strlen(s)) + { + strcpy(s, " "); + substrcpy(s + 1, p, 1); + } + else + { + strcpy(s, " Cheats"); + } + MenuWrite(entry, s, menusub == selentry, !cheats_available()); + + // add bit in menu mask + menumask = (menumask << 1) | 1; + entry++; + selentry++; + } + // check for 'T'oggle and 'R'eset (toggle and then close menu) strings if (p && ((p[0] == 'T') || (p[0] == 'R'))) { @@ -1306,7 +1331,13 @@ void HandleUI(void) entry++; } - if (p[0] == 'F') + if (p[0] == 'C' && cheats_available()) + { + menustate = MENU_CHEATS1; + cheatsub = menusub; + menusub = 0; + } + else if (p[0] == 'F') { opensave = (p[1] == 'S'); substrcpy(ext, p, 1); @@ -1363,7 +1394,6 @@ void HandleUI(void) menustate = MENU_8BIT_MAIN1; if (p[0] == 'R') menustate = MENU_NONE1; } - } } } @@ -1382,6 +1412,7 @@ void HandleUI(void) case MENU_8BIT_MAIN_FILE_SELECTED: printf("File selected: %s\n", SelectedPath); user_io_file_tx(SelectedPath, user_io_ext_idx(SelectedPath, fs_pFileExt) << 6 | (menusub + 1), opensave); + cheats_init(SelectedPath); menustate = MENU_NONE1; break; @@ -2900,6 +2931,72 @@ void HandleUI(void) break; + /******************************************************************/ + /* cheats menu */ + /******************************************************************/ + case MENU_CHEATS1: + helptext = helptexts[HELPTEXT_NONE]; + OsdSetTitle("Cheats", 0); + cheats_print(); + menustate = MENU_CHEATS2; + parentstate = menustate; + break; + + case MENU_CHEATS2: + menumask = 0; + + if (menu) + { + menustate = MENU_8BIT_MAIN1; + menusub = cheatsub; + break; + } + + cheats_scroll_name(); + + if (c == KEY_HOME) + { + cheats_scan(SCANF_INIT); + menustate = MENU_CHEATS1; + } + + if (c == KEY_END) + { + cheats_scan(SCANF_END); + menustate = MENU_CHEATS1; + } + + if ((c == KEY_PAGEUP) || (c == KEY_LEFT)) + { + cheats_scan(SCANF_PREV_PAGE); + menustate = MENU_CHEATS1; + } + + if ((c == KEY_PAGEDOWN) || (c == KEY_RIGHT)) + { + cheats_scan(SCANF_NEXT_PAGE); + menustate = MENU_CHEATS1; + } + + if (down) // scroll down one entry + { + cheats_scan(SCANF_NEXT); + menustate = MENU_CHEATS1; + } + + if (up) // scroll up one entry + { + cheats_scan(SCANF_PREV); + menustate = MENU_CHEATS1; + } + + if (select) + { + cheats_toggle(); + menustate = MENU_CHEATS1; + } + break; + /******************************************************************/ /* reset menu */ /******************************************************************/ @@ -3967,7 +4064,7 @@ void ScrollLongName(void) if (flist_SelectedItem()->d_type == DT_DIR) max_len = 25; // number of directory name characters to display - ScrollText(flist_iSelectedEntry()-flist_iFirstEntry(), flist_SelectedItem()->d_name, 2, len, max_len, 1); + ScrollText(flist_iSelectedEntry()-flist_iFirstEntry(), flist_SelectedItem()->d_name, 0, len, max_len, 1); } void PrintFileName(char *name, int row, int maxinv) diff --git a/osd.cpp b/osd.cpp index 89dc669..e67f7ee 100644 --- a/osd.cpp +++ b/osd.cpp @@ -397,7 +397,7 @@ void OsdDrawLogo(int row) } // write a null-terminated string to the OSD buffer starting at line -void OSD_PrintText(unsigned char line, const char *text, unsigned long start, unsigned long width, unsigned long offset, unsigned char invert) +void OSD_PrintText(unsigned char line, const char *hdr, const char *text, unsigned long start, unsigned long width, unsigned long offset, unsigned char invert) { // line : OSD line number (0-7) // text : pointer to null-terminated string @@ -419,45 +419,53 @@ void OSD_PrintText(unsigned char line, const char *text, unsigned long start, un invert = 0xff; p = &titlebuffer[(osd_size - 1 - line) * 8]; - if (start>2) { + if (start>2) + { spi16(0xffff); start -= 2; } i = start>16 ? 16 : start; - for (j = 0; j<(i / 2); ++j) - spi_n(255 ^ *p++, 2); + for (j = 0; j<(i / 2); ++j) spi_n(255 ^ *p++, 2); - if (i & 1) - spi8(255 ^ *p); + if (i & 1) spi8(255 ^ *p); start -= i; - if (start>2) { + if (start>2) + { spi16(0xffff); start -= 2; } - while (start--) - spi8(0x00); + while (start--) spi8(0x00); - if (offset) { + while(*hdr) + { + width -= 8; + p = charfont[(uint)(*hdr++)]; + for (int i=0; i < 8; i++) spi8(*p++^invert); + } + + if (offset) + { width -= 8 - offset; p = &charfont[(uint)(*text++)][offset]; for (; offset < 8; offset++) spi8(*p++^invert); } - while (width > 8) { + while (width > 8) + { unsigned char b; p = &charfont[(uint)(*text++)][0]; for (b = 0; b<8; b++) spi8(*p++^invert); width -= 8; } - if (width) { + if (width) + { p = &charfont[(uint)(*text++)][0]; - while (width--) - spi8(*p++^invert); + while (width--) spi8(*p++^invert); } DisableOsd(); @@ -640,12 +648,21 @@ void ScrollText(char n, const char *str, int off, int len, int max_len, unsigned #define BLANKSPACE 10 // number of spaces between the end and start of repeated name - char s[40]; + char s[40], hdr[40]; long offset; if (!max_len) max_len = 30; if (str && str[0] && CheckTimer(scroll_timer)) // scroll if long name and timer delay elapsed { + hdr[0] = 0; + if (off) + { + strncpy(hdr, str, off); + hdr[off] = 0; + str += off; + if (len > off) len -= off; + } + scroll_timer = GetTimer(SCROLL_DELAY2); // reset scroll timer to repeat delay scroll_offset++; // increase scroll position (1 pixel unit) @@ -653,7 +670,7 @@ void ScrollText(char n, const char *str, int off, int len, int max_len, unsigned if (!len) len = strlen(str); // get name length - if (off+len > max_len) // scroll name if longer than display size + if (off+2+len > max_len) // scroll name if longer than display size { // reset scroll position if it exceeds predefined maximum if (scroll_offset >= (uint)(len + BLANKSPACE) << 3) scroll_offset = 0; @@ -668,7 +685,7 @@ void ScrollText(char n, const char *str, int off, int len, int max_len, unsigned strncpy(s + len + BLANKSPACE, str, max_len - len - BLANKSPACE); // repeat the name after its end and predefined number of blank space } - OSD_PrintText(n, s, 22, (max_len - 1) << 3, (scroll_offset & 0x7), invert); // OSD print function with pixel precision + OSD_PrintText(n, hdr, s, 22, (max_len - 1) << 3, (scroll_offset & 0x7), invert); // OSD print function with pixel precision } } }