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
}
}
}