diff --git a/CMakeLists.txt b/CMakeLists.txt index 93ad1f8..f666026 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,9 @@ add_definitions(-DNO_PICO_LED) # prevent SD SPI library from using LED add_subdirectory(no-OS-FatFS-SD-SPI-RPi-Pico/FatFs_SPI PicoMemcard) add_subdirectory(poc_examples) +#optimization flag +add_compile_options(-O2) + # Example source target_sources(PicoMemcard PUBLIC ${CMAKE_SOURCE_DIR}/src/led.c @@ -52,4 +55,4 @@ target_link_libraries( hardware_adc ) -pico_add_extra_outputs(PicoMemcard) \ No newline at end of file +pico_add_extra_outputs(PicoMemcard) diff --git a/README.md b/README.md index 168018a..6259e79 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ PicoMemcard allows you to build your own supercharged PSX Memory Card that can b * Allows to play burned CDs (thanks to [FreePSXBoot]) * Cheaper than an original memory card * Can store hudreds of memory card images +* Switching memory cards using MemcardPro commands with L2/R2 ## Bill of materials * **Raspberry Pi Pico** (around $5) @@ -111,7 +112,9 @@ Inside `docs/images` you can find two memory card images. One has a couple of sa ## Switching/Creating Images On **PicoMemcard+** you can switch the active memory card image with the following inputs: * `START + SELECT + DPAD UP` will switch to the next image (e.g from `1.MCR` to `2.MCR`). +* `START + SELECT + DPAD RIGHT` will switch to the next image by 5 (e.g from `1.MCR` to `6.MCR`). * `START + SELECT + DPAD DOWN` will switch to the previous image (e.g from `1.MCR` to `0.MCR`). +* `START + SELECT + DPAD LEFT` will switch to the previous image by 5 (e.g from `6.MCR` to `1.MCR`). Additionally you can create a new empty memory card image (and automatically switch to it) by pressing `START + SELECT + TRIANGLE`. @@ -119,6 +122,9 @@ Additionally you can create a new empty memory card image (and automatically swi Additionally this method does not work on PS2 Memory Cards and Controllers are wired on a different bus. +## Game ID +On **PicoMemcard+** you can switch automatically to a virtual memory card based on the game you are playing, without any input, if you are using an Xstation, Unirom or a patched BIOS. + ## Syncing Changes Generally speaking, new data written to PicoMemcard (e.g. when you save) is permanently stored only after a short period of time (due to hardware limitation). The on board LED indicates whether all changes have been stored or not, in particular: * On Rapsbery Pi Pico the LED will be on when all changes have been saved, off otherwise. @@ -157,16 +163,20 @@ In particular, the RP2040-Zero has RGB LED that provides a more clear output. Th | Data synced | Led on | Green solid led | ### PicoMemcard+ Status -| Status | Pico | RP2040-Zero -| --- | --- | --- | -| Failed to read SD card | Blinking led | Red blinking led -| Data not fully synced (do not turn off PSX)| Led off | Red led (or flashing red and green) | -| Data synced | Led on | Green solid led | -| Memory Card image changed | Three fast blinks | Single blue blink | -| Memory Card image not changed (end of list) | Nine fast blinks | Single orange blink | -| New Memory Card image created | Multiple very fast blinks | Single light blue blink | +| Status | Pico | RP2040-Zero | Bitfunx Memory card +| --- | --- | --- | --- | +| Failed to read SD card | Blinking led | Red blinking led | Red blinking led | +| Data not fully synced (do not turn off PSX)| Led off | Yellow led | Yellow led | +| Data synced | Led on | Green led | Green led | +| Memory Card image changed | Three fast blinks | Single blue blink | Single blue blink | +| Memory Card image not changed (end of list) | Nine fast blinks | Single orange blink | Single purple blink | +| New Memory Card image created | Multiple very fast blinks | Single light blue blink | Single light blue blink | ## General Warnings +This fork has not been tested with a PicoMemcard+ (because i don't have one) but with a Bitfunx Memory card. This version of the firmware may or may not work with your board. + +Due to Hardware limitations the Game ID functionality can't detect a disc swap in multidisc game, if you boot the second cd a new memory card will be created without transfering your progress. + I would recommend to never plug PicoMemcard both into the PC (via USB) and the PSX at the same time! Otherwise the 5V provided by USB would end up on the 3.3V rail of the PSX. I'm not really sure if this could cause actual damage but I would avoid risking it. If you really need to have the Pico plugged into both the USB and PSX (e.g. for debugging purposes), disconnect the 3.3V line from the VBUS pin. In this way you can power on the Pico using a simple USB phone charger or by plugging it into your PC. @@ -184,6 +194,10 @@ Special thanks to everybody that supported it so far! You are all amazing. For people interested in understanding how PicoMemcard works I provide a more extensive explanation in [this post] (although now somewhat outdated). ## Thanks To +* [Schermaiolo] - Writing the Game ID write function +* [SantX27] - Debugging the 1.0.3 firmware to work on the Bitfunx Memory card +* [whitezombie2000] - Extensive debugging and report of all bugs related to Picomemcard, without him the releases would be way more buggy. +* [wired-filipino-owl] - Documentation and implementation of the led functionality on the Bitfunx Memory card. * [psx-spx] and Martin "NO$PSX" Korth - PlayStation Specifications and documented Memory Card protocol and filesystem. * [Andrew J. McCubbin] - Additional information about Memory Card and Controller communication with PSX. * [littlefs] - Filesystem designed to work on NOR flash used by many microcontrollers, including the Raspberry Pi Pico. @@ -191,6 +205,10 @@ For people interested in understanding how PicoMemcard works I provide a more ex * [Scoppy] - Use your Raspberry Pi Pico as an oscilloscope, very cheap and accurate. Without this I would have not been able to debug many issues. * [PulseView] - Used to import, visualize and manipulate the data from Scoppy on a PC. +[Schermaiolo]: https://github.com/schermaiolo +[SantX27]: https://github.com/SantX27 +[whitezombie2000]: https://github.com/whitezombie2000 +[wired-filipino-owl]: https://github.com/wired-filipino-owl [FreePSXBoot]: https://github.com/brad-lin/FreePSXBoot [psx-spx]: https://psx-spx.consoledev.net/pinouts/#controller-ports-and-memory-card-ports [Andrew J. McCubbin]: http://www.emudocs.org/PlayStation/psxcont/ @@ -202,4 +220,4 @@ For people interested in understanding how PicoMemcard works I provide a more ex [this post]: https://dangiu.github.io/2022/05/13/picomemcard.html [from here]: https://forms.gle/f6XHtz6W5fn5qDZV7 [This guy]: https://github.com/MrSVCD/PicoPSX_3D -[MemcardRex]: https://github.com/ShendoXT/memcardrex \ No newline at end of file +[MemcardRex]: https://github.com/ShendoXT/memcardrex diff --git a/inc/config.h b/inc/config.h index fdd2fb1..4ddf7c5 100644 --- a/inc/config.h +++ b/inc/config.h @@ -7,11 +7,13 @@ #define IDLE_AUTOSYNC_TIMEOUT 5 * 1000 // time (in ms) the memory card must be inactive before automatic sync from RAM to LFS #define MAX_MC_FILENAME_LEN 32 // max length of memory card file name (including extension) #define MAX_MC_IMAGES 255 // maximum number of different mc images -#define MC_RECONNECT_TIME 1000 // time (in ms) the memory card stays disconnected when simulating reconnection +#define MC_RECONNECT_TIME 250 // time (in ms) the memory card stays disconnected when simulating reconnection /* Board targeted by build */ -#define PICO +//#define PICO //#define RP2040ZERO +#define BITFUNX +#define GAMEID /* Invert red and green. Uncomment this if the LED colours for your RP2040 Zero are incorrect. */ #define INVERT_RED_GREEN @@ -33,6 +35,14 @@ #define PIN_ACK 13 #endif +#ifdef BITFUNX + //#define PIN_DAT 5 + //#define PIN_CMD PIN_DAT + 1 // must be immediately after PIN_DAT + //#define PIN_SEL PIN_CMD + 1 // must be immediately after PIN_CMD + //#define PIN_CLK PIN_SEL + 1 // must be immediately after PIN_SEL + //#define PIN_ACK 9 +#endif + /* SD Card Configuration */ #define BLOCK_SIZE 512 // SD card communicate using only 512 block size for consistency #define BAUD_RATE 5000 * 1000 @@ -50,4 +60,11 @@ #define PIN_SS 1 #endif +#ifdef BITFUNX + #define PIN_MISO 16 + #define PIN_MOSI 19 + #define PIN_SCK 18 + #define PIN_SS 17 +#endif + #endif \ No newline at end of file diff --git a/inc/memcard_manager.h b/inc/memcard_manager.h index 4853b59..5fc0a6e 100644 --- a/inc/memcard_manager.h +++ b/inc/memcard_manager.h @@ -3,6 +3,9 @@ #include #include +#include +#include + /* Error codes */ #define MM_OK 0 @@ -18,9 +21,10 @@ bool memcard_manager_exist(uint8_t* filename); uint32_t memcard_manager_count(); uint32_t memcard_manager_get(uint32_t index, uint8_t* out_filename); #define memcard_manager_get_initial(out_filename) memcard_manager_get(memcard_manager_get_prev_loaded_memcard_index(), (out_filename)) +uint32_t update_prev_loaded_memcard_index(uint32_t index); uint32_t memcard_manager_get_prev_loaded_memcard_index(); -uint32_t memcard_manager_get_next(uint8_t* filename, uint8_t* out_nextfile); -uint32_t memcard_manager_get_prev(uint8_t* filename, uint8_t* out_prevfile); +uint32_t memcard_manager_get_next(uint8_t* filename, uint8_t* out_nextfile, bool skip); +uint32_t memcard_manager_get_prev(uint8_t* filename, uint8_t* out_prevfile, bool skip); uint32_t memcard_manager_create(uint8_t* out_filename); - +uint32_t create_index(uint8_t *vec, uint8_t size, uint8_t *out_filename); #endif \ No newline at end of file diff --git a/src/led.c b/src/led.c index e58fa6b..9a9f230 100644 --- a/src/led.c +++ b/src/led.c @@ -14,6 +14,12 @@ #define PICO_LED_PIN 25 #endif +#ifdef BITFUNX +#define PICO_RED_PIN 25 +#define PICO_GRN_PIN 24 +#define PICO_BLU_PIN 23 +#endif + static uint smWs2813; static uint offsetWs2813; static int32_t picoW = -1; @@ -34,19 +40,27 @@ void ws2812_put_rgb(uint8_t red, uint8_t green, uint8_t blue) { #endif void led_init() { - #ifdef PICO - if (is_pico_w()) { - if (cyw43_arch_init()) { - picoW = 0; - } - } - init_led(PICO_LED_PIN); - #endif - #ifdef RP2040ZERO - offsetWs2813 = pio_add_program(pio1, &ws2812_program); - smWs2813 = pio_claim_unused_sm(pio1, true); - ws2812_program_init(pio1, smWs2813, offsetWs2813, 16, 800000, true); + #ifdef PICO + if (is_pico_w()) { + if (cyw43_arch_init()) { + picoW = 0; + } + } + init_led(PICO_LED_PIN); #endif + #ifdef RP2040ZERO + offsetWs2813 = pio_add_program(pio1, &ws2812_program); + smWs2813 = pio_claim_unused_sm(pio1, true); + ws2812_program_init(pio1, smWs2813, offsetWs2813, 16, 800000, true); + #endif + #ifdef BITFUNX + gpio_init(PICO_RED_PIN); + gpio_init(PICO_GRN_PIN); + gpio_init(PICO_BLU_PIN); + gpio_set_dir(PICO_RED_PIN, GPIO_OUT); + gpio_set_dir(PICO_GRN_PIN, GPIO_OUT); + gpio_set_dir(PICO_BLU_PIN, GPIO_OUT); + #endif } void led_output_sync_status(bool out_of_sync) { @@ -61,6 +75,20 @@ void led_output_sync_status(bool out_of_sync) { ws2812_put_rgb(0, 255, 0); } #endif + #ifdef BITFUNX + if(out_of_sync) { + //yellow + gpio_put(PICO_RED_PIN, 1); + gpio_put(PICO_GRN_PIN, 1); + gpio_put(PICO_BLU_PIN, 0); + } + else { + //green + gpio_put(PICO_RED_PIN, 0); + gpio_put(PICO_GRN_PIN, 1); + gpio_put(PICO_BLU_PIN, 0); + } + #endif } void led_blink_error(int amount) { @@ -71,6 +99,11 @@ void led_blink_error(int amount) { #ifdef RP2040ZERO ws2812_put_rgb(0, 0, 0); #endif + #ifdef BITFUNX + gpio_put(PICO_RED_PIN, 0); + gpio_put(PICO_GRN_PIN, 0); + gpio_put(PICO_BLU_PIN, 0); + #endif sleep_ms(500); /* start blinking */ for(int i = 0; i < amount; ++i) { @@ -80,13 +113,23 @@ void led_blink_error(int amount) { #ifdef RP2040ZERO ws2812_put_rgb(255, 0, 0); #endif + #ifdef BITFUNX + gpio_put(PICO_RED_PIN, 1); + gpio_put(PICO_GRN_PIN, 0); + gpio_put(PICO_BLU_PIN, 0); + #endif sleep_ms(500); #ifdef PICO set_led(PICO_LED_PIN, false); #endif #ifdef RP2040ZERO - ws2812_put_rgb(0, 0, 0); + ws2812_put_rgb(0, 0, 0); #endif + #ifdef BITFUNX + gpio_put(PICO_RED_PIN, 0); + gpio_put(PICO_GRN_PIN, 0); + gpio_put(PICO_BLU_PIN, 0); + #endif sleep_ms(500); } } @@ -105,6 +148,15 @@ void led_output_mc_change() { sleep_ms(100); ws2812_put_rgb(0, 0, 0); #endif + #ifdef BITFUNX + gpio_put(PICO_RED_PIN, 0); // blue + gpio_put(PICO_GRN_PIN, 0); + gpio_put(PICO_BLU_PIN, 1); + sleep_ms(100); + gpio_put(PICO_RED_PIN, 0); + gpio_put(PICO_GRN_PIN, 0); + gpio_put(PICO_BLU_PIN, 0); + #endif } void led_output_end_mc_list() { @@ -123,6 +175,15 @@ void led_output_end_mc_list() { sleep_ms(500); ws2812_put_rgb(0, 0, 0); #endif + #ifdef BITFUNX + gpio_put(PICO_RED_PIN, 1); // purple + gpio_put(PICO_GRN_PIN, 0); + gpio_put(PICO_BLU_PIN, 1); + sleep_ms(500); + gpio_put(PICO_RED_PIN, 0); + gpio_put(PICO_GRN_PIN, 0); + gpio_put(PICO_BLU_PIN, 0); + #endif } void led_output_new_mc() { @@ -141,11 +202,21 @@ void led_output_new_mc() { sleep_ms(1000); ws2812_put_rgb(0, 0, 0); #endif + #ifdef BITFUNX + gpio_put(PICO_RED_PIN, 0); // teal + gpio_put(PICO_GRN_PIN, 1); + gpio_put(PICO_BLU_PIN, 1); + sleep_ms(1000); + gpio_put(PICO_RED_PIN, 0); + gpio_put(PICO_GRN_PIN, 0); + gpio_put(PICO_BLU_PIN, 0); + #endif } // Attempt to check if device is a Raspberry Pi Pico W // Based on https://forums.raspberrypi.com/viewtopic.php?t=336884 int32_t is_pico_w() { + #ifdef PICO if (picoW == -1) { // When the VSYS_ADC is read on a Pico it will always be > 1V // On a Pico-W it will be < 1V when GPIO25 is low @@ -173,8 +244,14 @@ int32_t is_pico_w() { } } return picoW; + #endif + + #ifdef BITFUNX + return 0; + #endif } +#ifdef PICO void init_led(uint32_t pin) { if (!is_pico_w() || (pin != 25)) { gpio_init(pin); @@ -182,7 +259,7 @@ void init_led(uint32_t pin) { } } -#ifdef PICO + void set_led(uint32_t pin, uint32_t level) { if ((pin == 25) && is_pico_w()) { cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, level); diff --git a/src/main.c b/src/main.c index 29b7340..ee947b1 100644 --- a/src/main.c +++ b/src/main.c @@ -35,7 +35,7 @@ int main(void) { if(to_ms_since_boot(get_absolute_time()) > TUD_MOUNT_TIMEOUT && !tud_mount_status) break; } - + /* Pico powered by PSX, initialize memory card simulation */ simulate_memory_card(); diff --git a/src/memcard_manager.c b/src/memcard_manager.c index c6f7e5a..4a6ce7d 100644 --- a/src/memcard_manager.c +++ b/src/memcard_manager.c @@ -8,9 +8,6 @@ /* extension for memcard files */ static const char memcard_file_ext[] = ".MCR"; -/* filename to store previously loaded memcard index */ -static const char memcard_lastmemcardindex_filename[] = "LastMemcardIndex.dat"; - bool is_name_valid(uint8_t* filename) { if(!filename) return false; @@ -46,22 +43,29 @@ uint32_t update_prev_loaded_memcard_index(uint32_t index) { uint32_t retVal = MM_OK; FIL data_file; uint32_t buff_size = 100; - FRESULT res = f_open(&data_file, memcard_lastmemcardindex_filename, FA_CREATE_ALWAYS | FA_WRITE); + char line[buff_size]; + char buff[128]; + char finalname[MAX_MC_FILENAME_LEN + 13]; + memset(finalname, '\0', MAX_MC_FILENAME_LEN + 13); + memset(buff, '\0', 128); + FRESULT res = f_open(&data_file, "index.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE); if (res == FR_OK) { - /* int to string */ - char str_index[buff_size]; - int index_len = sprintf(str_index, "%d", index); - - /* overwrite the contents with new index */ - UINT bytes_written; - f_write(&data_file, str_index, index_len, &bytes_written); - if (bytes_written < index_len) { - /* error writing to file. disk full? */ - retVal = MM_FILE_WRITE_ERR; + f_gets(line, sizeof(line), &data_file); + printf("line be like: %s\n", line); + if(strncmp(line, "LASTMEMCARD:", 11) != 0){ + printf("LASTMEMCARD NOT PRESENT IN INDEX FILE\n"); + retVal = MM_FILE_OPEN_ERR; + }else{ + f_rewind(&data_file); + strcat(finalname, "LASTMEMCARD:"); + sprintf(buff,"%03d",index); + strcat(finalname, buff); + strcat(finalname, ".MCR\n"); + printf("%s\n", finalname); + f_puts(finalname, &data_file); } - f_close(&data_file); } - + f_close(&data_file); return retVal; } @@ -95,152 +99,124 @@ uint32_t memcard_manager_get(uint32_t index, uint8_t* out_filename) { return MM_BAD_PARAM; if(index < 0 || index > MAX_MC_IMAGES) return MM_INDEX_OUT_OF_BOUNDS; - uint32_t count = memcard_manager_count(); - if(index >= count) - return MM_INDEX_OUT_OF_BOUNDS; - uint8_t* image_names = malloc(((MAX_MC_FILENAME_LEN + 1) * count)); // allocate space for image names - if(!image_names) - return MM_ALLOC_FAIL; // malloc failed /* retrive images names */ FRESULT res; - DIR root; FILINFO f_info; - res = f_opendir(&root, ""); // open root directory - uint32_t i = 0; - if(res == FR_OK) { - while(true) { - res = f_readdir(&root, &f_info); - if(res != FR_OK || f_info.fname[0] == 0) break; - if(!(f_info.fattrib & AM_DIR)) { // not a directory - if(is_image_valid(f_info.fname)) { - strcpy(&image_names[(MAX_MC_FILENAME_LEN + 1) * i], f_info.fname); - ++i; - } - } - } + char finalname[MAX_MC_FILENAME_LEN + 1]; + memset(finalname, '\0', MAX_MC_FILENAME_LEN + 1); + sprintf(finalname,"%d",index); + strcat(finalname, ".MCR"); + res = f_stat(finalname, &f_info); + if(res == FR_NO_FILE){ + printf("THE MCR DOES NOT EXISTS\n"); + return MM_FILE_OPEN_ERR; } - /* sort names alphabetically */ - qsort(image_names, count, (MAX_MC_FILENAME_LEN + 1), (__compar_fn_t) strcmp); - strcpy(out_filename, &image_names[(MAX_MC_FILENAME_LEN + 1) * index]); - free(image_names); // free allocated memory + strcpy(out_filename, finalname); + printf("got: %s\n", out_filename); return MM_OK; } uint32_t memcard_manager_get_prev_loaded_memcard_index() { /* read which memcard to load from last session from SD card */ uint32_t index = 0; + char finalname[MAX_MC_FILENAME_LEN + 1]; + memset(finalname, '\0', MAX_MC_FILENAME_LEN + 1); FIL data_file; uint32_t buff_size = 100; - FRESULT res = f_open(&data_file, memcard_lastmemcardindex_filename, FA_OPEN_EXISTING | FA_READ); + FRESULT res = f_open(&data_file, "index.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE); + char line[buff_size]; if (res == FR_OK) { - char line[buff_size]; - if (f_gets(line, sizeof(line), &data_file)) { - /* string to int (base 10) */ - index = (uint32_t)strtol(line, (char**)NULL, 10); + f_gets(line, sizeof(line), &data_file); + if(strncmp(line, "LASTMEMCARD:", 11) != 0){ + char lastmem[MAX_MC_FILENAME_LEN + 13]; + memset(lastmem, '\0', MAX_MC_FILENAME_LEN + 13); + strcat(lastmem, "LASTMEMCARD:\n"); + printf("LASTMEMCARD NOT PRESENT IN INDEX FILE, WRITING...\n"); + f_puts(lastmem, &data_file); + }else{ + int j = 0; + for(int i = 12; i < MAX_MC_FILENAME_LEN + 1; i++){ + if(line[i] == '\n'){ + break; + }else{ + finalname[j] = line[i]; + j++; + } + } + index = atoi(finalname); } - f_close(&data_file); } + f_close(&data_file); return index; } -uint32_t memcard_manager_get_next(uint8_t* filename, uint8_t* out_nextfile) { +uint32_t memcard_manager_get_next(uint8_t* filename, uint8_t* out_nextfile, bool skip) { if(!filename || !out_nextfile) return MM_BAD_PARAM; - uint32_t count = memcard_manager_count(); - uint32_t buff_size = (MAX_MC_FILENAME_LEN + 1) * count; - uint8_t* image_names = malloc(buff_size); // allocate space for image names - if(!image_names) - return MM_ALLOC_FAIL; // malloc failed /* retrive images names */ FRESULT res; DIR root; FILINFO f_info; - res = f_opendir(&root, ""); // open root directory - uint32_t i = 0; - if(res == FR_OK) { - while(true) { - res = f_readdir(&root, &f_info); - if(res != FR_OK || f_info.fname[0] == 0) break; - if(!(f_info.fattrib & AM_DIR)) { // not a directory - if(is_image_valid(f_info.fname)) { - strcpy(&image_names[(MAX_MC_FILENAME_LEN + 1) * i], f_info.fname); - ++i; - } - } - } + //res = f_opendir(&root, ""); // open root directory + char finalname[MAX_MC_FILENAME_LEN + 1]; + memset(finalname, '\0', MAX_MC_FILENAME_LEN + 1); + uint32_t index = atoi(filename); + if(skip){ + index += 5; + }else{ + index++; } - /* sort names alphabetically */ - qsort(image_names, count, (MAX_MC_FILENAME_LEN + 1), (__compar_fn_t) strcmp); - /* find current and return following one */ - bool found = false; - for(uint32_t i = 0; i < buff_size; i = i + (MAX_MC_FILENAME_LEN + 1)) { - if(!strcmp(filename, &image_names[i])) { - int32_t next_i = i + (MAX_MC_FILENAME_LEN + 1); - if(next_i < buff_size) { - int32_t new_index = next_i / ((MAX_MC_FILENAME_LEN + 1)); - update_prev_loaded_memcard_index(new_index); - strcpy(out_nextfile, &image_names[next_i]); - found = true; - break; + sprintf(finalname,"%d",index); + strcat(finalname, ".MCR"); + res = f_stat(finalname, &f_info); + if(res == FR_NO_FILE){ + while(res == FR_NO_FILE){ + printf("THE MCR DOES NOT EXISTS\n"); + memset(finalname, '\0', MAX_MC_FILENAME_LEN + 1); + index--; + if(index < atoi(filename)){ + return MM_NO_ENTRY; } + sprintf(finalname,"%d",index); + strcat(finalname, ".MCR"); + res = f_stat(finalname, &f_info); } } - free(image_names); // free allocated memory + strcpy(out_nextfile, finalname); + printf("next mc is: %s", out_nextfile); /* return */ - if(found) - return MM_OK; - else - return MM_NO_ENTRY; + return MM_OK; } -uint32_t memcard_manager_get_prev(uint8_t* filename, uint8_t* out_prevfile) { +uint32_t memcard_manager_get_prev(uint8_t* filename, uint8_t* out_prevfile, bool skip) { if(!filename || !out_prevfile) return MM_BAD_PARAM; - uint32_t count = memcard_manager_count(); - uint32_t buff_size = (MAX_MC_FILENAME_LEN + 1) * count; - uint8_t* image_names = malloc(buff_size); // allocate space for image names - if(!image_names) - return MM_ALLOC_FAIL; // malloc failed /* retrive images names */ FRESULT res; - DIR root; FILINFO f_info; - res = f_opendir(&root, ""); // open root directory - uint32_t i = 0; - if(res == FR_OK) { - while(true) { - res = f_readdir(&root, &f_info); - if(res != FR_OK || f_info.fname[0] == 0) break; - if(!(f_info.fattrib & AM_DIR)) { // not a directory - if(is_image_valid(f_info.fname)) { - strcpy(&image_names[(MAX_MC_FILENAME_LEN + 1) * i], f_info.fname); - ++i; - } - } - } + //res = f_opendir(&root, ""); // open root directory + char finalname[MAX_MC_FILENAME_LEN + 1]; + memset(finalname, '\0', MAX_MC_FILENAME_LEN + 1); + uint32_t index = atoi(filename); + if(skip){ + index -= 5; + }else{ + index--; } - /* sort names alphabetically */ - qsort(image_names, count, (MAX_MC_FILENAME_LEN + 1), (__compar_fn_t) strcmp); - /* find current and return prior one */ - bool found = false; - for(uint32_t i = 0; i < buff_size; i = i + (MAX_MC_FILENAME_LEN + 1)) { - if(!strcmp(filename, &image_names[i])) { - int32_t prev_i = i - (MAX_MC_FILENAME_LEN + 1); - if(prev_i >= 0) { - int32_t new_index = prev_i / (MAX_MC_FILENAME_LEN + 1); - update_prev_loaded_memcard_index(new_index); - strcpy(out_prevfile, &image_names[prev_i]); - found = true; - break; - } - } + if(index < 0){ + index = 0; } - free(image_names); // free allocated memory - /* return */ - if(found) - return MM_OK; - else + sprintf(finalname,"%d",index); + strcat(finalname, ".MCR"); + res = f_stat(finalname, &f_info); + if(res == FR_NO_FILE){ + printf("THE MCR DOES NOT EXISTS\n"); return MM_NO_ENTRY; + } + strcpy(out_prevfile, finalname); + printf("prev mc is: %s", out_prevfile); + /* return */ + return MM_OK; } uint32_t memcard_manager_create(uint8_t* out_filename) { @@ -359,4 +335,133 @@ uint32_t memcard_manager_create(uint8_t* out_filename) { } update_prev_loaded_memcard_index(memcard_n - 1); return MM_OK; -} \ No newline at end of file +} + +uint32_t create_index(uint8_t *vec, uint8_t size, uint8_t *out_filename){ + uint8_t new_name[MAX_MC_FILENAME_LEN + 1]; + + FIL fptr; + + FRESULT fr; + fr = f_open(&fptr, "index.txt", FA_OPEN_ALWAYS | FA_READ |FA_WRITE); + + if (fr != FR_OK) { + printf("error opening file. Skill issue\n"); + return FR_DISK_ERR; + } + else { + printf("The file is created Successfully.\n"); + } + + char data[128]; + char n_data[128]; + int ind = 0; + --size; + + char lastmem[MAX_MC_FILENAME_LEN + 1]; + + //we init the var otherwise it will be dirty as hell + memset(data, '\0', 128); + memset(new_name, '\0', MAX_MC_FILENAME_LEN + 1); + memset(out_filename, '\0', MAX_MC_FILENAME_LEN + 1); + memset(lastmem, '\0', MAX_MC_FILENAME_LEN + 1); + strcat(data, "\n"); + bool backslash = false; + for(int i = 0; i < size; i++){ + if(vec[i] == ':'){ + for(int j = i + 1; j < size; j++){ + + // just to eliminate backslash from str + if(vec[j] == 92 && backslash == false){ + backslash = true; + continue; + } + if(vec[j] == ';'){ + ind++; + break; + } + n_data[ind] = vec[j]; + ind++; + } + } + } + + strcat(data, n_data); + printf("the array is: %s\n", data); + + // check if the string is \PSIO and if it is we get the hell out of here + if(strcmp(n_data, "\\PSIO") == 0 ||strcmp(n_data, "/XSTATION") == 0 || strcmp(n_data, "UNIROM") == 0 || strcmp(n_data, "\\UNIROM_B.EXE") == 0){ + printf("\nSkipped psio/xstation/UNIROM\n"); + f_close(&fptr); + return 1; + }else if(strlen(n_data) < 5){ + printf("array too short, a comunication error might be occured\n"); + f_close(&fptr); + return 1; + } + + if (fr == FR_OK) { + char line[256]; + bool flag = false; + ind--; + //checks if the ID is already present + while (f_gets(line, sizeof(line), &fptr)){ + if(strncmp(n_data, line, ind) == 0){ + flag = true; + printf("GAMEID already present\n"); + break; + } + } + //if it is we search the memorycard assigned to it + if(flag){ + if(line[ind] == ':'){ + int len = 1; + char buff[MAX_MC_FILENAME_LEN + 1]; + memset(buff, '\0', MAX_MC_FILENAME_LEN + 1); + ind++; + for(int y = 0; y < MAX_MC_FILENAME_LEN + 1; y++){ + if(line[ind] == '\n'){ + break; + } + new_name[y] = line[ind]; + ind++; + } + strcpy(out_filename, new_name); + + //updating lastmemcard id + f_rewind(&fptr); + strcat(lastmem, "LASTMEMCARD:"); + sprintf(buff, "%03d", atoi(new_name)); + strcat(lastmem, buff); + strcat(lastmem, ".MCR\n"); + f_puts(lastmem, &fptr); + printf("memcard is: %s\n", out_filename); + }else{ + printf("\n COULD NOT FIND ':' CHAR FIX YOUR INDEX FILE\n"); + f_close(&fptr); + return FR_DISK_ERR; + } + + } + //if it is not, then we add the GAMEID with the new MCR file + else if(!flag){ + printf("GAMEID not present, writing to index.txt\n"); + uint32_t status = memcard_manager_create(new_name); + //we concatenate stuff to have a valid id with the memcard + + strcat(data, ":"); + strcat(data, new_name); + printf("\n what i write: %s", data); + f_puts(data, &fptr); + strcpy(out_filename, new_name); + printf("\n THE NEW MCR IS: %s\n", out_filename); + } + }else{ + //this should not happen + printf("WE HAVE A HUGE PROBLEM!\n"); + f_close(&fptr); + return FR_DISK_ERR; + } + f_close(&fptr); + return MM_OK; +} diff --git a/src/memcard_simulator.c b/src/memcard_simulator.c index fb3a6ed..a935aeb 100644 --- a/src/memcard_simulator.c +++ b/src/memcard_simulator.c @@ -1,4 +1,5 @@ #include +#include #include "memcard_simulator.h" #include "stdio.h" #include "pico/multicore.h" @@ -47,9 +48,13 @@ memory_card_t mc; bool request_next_mc = false; bool request_prev_mc = false; bool request_new_mc = false; +bool skip = false; +bool indexfile = false; mutex_t write_transaction; queue_t mc_sector_sync_queue; const uint8_t id_data[] = {0x04, 0x00, 0x00, 0x80}; +uint8_t game_id_len = 0; +uint8_t game_id[24]; void simulate_mc_reconnect() { irq_set_enabled(IO_IRQ_BANK0, false); @@ -180,25 +185,54 @@ void process_memcard_cmd() { RECV_CMD(); SEND(0x27); // card present RECV_CMD(); + SEND(0xFF); // send ack printf("MC Received Ping from PS\n"); } break; + case MEMCARD_PREV_CARD: + { + SEND(0x00); // byte 1 reserved + RECV_CMD(); + SEND(0x00); // byte 2 reserved + RECV_CMD(); + SEND(0x20); // SUCC + RECV_CMD(); + SEND(0xFF); // TERMINATION + request_prev_mc = true; + } + break; + case MEMCARD_NEXT_CARD: + { + SEND(0x00); // byte 1 reserved + RECV_CMD(); + SEND(0x00); // byte 2 reserved + RECV_CMD(); + SEND(0x20); // SUCCESS + RECV_CMD(); + SEND(0xFF); // TERMINATION + request_next_mc = true; + + } + break; + #ifdef GAMEID case MEMCARD_GAMEID: { SEND(0x00); - uint8_t game_id_len = RECV_CMD(); + RECV_CMD(); //reserved + SEND(0x00); //reserved + game_id_len = RECV_CMD(); data = 0x00; - uint8_t game_id[256] = {0}; /* read game id */ - for(uint32_t i = 0; i < game_id_len; i++) { + for(int i = 0; i < game_id_len; ++i){ SEND(data); // ack previous data data = RECV_CMD(); game_id[i] = data; } SEND(data); // ack last byte - printf("Game ID: %s\n", game_id); + indexfile = true; } break; + #endif default: break; } @@ -215,9 +249,17 @@ void process_pad_cmd() { /* during pad interaction never call SEND() only int case START & SELECT & UP: request_next_mc = true; break; + case START & SELECT & RIGHT: + skip = true; + request_next_mc = true; + break; case START & SELECT & DOWN: request_prev_mc = true; break; + case START & SELECT & LEFT: + skip = true; + request_prev_mc = true; + break; case START & SELECT & TRIANGLE: request_new_mc = true; break; @@ -314,9 +356,11 @@ _Noreturn int simulate_memory_card() { while(true) led_blink_error(1); } + printf("sd card mounted\n"); uint32_t status = memory_card_init(&mc); if(status != MC_OK) { + printf("could not init memcard\n"); while(true) { led_blink_error(status); sleep_ms(2000); @@ -326,14 +370,17 @@ _Noreturn int simulate_memory_card() { if(status != MM_OK) { status = memcard_manager_get(0, mc_file_name); // revert to first mem card if failing to load previously loaded card if(status != MM_OK) { + printf("could not load memcard\n"); while(true) { led_blink_error(status); sleep_ms(1000); } } } + printf("mcfilename: %s\n", mc_file_name); status = memory_card_import(&mc, mc_file_name); if(status != MC_OK) { + printf("could not import memcard\n"); while(true) { led_blink_error(status); sleep_ms(2000); @@ -356,6 +403,8 @@ _Noreturn int simulate_memory_card() { /* SMs are automatically enabled on first SEL reset */ + memset(game_id, 0, 24); + /* Launch memory card thread */ printf("Starting simulation core..."); multicore_launch_core1(simulation_thread); @@ -375,11 +424,12 @@ _Noreturn int simulate_memory_card() { request_next_mc = false; request_prev_mc = false; } else { + uint8_t test = 0x10; uint8_t new_file_name[MAX_MC_FILENAME_LEN + 1]; if(request_next_mc) - status = memcard_manager_get_next(mc_file_name, new_file_name); + status = memcard_manager_get_next(mc_file_name, new_file_name, skip); else if (request_prev_mc) - status = memcard_manager_get_prev(mc_file_name, new_file_name); + status = memcard_manager_get_prev(mc_file_name, new_file_name, skip); if(status != MM_OK) { led_output_end_mc_list(); request_next_mc = false; @@ -394,11 +444,14 @@ _Noreturn int simulate_memory_card() { /* switch mc */ strcpy(mc_file_name, new_file_name); status = memory_card_import(&mc, mc_file_name); + update_prev_loaded_memcard_index(atoi(mc_file_name)); + printf("mc is: %s\n", mc_file_name); if(status != MC_OK) led_blink_error(status); simulate_mc_reconnect(); request_next_mc = false; request_prev_mc = false; + skip = false; mutex_exit(&write_transaction); } } @@ -423,6 +476,33 @@ _Noreturn int simulate_memory_card() { simulate_mc_reconnect(); request_new_mc = false; mutex_exit(&write_transaction); - } + } else if(indexfile){ + mutex_enter_blocking(&write_transaction); + printf("%s\n", game_id); + /* ensure latest write operations have been synced */ + led_output_sync_status(true); + while(!queue_is_empty(&mc_sector_sync_queue)) + queue_sync_step(&mc_sector_sync_queue, mc_file_name); + led_output_sync_status(false); + uint8_t new_name[MAX_MC_FILENAME_LEN + 1]; + status = create_index(game_id, game_id_len, new_name); //we do game id stuff here + if(status == MM_OK) { + led_output_new_mc(); + strcpy(mc_file_name, new_name); + status = memory_card_import(&mc, mc_file_name); // switch to newly/already created mc image + if(status != MC_OK){ + led_blink_error(status); + } + printf("\n THE NEW MC MOUNTED IS: %s\n", mc_file_name); + }else if(status == 1){ + //he did nothing + printf("\ndid nothing\n"); + }else + led_blink_error(status); + simulate_mc_reconnect(); + memset(game_id, '\0', 24); + indexfile = false; + mutex_exit(&write_transaction); + } } } \ No newline at end of file