diff --git a/UNFLoader/device.cpp b/UNFLoader/device.cpp index fee74bf..bf89676 100644 --- a/UNFLoader/device.cpp +++ b/UNFLoader/device.cpp @@ -9,8 +9,8 @@ Passes flashcart communication to more specific functions #include "device_everdrive.h" #include "device_sc64.h" #include -#include #include +#include #pragma comment(lib, "Include/FTD2XX.lib") @@ -19,9 +19,11 @@ Passes flashcart communication to more specific functions *********************************/ DeviceError (*funcPointer_open)(FTDIDevice*); -void (*funcPointer_sendrom)(FTDIDevice*, FILE *file, uint32_t size); +DeviceError (*funcPointer_sendrom)(FTDIDevice*, uint8_t* rom, uint32_t size); bool (*funcPointer_testdebug)(FTDIDevice*); -void (*funcPointer_senddata)(FTDIDevice*, int datatype, char *data, uint32_t size); +bool (*funcPointer_shouldpadrom)(); +uint32_t (*funcPointer_maxromsize)(); +DeviceError (*funcPointer_senddata)(FTDIDevice*, int datatype, char *data, uint32_t size); DeviceError (*funcPointer_close)(FTDIDevice*); static void device_set_64drive1(FTDIDevice* cart, uint32_t index); @@ -34,12 +36,11 @@ static void device_set_sc64(FTDIDevice* cart, uint32_t index); Globals *********************************/ +// File static char* local_rompath = NULL; -static bool local_isz64 = false; -static CartType local_carttype = CART_NONE; -static CICType local_cictype = CIC_NONE; -static SaveType local_savetype = SAVE_NONE; +// Cart +static CartType local_carttype = CART_NONE; static FTDIDevice local_cart; @@ -121,13 +122,15 @@ static void device_set_64drive1(FTDIDevice* cart, uint32_t index) { // Set cart settings cart->device_index = index; - cart->synchronous = 0; + cart->synchronous = false; cart->carttype = CART_64DRIVE1; // Set function pointers funcPointer_open = &device_open_64drive; - /* + funcPointer_maxromsize = &device_maxromsize_64drive; funcPointer_sendrom = &device_sendrom_64drive; + funcPointer_shouldpadrom = &device_shouldpadrom_64drive; + /* funcPointer_testdebug = &device_testdebug_64drive; funcPointer_senddata = &device_senddata_64drive; */ @@ -148,7 +151,7 @@ static void device_set_64drive2(FTDIDevice* cart, uint32_t index) device_set_64drive1(cart, index); // But modify the important cart settings - cart->synchronous = 1; + cart->synchronous = true; cart->carttype = CART_64DRIVE2; } @@ -168,6 +171,8 @@ static void device_set_everdrive(FTDIDevice* cart, uint32_t index) // Set function pointers funcPointer_open = &device_open_everdrive; + funcPointer_maxromsize = &device_maxromsize_everdrive; + funcPointer_shouldpadrom = &device_shouldpadrom_everdrive; /* funcPointer_sendrom = &device_sendrom_everdrive; funcPointer_testdebug = &device_testdebug_everdrive; @@ -192,6 +197,8 @@ static void device_set_sc64(FTDIDevice* cart, uint32_t index) // Set function pointers funcPointer_open = &device_open_sc64; + funcPointer_maxromsize = &device_maxromsize_sc64; + funcPointer_shouldpadrom = &device_shouldpadrom_sc64; /* funcPointer_sendrom = &device_sendrom_sc64; funcPointer_testdebug = &device_testdebug_sc64; @@ -225,6 +232,61 @@ bool device_isopen() } +/*============================== + device_maxromsize + Gets the max ROM size that + the flashcart supports + @return The max ROM size +==============================*/ + +uint32_t device_getmaxromsize() +{ + return funcPointer_maxromsize(); +} + + +/*============================== + device_sendrom + Opens the ROM and calls the function to send it to the flashcart + @param The ROM FILE pointer + @param The size of the ROM in bytes +==============================*/ + +DeviceError device_sendrom(FILE* rom, uint32_t filesize) +{ + bool is_z64 = false; + uint8_t* rom_buffer; + DeviceError err; + + // Pad the ROM if necessary + if (funcPointer_shouldpadrom()) + filesize = calc_padsize(filesize/(1024*1024))*1024*1024; + + // Check we managed to malloc + rom_buffer = (uint8_t*) malloc(sizeof(uint8_t)*filesize); + if (rom_buffer == NULL) + return DEVICEERR_MALLOCFAIL; + + // Read the ROM into a buffer + fread(rom_buffer, 1, filesize, rom); + + // Check if we have a Z64 ROM + if (!(rom_buffer[0] == 0x80 && rom_buffer[1] == 0x37 && rom_buffer[2] == 0x12 && rom_buffer[3] == 0x40)) + is_z64 = true; + fseek(rom, 0, SEEK_SET); + + // Byteswap if it's a Z64 ROM + if (is_z64) + for (uint32_t i=0; i> 8) & 0x0000ff00) | ((val >> 24)); +} + + +/*============================== + calc_padsize + Returns the correct size a ROM should be. Code taken from: + https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + @param The current ROM filesize + @return The correct ROM filesize +==============================*/ + +uint32_t calc_padsize(uint32_t size) +{ + size--; + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size |= size >> 8; + size |= size >> 16; + size++; + return size; +} + + +/*============================== + romhash + Returns an int with a simple hash of the inputted data + @param The data to hash + @param The size of the data + @return The hash number +==============================*/ + +uint32_t romhash(uint8_t *buff, uint32_t len) +{ + uint32_t i; + uint32_t hash=0; + for (i=0; i + #include + #include #include @@ -27,7 +29,8 @@ CIC_X103 = 4, CIC_X105 = 5, CIC_X106 = 6, - CIC_5101 = 7 + CIC_5101 = 7, + CIC_8303 = 8 } CICType; typedef enum { @@ -52,6 +55,7 @@ DEVICEERR_PURGEFAIL, DEVICEERR_READFAIL, DEVICEERR_WRITEFAIL, + DEVICEERR_WRITEZERO, DEVICEERR_CLOSEFAIL, DEVICEERR_BITMODEFAIL_RESET, DEVICEERR_BITMODEFAIL_SYNCFIFO, @@ -63,6 +67,8 @@ DEVICEERR_NOCOMPSIG, DEVICEERR_READPACKSIZEFAIL, DEVICEERR_BADPACKSIZE, + DEVICEERR_MALLOCFAIL, + DEVICEERR_64D_8303USB, DEVICEERR_SC64_CTRLRESETFAIL, DEVICEERR_SC64_CTRLRELEASEFAIL, DEVICEERR_SC64_FIRMWARECHECKFAIL, @@ -77,13 +83,13 @@ typedef struct { CartType carttype; CICType cictype; - CICType savetype; + SaveType savetype; DWORD device_count; uint32_t device_index; FT_DEVICE_LIST_INFO_NODE* device_info; FT_STATUS status; FT_HANDLE handle; - uint32_t synchronous; // For 64Drive + bool synchronous; // For 64Drive DWORD bytes_written; DWORD bytes_read; } FTDIDevice; @@ -93,18 +99,27 @@ Function Prototypes *********************************/ + // Main device functions DeviceError device_find(); DeviceError device_open(); + uint32_t device_getmaxromsize(); + DeviceError device_sendrom(FILE* rom, uint32_t filesize); bool device_isopen(); DeviceError device_close(); - void device_setrom(char* path); + // Device configuration + bool device_setrom(char* path); void device_setcart(CartType cart); void device_setcic(CICType cic); void device_setsave(SaveType save); char* device_getrom(); CartType device_getcart(); + // Helper functions + #define SWAP(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) // From https://graphics.stanford.edu/~seander/bithacks.html#SwappingValuesXOR uint32_t swap_endian(uint32_t val); + uint32_t calc_padsize(uint32_t size); + uint32_t romhash(uint8_t *buff, uint32_t len); + CICType cic_from_hash(uint32_t hash); #endif \ No newline at end of file diff --git a/UNFLoader/device_64drive.cpp b/UNFLoader/device_64drive.cpp index a12feb0..8e2977b 100644 --- a/UNFLoader/device_64drive.cpp +++ b/UNFLoader/device_64drive.cpp @@ -7,9 +7,18 @@ here is courtesy of MarshallH's 64Drive USB tool: ***************************************************************/ #include "device_64drive.h" +#include +#include #include +/********************************* + Function Prototypes +*********************************/ + +DeviceError device_sendcmd_64drive(FTDIDevice* cart, uint8_t command, bool reply, uint32_t* result, uint32_t numparams, ...); + + /*============================== device_test_64drive1 Checks whether the device passed as an argument is 64Drive HW1 @@ -38,6 +47,110 @@ bool device_test_64drive2(FTDIDevice* cart, uint32_t index) } +/*============================== + device_maxromsize_64drive + Gets the max ROM size that + the 64Drive supports + @return The max ROM size +==============================*/ + +uint32_t device_maxromsize_64drive() +{ + // TODO: Extended address mode for 256 MB ROMs + return 64*1024*1024; +} + + +/*============================== + device_shouldpadrom_64drive + Checks if the ROM should be + padded before uploading on the + 64Drive. + @return Whether or not to pad + the ROM. +==============================*/ + +bool device_shouldpadrom_64drive() +{ + return true; +} + + +/*============================== + device_sendcmd_64drive + Opens the USB pipe + @param A pointer to the cart context + @param The command to send + @param A bool stating whether a reply should be expected + @param A pointer to store the result of the reply + @param The number of extra params to send + @param The extra variadic commands to send + @return The device error, or OK +==============================*/ + +DeviceError device_sendcmd_64drive(FTDIDevice* cart, uint8_t command, bool reply, uint32_t* result, uint32_t numparams, ...) +{ + uint8_t send_buff[32]; + uint32_t recv_buff[32]; + va_list params; + + // Clear the buffers + memset(send_buff, 0, 32*sizeof(uint8_t)); + memset(recv_buff, 0, 32*sizeof(uint32_t)); + + // Setup the command to send + send_buff[0] = command; + send_buff[1] = 0x43; // C + send_buff[2] = 0x4D; // M + send_buff[3] = 0x44; // D + + // Append extra arguments to the command if needed + va_start(params, numparams); + if (numparams > 0) + *(uint32_t*)&send_buff[4] = swap_endian(va_arg(params, uint32_t)); + if (numparams > 1) + *(uint32_t*)&send_buff[8] = swap_endian(va_arg(params, uint32_t)); + va_end(params); + + // Write to the cart + if (FT_Write(cart->handle, send_buff, 4+(numparams*4), &cart->bytes_written) != FT_OK) + return DEVICEERR_WRITEFAIL; + if (cart->bytes_written == 0) + return DEVICEERR_WRITEZERO; + + // If the command expects a response + if (reply) + { + uint32_t expected = command << 24 | 0x00504D43; + + // These two instructions do not return a success, so ignore them + if (command == DEV_CMD_PI_WR_BL || command == DEV_CMD_PI_WR_BL_LONG) + return DEVICEERR_OK; + + // Read the reply from the 64Drive + if (FT_Read(cart->handle, recv_buff, 4, &cart->bytes_read) != FT_OK) + return DEVICEERR_NOCOMPSIG; + + // Store the result if requested + if (result != NULL) + { + (*result) = swap_endian(recv_buff[0]); + + // Read the rest of the stuff and the CMP as well + if (FT_Read(cart->handle, recv_buff, 4, &cart->bytes_read) != FT_OK) + return DEVICEERR_NOCOMPSIG; + if (FT_Read(cart->handle, recv_buff, 4, &cart->bytes_read) != FT_OK) + return DEVICEERR_NOCOMPSIG; + } + + // Check that we received the signal that the operation completed + if (memcmp(recv_buff, &expected, 4) != 0) + return DEVICEERR_NOCOMPSIG; + } + return DEVICEERR_OK; +} + + /*============================== device_open_64drive Opens the USB pipe @@ -76,6 +189,175 @@ DeviceError device_open_64drive(FTDIDevice* cart) } +/*============================== + device_sendrom_64drive + Sends the ROM to the flashcart + @param A pointer to the cart context + @param A pointer to the ROM to send + @param The size of the ROM + @return The device error, or OK +==============================*/ + +DeviceError device_sendrom_64drive(FTDIDevice* cart, uint8_t* rom, uint32_t size) +{ + uint32_t ram_addr = 0x0; + uint32_t bytes_left = size; + uint32_t bytes_done = 0; + uint32_t bytes_do; + uint32_t chunk = 0; + DWORD cmps; + + // Start by setting the CIC + if (cart->cictype != CIC_NONE) + { + DeviceError err = device_sendcmd_64drive(cart, DEV_CMD_SETCIC, false, NULL, 1, (1 << 31) | cart->cictype, 0); + if (err != DEVICEERR_OK) + return err; + } + else + { + CICType cic; + DeviceError err; + uint8_t* bootcode = (uint8_t*)malloc(4032); + if (bootcode == NULL) + return DEVICEERR_MALLOCFAIL; + + // Read the bootcode and store it + memcpy(bootcode, rom+0x40, 4032); + + // Pick the CIC from the bootcode + cic = cic_from_hash(romhash(bootcode, 4032)); + if (cic != CIC_NONE) + { + err = device_sendcmd_64drive(cart, DEV_CMD_SETCIC, false, NULL, 1, (1 << 31) | cic, 0); + if (err != DEVICEERR_OK) + return err; + } + + // Free used memory + free(bootcode); + } + + // Then, set the save type + if (cart->savetype != SAVE_NONE) + { + DeviceError err = device_sendcmd_64drive(cart, DEV_CMD_SETSAVE, false, NULL, 1, cart->savetype, 0); + if (err != DEVICEERR_OK) + return err; + } + + // Decide a better, more optimized chunk size + if (size > 16*1024*1024) + chunk = 32; + else if ( size > 2*1024*1024) + chunk = 16; + else + chunk = 4; + chunk *= 128*1024; // Convert to megabytes + + /* + for ( ; ; ) + { + int i, ch; + + // Decide how many bytes to send + if (bytes_left >= chunk) + bytes_do = chunk; + else + bytes_do = bytes_left; + + // If we have an uneven number of bytes, fix that + if (bytes_do%4 != 0) + bytes_do -= bytes_do%4; + + // End if we've got nothing else to send + if (bytes_do <= 0) + break; + + // Check if ESC was pressed + ch = getch(); + if (ch == CH_ESCAPE) + { + pdprint_replace("ROM upload canceled by the user\n", CRDEF_PROGRAM); + free(rom_buffer); + return; + } + + // Try to send chunks + for (i=0; i<2; i++) + { + int j; + + // If we failed the first time, clear the USB and try again + if (i == 1) + { + FT_ResetPort(cart->handle); + FT_ResetDevice(cart->handle); + FT_Purge(cart->handle, FT_PURGE_RX | FT_PURGE_TX); + } + + // Send the chunk to RAM + device_sendcmd_64drive(cart, DEV_CMD_LOADRAM, false, NULL, 2, ram_addr, (bytes_do & 0xffffff) | 0 << 24); + fread(rom_buffer, bytes_do, 1, file); + if (global_z64) + for (j=0; jhandle, rom_buffer, bytes_do, &cart->bytes_written); + + // If we managed to write, don't try again + if (cart->bytes_written) + break; + } + + // Check for a timeout + if (cart->bytes_written == 0) + { + free(rom_buffer); + terminate("64Drive timed out."); + } + + // Ignore the success response + cart->status = FT_Read(cart->handle, rom_buffer, 4, &cart->bytes_read); + + // Keep track of how many bytes were uploaded + bytes_left -= bytes_do; + bytes_done += bytes_do; + ram_addr += bytes_do; + } + + // Wait for the CMP signal + #ifndef LINUX + Sleep(50); + #else + usleep(50); + #endif + + // Read the incoming CMP signals to ensure everything's fine + FT_GetQueueStatus(cart->handle, &cmps); + while (cmps > 0) + { + // Read the CMP signal and ensure it's correct + FT_Read(cart->handle, rom_buffer, 4, &cart->bytes_read); + if (rom_buffer[0] != 'C' || rom_buffer[1] != 'M' || rom_buffer[2] != 'P' || rom_buffer[3] != 0x20) + terminate("Received wrong CMPlete signal: %c %c %c %02x.", rom_buffer[0], rom_buffer[1], rom_buffer[2], rom_buffer[3]); + + // Wait a little bit before reading the next CMP signal + #ifndef LINUX + Sleep(50); + #else + usleep(50); + #endif + FT_GetQueueStatus(cart->handle, &cmps); + } + + // Print that we've finished + pdprint_replace("ROM successfully uploaded in %.2f seconds!\n", CRDEF_PROGRAM, ((double)(clock()-upload_time))/CLOCKS_PER_SEC); + */ + + return DEVICEERR_OK; +} + + /*============================== device_close_64drive Closes the USB pipe @@ -88,4 +370,4 @@ DeviceError device_close_64drive(FTDIDevice* cart) if (FT_Close(cart->handle) != FT_OK) return DEVICEERR_CLOSEFAIL; return DEVICEERR_OK; -} +} \ No newline at end of file diff --git a/UNFLoader/device_64drive.h b/UNFLoader/device_64drive.h index 5b8eda0..8e2d0ca 100644 --- a/UNFLoader/device_64drive.h +++ b/UNFLoader/device_64drive.h @@ -37,10 +37,12 @@ bool device_test_64drive1(FTDIDevice* cart, uint32_t index); bool device_test_64drive2(FTDIDevice* cart, uint32_t index); DeviceError device_open_64drive(FTDIDevice* cart); + DeviceError device_sendrom_64drive(FTDIDevice* cart, uint8_t* rom, uint32_t size); + uint32_t device_maxromsize_64drive(); + bool device_shouldpadrom_64drive(); /* - void device_sendrom_64drive(FTDIDevice* cart, FILE *file, uint32_t size); bool device_testdebug_64drive(FTDIDevice* cart); - void device_senddata_64drive(FTDIDevice* cart, int datatype, char* data, uint32_t size); + DeviceError device_senddata_64drive(FTDIDevice* cart, int datatype, char* data, uint32_t size); */ DeviceError device_close_64drive(FTDIDevice* cart); diff --git a/UNFLoader/device_everdrive.cpp b/UNFLoader/device_everdrive.cpp index b52835c..53fcb2b 100644 --- a/UNFLoader/device_everdrive.cpp +++ b/UNFLoader/device_everdrive.cpp @@ -63,6 +63,34 @@ DeviceError device_test_everdrive(FTDIDevice* cart, uint32_t index) } +/*============================== + device_maxromsize_everdrive + Gets the max ROM size that + the EverDrive supports + @return The max ROM size +==============================*/ + +uint32_t device_maxromsize_everdrive() +{ + return 64*1024*1024; +} + + +/*============================== + device_shouldpadrom_everdrive + Checks if the ROM should be + padded before uploading on the + EverDrive. + @return Whether or not to pad + the ROM. +==============================*/ + +bool device_shouldpadrom_everdrive() +{ + return false; +} + + /*============================== device_open_everdrive Opens the USB pipe diff --git a/UNFLoader/device_everdrive.h b/UNFLoader/device_everdrive.h index e9efb5f..721a825 100644 --- a/UNFLoader/device_everdrive.h +++ b/UNFLoader/device_everdrive.h @@ -2,7 +2,6 @@ #define __DEVICE_EVERDRIVE_HEADER #include "device.h" - #include /********************************* @@ -11,8 +10,10 @@ DeviceError device_test_everdrive(FTDIDevice* cart, uint32_t index); DeviceError device_open_everdrive(FTDIDevice* cart); + uint32_t device_maxromsize_everdrive(); + bool device_shouldpadrom_everdrive(); /* - void device_sendrom_everdrive(FTDIDevice* cart, FILE *file, uint32_t size); + void device_sendrom_everdrive(FTDIDevice* cart, uint8_t* rom, uint32_t size); bool device_testdebug_everdrive(FTDIDevice* cart); void device_senddata_everdrive(FTDIDevice* cart, int datatype, char *data, uint32_t size); */ diff --git a/UNFLoader/device_sc64.cpp b/UNFLoader/device_sc64.cpp index 843225f..c3db7ef 100644 --- a/UNFLoader/device_sc64.cpp +++ b/UNFLoader/device_sc64.cpp @@ -125,6 +125,34 @@ bool device_test_sc64(FTDIDevice* cart, uint32_t index) } +/*============================== + device_maxromsize_sc64 + Gets the max ROM size that + the SC64 supports + @return The max ROM size +==============================*/ + +uint32_t device_maxromsize_sc64() +{ + return 64*1024*1024; +} + + +/*============================== + device_shouldpadrom_sc64 + Checks if the ROM should be + padded before uploading on the + SC64. + @return Whether or not to pad + the ROM. +==============================*/ + +bool device_shouldpadrom_sc64() +{ + return true; +} + + /*============================== device_open_sc64 Opens the USB pipe @@ -211,4 +239,4 @@ DeviceError device_close_sc64(FTDIDevice* cart) if (FT_Close(cart->handle) != FT_OK) return DEVICEERR_CLOSEFAIL; return DEVICEERR_OK; -} +} \ No newline at end of file diff --git a/UNFLoader/device_sc64.h b/UNFLoader/device_sc64.h index 185b582..0e7214f 100644 --- a/UNFLoader/device_sc64.h +++ b/UNFLoader/device_sc64.h @@ -2,7 +2,6 @@ #define __DEVICE_SC64_HEADER #include "device.h" - #include /********************************* @@ -11,8 +10,10 @@ bool device_test_sc64(FTDIDevice* cart, uint32_t index); DeviceError device_open_sc64(FTDIDevice* cart); + uint32_t device_maxromsize_sc64(); + bool device_shouldpadrom_sc64(); /* - void device_sendrom_sc64(FTDIDevice* cart, FILE *file, uint32_t size); + void device_sendrom_sc64(FTDIDevice* cart, uint8_t* rom, uint32_t size); bool device_testdebug_sc64(FTDIDevice* cart); void device_senddata_sc64(FTDIDevice* cart, int datatype, char* data, uint32_t size); */ diff --git a/UNFLoader/helper.cpp b/UNFLoader/helper.cpp index 9d59ca8..7b35c73 100644 --- a/UNFLoader/helper.cpp +++ b/UNFLoader/helper.cpp @@ -11,6 +11,7 @@ #include "Include/panel.h" #else #include + #include #include #include #endif @@ -239,6 +240,34 @@ const char* save_typetostr(SaveType saveenum) } +/*============================== + file_lastmodtime + Gets the last modification time of + a file. This is usually done via stat, + but it is broken on WinXP: + https://stackoverflow.com/questions/32452777/visual-c-2015-express-stat-not-working-on-windows-xp + @param Path to the file to check the + timestamp of + @return The file modification time +==============================*/ + +time_t file_lastmodtime(const char* path) +{ + struct stat finfo; + #ifndef LINUX + LARGE_INTEGER lt; + WIN32_FILE_ATTRIBUTE_DATA fdata; + GetFileAttributesExA(path, GetFileExInfoStandard, &fdata); + lt.LowPart = fdata.ftLastWriteTime.dwLowDateTime; + lt.HighPart = (long)fdata.ftLastWriteTime.dwHighDateTime; + finfo.st_mtime = (time_t)(lt.QuadPart*1e-7); + #else + stat(path, &finfo); + #endif + return finfo.st_mtime; +} + + /*============================== handle_deviceerror Stops the program with a useful @@ -286,6 +315,9 @@ void handle_deviceerror(DeviceError err) case DEVICEERR_WRITEFAIL: terminate("Unable to write to flashcart."); break; + case DEVICEERR_WRITEZERO: + terminate("Zero bytes were written to flashcart."); + break; case DEVICEERR_CLOSEFAIL: terminate("Unable to close flashcart."); break; @@ -316,6 +348,12 @@ void handle_deviceerror(DeviceError err) case DEVICEERR_BADPACKSIZE: terminate("Wrong read packet size."); break; + case DEVICEERR_MALLOCFAIL: + terminate("Malloc failure."); + return; + case DEVICEERR_64D_8303USB: + terminate("The 8303 CIC is not supported through USB."); + break; case DEVICEERR_SC64_CTRLRESETFAIL: terminate("Couldn't perform SC64 controller reset."); break; diff --git a/UNFLoader/helper.h b/UNFLoader/helper.h index dd21e09..1083055 100644 --- a/UNFLoader/helper.h +++ b/UNFLoader/helper.h @@ -11,6 +11,7 @@ // Useful void terminate(const char* reason, ...); uint64_t time_miliseconds(); + time_t file_lastmodtime(const char* path); void handle_deviceerror(DeviceError err); // Program configuration diff --git a/UNFLoader/main.cpp b/UNFLoader/main.cpp index 60cfc8d..fe308f0 100644 --- a/UNFLoader/main.cpp +++ b/UNFLoader/main.cpp @@ -10,6 +10,7 @@ UNFLoader Entrypoint #include "device.h" #include #include +#include #include #include #include @@ -179,7 +180,8 @@ void parse_args(std::list* args) // If the first character is not a dash, assume a ROM path if (args->front()[0] != '-') { - device_setrom(args->front()); + if (!device_setrom(args->front())) + terminate("'%s' is not a file."); return; } @@ -197,7 +199,10 @@ void parse_args(std::list* args) { case 'r': // ROM to upload if (nextarg_isvalid(it, args)) - device_setrom(*it); + { + if (!device_setrom(*it)) + terminate("'%s' is not a file."); + } else terminate("Missing parameter(s) for command '%s'.", command); break; @@ -297,7 +302,9 @@ void show_title() void program_loop() { + bool firstupload = true; bool autocart = (device_getcart() == CART_NONE); + time_t lastmodtime = 0; // Check if we have a flashcart if (autocart) @@ -313,20 +320,61 @@ void program_loop() // Loop if debug mode or listen mode is enabled, and esc hasn't been pressed do { - // If we have a ROM, upload it. - // Try multiple times because sometimes it might not work the first time in Listen mode + time_t newmodtime; + if (device_getrom() != NULL) + newmodtime = file_lastmodtime(device_getrom()); - // This is also a reminder to implement file logging, ya numbskull + // If we have a ROM, upload it + if (device_getrom() != NULL && (firstupload || (local_listenmode && lastmodtime != newmodtime))) + { + FILE* fp; + uint32_t filesize = 0; // I could use stat, but it doesn't work in WinXP (more info below) + firstupload = false; + + // Try multiple times to open the file, because sometimes does not work the first time in Listen mode + for (int i=0; i<5; i++) + { + fp = fopen(device_getrom(), "rb"); + if (fp != NULL) + break; + else + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + if (fp == NULL) + terminate("Unable to open file '%s'", device_getrom()); + + // Get the filesize and reset the seek position + // Workaround for https://stackoverflow.com/questions/32452777/visual-c-2015-express-stat-not-working-on-windows-xp + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + // File size checks + if (filesize < 1*1024*1024) + log_simple("ROM is smaller than 1MB, it might not boot properly.\n"); + if (filesize > device_getmaxromsize()) + terminate("The %s only supports ROMs up to %d bytes.", cart_typetostr(device_getcart()), device_getmaxromsize()); + + // Handle CIC + + // Upload the ROM + handle_deviceerror(device_sendrom(fp, filesize)); + + // Cleanup + lastmodtime = newmodtime; + fclose(fp); + } - // Keep track of the ROM timestamp if listen mode is enabled + // This is also a reminder to implement file logging, ya numbskull // Open the debug server, and enable terminal input /* - static int i = 0; + static int i = 0; log_colored("Hello %d\n", CRDEF_PRINT, i++); */ - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if (local_listenmode || local_debugmode) + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } while ((local_debugmode || local_listenmode) && !global_escpressed);