From 511902eb5613d057605eba1cff0697d4c6fb0b1d Mon Sep 17 00:00:00 2001 From: sanni Date: Sat, 6 Aug 2022 14:50:40 +0200 Subject: [PATCH] 9.4: Add selecting mapping from database list for NES --- Cart_Reader/COLV.ino | 42 +++++- Cart_Reader/Cart_Reader.ino | 124 ++++++++++++++++- Cart_Reader/INTV.ino | 45 ++++++- Cart_Reader/NES.ino | 262 +++++++++++++++++++++++++++++++++++- 4 files changed, 460 insertions(+), 13 deletions(-) diff --git a/Cart_Reader/COLV.ino b/Cart_Reader/COLV.ino index c17e60f..3fcb0dd 100644 --- a/Cart_Reader/COLV.ino +++ b/Cart_Reader/COLV.ino @@ -372,7 +372,6 @@ void checkStatus_COL() // CART SELECT CODE //****************************************** void setCart_COL() { - //Search for CRC32 in file char gamename[100]; char tempStr2[2]; char crc_search[9]; @@ -380,8 +379,46 @@ void setCart_COL() { //go to root sd.chdir(); + // Select starting letter + byte myLetter = starting_letter(); + // Open database if (myFile.open("colv.txt", O_READ)) { + // Skip ahead to selected starting letter + if ((myLetter > 0) && (myLetter <= 26)) { + while (myFile.available()) { + // Read current name + get_line(gamename, &myFile, 96); + + // Compare selected letter with first letter of current name until match + while (gamename[0] != 64 + myLetter) { + skip_line(&myFile); + skip_line(&myFile); + get_line(gamename, &myFile, 96); + } + break; + } + + // Rewind one line + for (byte count_newline = 0; count_newline < 2; count_newline++) { + while (1) { + if (myFile.curPosition() == 0) { + break; + } + else if (myFile.peek() == '\n') { + myFile.seekSet(myFile.curPosition() - 1); + break; + } + else { + myFile.seekSet(myFile.curPosition() - 1); + } + } + } + if (myFile.curPosition() != 0) + myFile.seekSet(myFile.curPosition() + 2); + } + + // Display database while (myFile.available()) { display_Clear(); @@ -520,8 +557,7 @@ void setCart_COL() { } } else { - println_Msg(F("Database file not found")); - return 0; + print_Error(F("Database file not found"), true); } } #endif diff --git a/Cart_Reader/Cart_Reader.ino b/Cart_Reader/Cart_Reader.ino index f0ee09b..b2a7553 100644 --- a/Cart_Reader/Cart_Reader.ino +++ b/Cart_Reader/Cart_Reader.ino @@ -4,8 +4,8 @@ This project represents a community-driven effort to provide an easy to build and easy to modify cartridge dumper. - Date: 03.08.2022 - Version: 9.3 + Date: 06.08.2022 + Version: 9.4 SD lib: https://github.com/greiman/SdFat OLED lib: https://github.com/adafruit/Adafruit_SSD1306 @@ -60,7 +60,7 @@ **********************************************************************************/ -char ver[5] = "9.3"; +char ver[5] = "9.4"; //****************************************** // !!! CHOOSE HARDWARE VERSION !!! @@ -691,6 +691,108 @@ boolean compareCRC(char* database, char* crcString, boolean renamerom, int offse #endif } +byte starting_letter() { +#if (defined(enable_LCD) || defined(enable_OLED)) + byte selection = 0; + byte line = 0; + + display_Clear(); +#if defined(enable_LCD) + println_Msg(F("[#] [A] [B] [C] [D] [E] [F]")); + println_Msg(F("")); + println_Msg(F("[G] [H] [ I ] [J] [K] [L] [M]")); + println_Msg(F("")); + println_Msg(F("[N] [O] [P] [Q] [R] [S] [T]")); + println_Msg(F("")); + println_Msg(F("[U] [V] [W] [X] [Y] [Z] [?]")); +#elif defined(enable_OLED) + println_Msg(F("# A B C D E F")); + println_Msg(F("")); + println_Msg(F("G H I J K L M")); + println_Msg(F("")); + println_Msg(F("N O P Q R S T")); + println_Msg(F("")); + println_Msg(F("U V W X Y Z ?")); +#endif + + // Draw selection line +#if defined(enable_LCD) + display.setDrawColor(1); + display.drawLine(4 + selection * 16, 10 + line * 16, 9 + selection * 16, 10 + line * 16); +#elif defined(enable_OLED) + display.drawLine(selection * 18, 10 + line * 16, 5 + selection * 18, 10 + line * 16, WHITE); +#endif + display_Update(); + + while (1) { + int b = checkButton(); + if (b == 2) { // Previous + if ((selection == 0) && (line > 0)) { + line--; + selection = 6; + } + else if (selection > 0) { + selection--; + } +#if defined(enable_LCD) + display.setDrawColor(0); + display.drawLine(0, 10 + (line + 1) * 16, 128, 10 + (line + 1) * 16); + display.drawLine(0, 10 + line * 16, 128, 10 + line * 16); + display.setDrawColor(1); + display.drawLine(4 + selection * 16, 10 + line * 16, 9 + selection * 16, 10 + line * 16); +#elif defined(enable_OLED) + display.drawLine(0, 10 + (line + 1) * 16, 128, 10 + (line + 1) * 16, BLACK); + display.drawLine(0, 10 + line * 16, 128, 10 + line * 16, BLACK); + display.drawLine(selection * 18, 10 + line * 16, 5 + selection * 18, 10 + line * 16, WHITE); +#endif + display_Update(); + + } + + else if (b == 1) { // Next + if ((selection == 6) && (line < 3)) { + line++; + selection = 0; + } + else if (selection < 6) { + selection++; + } +#if defined(enable_LCD) + display.setDrawColor(0); + display.drawLine(0, 10 + (line - 1) * 16, 128, 10 + (line - 1) * 16); + display.drawLine(0, 10 + line * 16, 128, 10 + line * 16); + display.setDrawColor(1); + display.drawLine(4 + selection * 16, 10 + line * 16, 9 + selection * 16, 10 + line * 16); +#elif defined(enable_OLED) + display.drawLine(0, 10 + (line - 1) * 16, 128, 10 + (line - 1) * 16, BLACK); + display.drawLine(0, 10 + line * 16, 128, 10 + line * 16, BLACK); + display.drawLine(selection * 18, 10 + line * 16, 5 + selection * 18, 10 + line * 16, WHITE); +#endif + display_Update(); + } + + else if (b == 3) { // Long Press - Execute + display_Clear(); + println_Msg(F("")); + println_Msg(F("")); + println_Msg(F("")); + println_Msg(F("Please wait...")); + display_Update(); + break; + } + } + return (selection + line * 7); +#elif defined(SERIAL_MONITOR) + Serial.println(F("Enter first letter: ")); + while (Serial.available() == 0) { + } + + // Read the incoming byte: + byte incomingByte = Serial.read(); + return incomingByte; +#endif +} + /****************************************** Main menu optimized for rotary encoder *****************************************/ @@ -759,7 +861,13 @@ void mainMenu() { display_Update(); setup_NES(); #ifdef no-intro - checkStatus_NES(getMapping()); + if (getMapping() == 0) { + selectMapping(); + checkStatus_NES(0); + } + else { + checkStatus_NES(1); + } #else checkStatus_NES(0); #endif @@ -1001,7 +1109,13 @@ void consoleMenu() { display_Update(); setup_NES(); #ifdef no-intro - checkStatus_NES(getMapping()); + if (getMapping() == 0) { + selectMapping(); + checkStatus_NES(0); + } + else { + checkStatus_NES(1); + } #else checkStatus_NES(0); #endif diff --git a/Cart_Reader/INTV.ino b/Cart_Reader/INTV.ino index 81f5876..c8eb31c 100644 --- a/Cart_Reader/INTV.ino +++ b/Cart_Reader/INTV.ino @@ -757,8 +757,10 @@ void checkStatus_INTV() #endif } +//****************************************** +// CART SELECT CODE +//****************************************** void setCart_INTV() { - //Search for CRC32 in file char gamename[100]; char tempStr2[2]; char crc_search[9]; @@ -766,8 +768,46 @@ void setCart_INTV() { //go to root sd.chdir(); + // Select starting letter + byte myLetter = starting_letter(); + // Open database if (myFile.open("intv.txt", O_READ)) { + // Skip ahead to selected starting letter + if ((myLetter > 0) && (myLetter <= 26)) { + while (myFile.available()) { + // Read current name + get_line(gamename, &myFile, 96); + + // Compare selected letter with first letter of current name until match + while (gamename[0] != 64 + myLetter) { + skip_line(&myFile); + skip_line(&myFile); + get_line(gamename, &myFile, 96); + } + break; + } + + // Rewind one line + for (byte count_newline = 0; count_newline < 2; count_newline++) { + while (1) { + if (myFile.curPosition() == 0) { + break; + } + else if (myFile.peek() == '\n') { + myFile.seekSet(myFile.curPosition() - 1); + break; + } + else { + myFile.seekSet(myFile.curPosition() - 1); + } + } + } + if (myFile.curPosition() != 0) + myFile.seekSet(myFile.curPosition() + 2); + } + + // Display database while (myFile.available()) { display_Clear(); @@ -916,8 +956,7 @@ void setCart_INTV() { } } else { - println_Msg(F("Database file not found")); - return 0; + print_Error(F("Database file not found"), true); } } #endif diff --git a/Cart_Reader/NES.ino b/Cart_Reader/NES.ino index 704318e..9eb0d7c 100644 --- a/Cart_Reader/NES.ino +++ b/Cart_Reader/NES.ino @@ -603,6 +603,266 @@ boolean getMapping() { } } +void selectMapping() { + char gamename[100]; + char tempStr2[2]; + char iNES_STR[33]; + char crc_search[9]; + + //go to root + sd.chdir(); + + // Select starting letter + byte myLetter = starting_letter(); + + // Open database + if (myFile.open("nes.txt", O_READ)) { + // Skip ahead to selected starting letter + if ((myLetter > 0) && (myLetter <= 26)) { + while (myFile.available()) { + // Read current name + get_line(gamename, &myFile, 96); + + // Compare selected letter with first letter of current name until match + while (gamename[0] != 64 + myLetter) { + skip_line(&myFile); + skip_line(&myFile); + get_line(gamename, &myFile, 96); + } + break; + } + + // Rewind one line + for (byte count_newline = 0; count_newline < 2; count_newline++) { + while (1) { + if (myFile.curPosition() == 0) { + break; + } + else if (myFile.peek() == '\n') { + myFile.seekSet(myFile.curPosition() - 1); + break; + } + else { + myFile.seekSet(myFile.curPosition() - 1); + } + } + } + if (myFile.curPosition() != 0) + myFile.seekSet(myFile.curPosition() + 2); + } + + // Display database + while (myFile.available()) { + display_Clear(); + + // Read game name +#if defined(enable_OLED) + get_line(gamename, &myFile, 42); +#else + get_line(gamename, &myFile, 96); +#endif + + // Read CRC32 checksum + sprintf(checksumStr, "%c", myFile.read()); + for (byte i = 0; i < 7; i++) { + sprintf(tempStr2, "%c", myFile.read()); + strcat(checksumStr, tempStr2); + } + + // Skip over semicolon + myFile.seekSet(myFile.curPosition() + 1); + + // Read CRC32 of first 512 bytes + sprintf(crc_search, "%c", myFile.read()); + for (byte i = 0; i < 7; i++) { + sprintf(tempStr2, "%c", myFile.read()); + strcat(crc_search, tempStr2); + } + + // Skip over semicolon + myFile.seekSet(myFile.curPosition() + 1); + + // Read iNES header + get_line(iNES_STR, &myFile, 33); + + // Skip every 3rd line + skip_line(&myFile); + + // Convert "4E4553" to (0x4E, 0x45, 0x53) + byte iNES_BUF[2]; + for (byte j = 0; j < 16; j++) { + sscanf(iNES_STR + j * 2, "%2X", iNES_BUF); + iNES_HEADER[j] = iNES_BUF[0]; + } + + // Convert iNES garbage to useful info (thx to fceux) + mapper = (iNES_HEADER[6] >> 4); + mapper |= (iNES_HEADER[7] & 0xF0); + mapper |= ((iNES_HEADER[8] & 0x0F) << 8); + + // PRG size + if ((iNES_HEADER[9] & 0x0F) != 0x0F) { + // simple notation + prgsize = (iNES_HEADER[4] | ((iNES_HEADER[9] & 0x0F) << 8)); //*16 + } + else { + // exponent-multiplier notation + prgsize = (((1 << (iNES_HEADER[4] >> 2)) * ((iNES_HEADER[4] & 0b11) * 2 + 1)) >> 14); //*16 + } + if (prgsize != 0) + prgsize = (int(log(prgsize) / log(2))); + + // CHR size + if ((iNES_HEADER[9] & 0xF0) != 0xF0) { + // simple notation + chrsize = (uppow2(iNES_HEADER[5] | ((iNES_HEADER[9] & 0xF0) << 4))) * 2; //*4 + } + else { + chrsize = (((1 << (iNES_HEADER[5] >> 2)) * ((iNES_HEADER[5] & 0b11) * 2 + 1)) >> 13) * 2; //*4 + } + if (chrsize != 0) + chrsize = (int(log(chrsize) / log(2))); + + // RAM size + ramsize = ((iNES_HEADER[10] & 0xF0) ? (64 << ((iNES_HEADER[10] & 0xF0) >> 4)) : 0) / 4096; //*4 + if (ramsize != 0) + ramsize = (int(log(ramsize) / log(2))); + + prg = (int_pow(2, prgsize)) * 16; + if (chrsize == 0) + chr = 0; // 0K + else + chr = (int_pow(2, chrsize)) * 4; + if (ramsize == 0) + ram = 0; // 0K + else if (mapper == 82) + ram = 5; // 5K + else + ram = (int_pow(2, ramsize)) * 4; + + // Mapper Variants + // Identify variant for use across multiple functions + if (mapper == 4) { // Check for MMC6/MMC3 + checkMMC6(); + if (mmc6) + ram = 1; // 1K + } + + // Get name + byte myLength = 0; + for (unsigned int i = 0; i < 20; i++) { + // Stop at first "(" to remove "(Country)" + if (char(gamename[i]) == 40) { + break; + } + if (((char(gamename[i]) >= 48 && char(gamename[i]) <= 57) || (char(gamename[i]) >= 65 && char(gamename[i]) <= 90) || (char(gamename[i]) >= 97 && char(gamename[i]) <= 122) || (char(gamename[i]) == 32)) && (myLength < 15)) { + romName[myLength] = char(gamename[i]); + myLength++; + } + } + + // If name consists out of all japanese characters use CART as name + if (myLength == 0) { + romName[0] = 'C'; + romName[1] = 'A'; + romName[2] = 'R'; + romName[3] = 'T'; + } + println_Msg(romName); + print_Msg(F("MAPPER: ")); + println_Msg(mapper); + print_Msg(F("PRG SIZE: ")); + print_Msg(prg); + println_Msg(F("K")); + print_Msg(F("CHR SIZE: ")); + print_Msg(chr); + println_Msg(F("K")); + print_Msg(F("RAM SIZE: ")); + if (mapper == 0) { + print_Msg(ram / 4); + println_Msg(F("K")); + } + else if ((mapper == 16) || (mapper == 80) || (mapper == 159)) { + if (mapper == 16) + print_Msg(ram * 32); + else + print_Msg(ram * 16); + println_Msg(F("B")); + } + else if (mapper == 19) { + if (ramsize == 2) + println_Msg(F("128B")); + else { + print_Msg(ram); + println_Msg(F("K")); + } + } + else { + print_Msg(ram); + println_Msg(F("K")); + } + println_Msg(F("")); +#if defined(enable_OLED) + println_Msg(F("Press left to Change")); + println_Msg(F("and right to Select")); +#elif defined(enable_LCD) + println_Msg(F("Rotate to Change")); + println_Msg(F("Press to Select")); +#elif defined(SERIAL_MONITOR) + println_Msg(F("U/D to Change")); + println_Msg(F("Space to Select")); +#endif + display_Update(); + + int b = 0; + while (1) { + // Check button input + b = checkButton(); + + // Next + if (b == 1) { + break; + } + + // Previous + else if (b == 2) { + for (byte count_newline = 0; count_newline < 7; count_newline++) { + while (1) { + if (myFile.curPosition() == 0) { + break; + } + else if (myFile.peek() == '\n') { + myFile.seekSet(myFile.curPosition() - 1); + break; + } + else { + myFile.seekSet(myFile.curPosition() - 1); + } + } + } + if (myFile.curPosition() != 0) + myFile.seekSet(myFile.curPosition() + 2); + break; + } + + // Selection + else if (b == 3) { + // Save Mapper + EEPROM_writeAnything(7, mapper); + EEPROM_writeAnything(8, prgsize); + EEPROM_writeAnything(9, chrsize); + EEPROM_writeAnything(10, ramsize); + myFile.close(); + break; + } + } + } + } + else { + print_Error(F("Database file not found"), true); + } +} + void readRom_NES() { // Get name, add extension and convert to char array for sd lib strcpy(fileName, romName); @@ -4267,9 +4527,7 @@ void writeFLASH() { sd.chdir(); // root filePath[0] = '\0'; // Reset filePath } - #endif - //****************************************** // End of File //******************************************