12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160 |
- /*
- gd.c - Game Doctor support for uCON64
- Copyright (c) 2002 - 2003 John Weidman
- Copyright (c) 2002 - 2006 dbjh
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
- #ifdef HAVE_CONFIG_H
- #include "config.h"
- #endif
- #include <ctype.h>
- #include <stdlib.h>
- #include "misc/archive.h"
- #include "misc/file.h"
- #include "misc/misc.h"
- #include "misc/parallel.h"
- #include "misc/string.h"
- #include "misc/term.h"
- #include "ucon64_misc.h"
- #include "console/snes.h" // for snes_make_gd_names() &
- #include "backup/gd.h" // snes_get_snes_hirom()
- #ifdef USE_PARALLEL
- static st_ucon64_obj_t gd_obj[] =
- {
- {UCON64_SNES, WF_DEFAULT | WF_STOP | WF_NO_ROM},
- {UCON64_SNES, WF_STOP | WF_NO_ROM}
- };
- #endif
- const st_getopt2_t gd_usage[] =
- {
- {
- NULL, 0, 0, 0,
- NULL, "Game Doctor SF3(SF6/SF7)/Professor SF(SF II)"/*"19XX Bung Enterprises Ltd http://www.bung.com.hk"*/,
- NULL
- },
- #ifdef USE_PARALLEL
- {
- "xgd3", 0, 0, UCON64_XGD3, // supports split files
- NULL, "send ROM to Game Doctor SF3/SF6/SF7; " OPTION_LONG_S "port=PORT\n"
- "this option uses the Game Doctor SF3 protocol",
- &gd_obj[0]
- },
- {
- "xgd6", 0, 0, UCON64_XGD6,
- #if 1 // dumping is not yet supported
- NULL, "send ROM to Game Doctor SF6/SF7; " OPTION_LONG_S "port=PORT\n" // supports split files
- #else
- NULL, "send/receive ROM to/from Game Doctor SF6/SF7; " OPTION_LONG_S "port=PORT\n"
- "receives automatically when ROM does not exist\n"
- #endif
- "this option uses the Game Doctor SF6 protocol",
- &gd_obj[0]
- },
- {
- "xgd3s", 0, 0, UCON64_XGD3S,
- NULL, "send SRAM to Game Doctor SF3/SF6/SF7; " OPTION_LONG_S "port=PORT",
- &gd_obj[1]
- },
- // --xgd3r should remain hidden until receiving works
- {
- "xgd3r", 0, 0, UCON64_XGD3R,
- NULL, NULL,
- &gd_obj[1]
- },
- {
- "xgd6s", 0, 0, UCON64_XGD6S,
- NULL, "send/receive SRAM to/from Game Doctor SF6/SF7; " OPTION_LONG_S "port=PORT\n"
- "receives automatically when SRAM does not exist",
- &gd_obj[1]
- },
- {
- "xgd6r", 0, 0, UCON64_XGD6R,
- NULL, "send/receive saver (RTS) data to/from Game Doctor SF6/SF7;\n" OPTION_LONG_S "port=PORT\n"
- "receives automatically when saver file does not exist",
- &gd_obj[1]
- },
- #endif // USE_PARALLEL
- {NULL, 0, 0, 0, NULL, NULL, NULL}
- };
- #ifdef USE_PARALLEL
- #define BUFFERSIZE 8192
- #define GD_OK 0
- #define GD_ERROR 1
- #define GD3_PROLOG_STRING "DSF3"
- #define GD6_READ_PROLOG_STRING "GD6R" // GD reading, PC writing
- #define GD6_WRITE_PROLOG_STRING "GD6W" // GD writing, PC reading
- #define GD6_TIMEOUT_ATTEMPTS 0x4000
- #define GD6_RX_SYNC_TIMEOUT_ATTEMPTS 0x2000
- #define GD6_SYNC_RETRIES 16
- #ifdef _MSC_VER
- // Visual C++ doesn't allow inline in C source code
- #define inline __inline
- #endif
- static void init_io (unsigned short port);
- static void deinit_io (void);
- static void io_error (void);
- static void gd_checkabort (int status);
- static void remove_destfile (void);
- static int gd3_send_prolog_byte (unsigned char data);
- static int gd3_send_prolog_bytes (unsigned char *data, int len);
- static void gd3_send_byte (unsigned char data);
- static int gd3_send_bytes (unsigned char *data, int len);
- static int gd6_sync_hardware (void);
- static int gd6_sync_receive_start (void);
- static inline int gd6_send_byte_helper (unsigned char data, unsigned int timeout);
- static int gd6_send_prolog_byte (unsigned char data);
- static int gd6_send_prolog_bytes (unsigned char *data, int len);
- static int gd6_send_bytes (unsigned char *data, int len);
- static int gd6_receive_bytes (unsigned char *buffer, int len);
- static int gd_send_unit_prolog (int header, unsigned size);
- static int gd_write_rom (const char *filename, unsigned short parport,
- st_ucon64_nfo_t *rominfo, const char *prolog_str);
- static int gd_write_sram (const char *filename, unsigned short parport,
- const char *prolog_str);
- static int gd_write_saver (const char *filename, unsigned short parport,
- const char *prolog_str);
- typedef struct st_gd3_memory_unit
- {
- char name[12]; // Exact size is 11 chars but I'll
- // unsigned char *data; // add one extra for string terminator
- unsigned int size; // Usually either 0x100000 or 0x80000
- } st_gd3_memory_unit_t;
- static int (*gd_send_prolog_byte) (unsigned char data);
- static int (*gd_send_prolog_bytes) (unsigned char *data, int len);
- static int (*gd_send_bytes) (unsigned char *data, int len);
- static st_gd3_memory_unit_t gd3_dram_unit[GD3_MAX_UNITS];
- static unsigned short gd_port;
- static int gd_bytessent, gd_fsize, gd_name_i = 0;
- static time_t gd_starttime;
- static char **gd_names;
- static unsigned char gd6_send_toggle;
- static const char *gd_destfname = NULL;
- static FILE *gd_destfile;
- static void
- init_io (unsigned short port)
- /*
- - sets global `gd_port'. Then the send/receive functions don't need to pass
- `gd_port' to all the I/O functions.
- - calls init_conio(). Necessary for kbhit() and DOS-like behaviour of getch().
- */
- {
- gd_port = port;
- #if 0 // we want to support non-standard parallel port addresses
- if (gd_port != 0x3bc && gd_port != 0x378 && gd_port != 0x278)
- {
- fputs ("ERROR: PORT must be 0x3bc, 0x378 or 0x278\n", stderr);
- exit (1);
- }
- #endif
- #if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
- init_conio ();
- #endif
- if (register_func (deinit_io) == -1)
- {
- fputs ("ERROR: Could not register function with register_func()\n", stderr);
- exit (1);
- }
- parport_print_info ();
- }
- static void
- deinit_io (void)
- {
- // Put possible transfer cleanup stuff here
- #if (defined __unix__ || defined __BEOS__) && !defined __MSDOS__
- deinit_conio ();
- #endif
- }
- static void
- io_error (void)
- // This function could be changed to take a string argument that describes the
- // error. Or take an integer code that we can interpret here.
- {
- fflush (stdout);
- fputs ("ERROR: Communication with Game Doctor failed\n", stderr);
- fflush (stderr);
- // calling fflush() seems to be necessary in Msys in order to make the error
- // message be displayed before the "Removing: <filename>" message
- exit (1);
- }
- static void
- gd_checkabort (int status)
- {
- if ((!ucon64.frontend ? kbhit () : 0) && getch () == 'q')
- {
- puts ("\nProgram aborted");
- exit (status);
- }
- }
- static void
- remove_destfile (void)
- {
- if (gd_destfname)
- {
- printf ("Removing: %s\n", gd_destfname);
- fclose (gd_destfile);
- remove (gd_destfname);
- gd_destfname = NULL;
- }
- }
- static int
- gd3_send_prolog_byte (unsigned char data)
- /*
- Prolog specific data output routine
- We could probably get away with using the general routine but the
- transfer program I (JW) traced to analyze the protocol did this for
- the bytes used to set up the transfer so here it is.
- */
- {
- // Wait until SF3 is not busy
- do
- {
- if ((inportb (gd_port + PARPORT_STATUS) & 0x08) == 0)
- return GD_ERROR;
- }
- while ((inportb (gd_port + PARPORT_STATUS) & 0x80) == 0);
- outportb (gd_port, data); // Set data
- outportb (gd_port + PARPORT_CONTROL, 5); // Clock data out to SF3
- inportb (gd_port + PARPORT_CONTROL); // Let data "settle down"
- outportb (gd_port + PARPORT_CONTROL, 4);
-
- return GD_OK;
- }
- static int
- gd3_send_prolog_bytes (unsigned char *data, int len)
- {
- int i;
- for (i = 0; i < len; i++)
- if (gd3_send_prolog_byte (data[i]) == GD_ERROR)
- return GD_ERROR;
- return GD_OK;
- }
- static int
- gd_send_unit_prolog (int header, unsigned size)
- {
- if (gd_send_prolog_byte (0x00) == GD_ERROR)
- return GD_ERROR;
- if (gd_send_prolog_byte ((unsigned char) ((header != 0) ? 0x02 : 0x00)) == GD_ERROR)
- return GD_ERROR;
- if (gd_send_prolog_byte ((unsigned char) (size >> 16)) == GD_ERROR) // 0x10 = 8 Mbit
- return GD_ERROR;
- if (gd_send_prolog_byte (0x00) == GD_ERROR)
- return GD_ERROR;
- return GD_OK;
- }
- static void
- gd3_send_byte (unsigned char data)
- /*
- General data output routine
- Use this routine for sending ROM data bytes to the Game Doctor SF3 (SF6/SF7
- too).
- */
- {
- // Wait until SF3 is not busy
- while ((inportb (gd_port + PARPORT_STATUS) & 0x80) == 0)
- ;
- outportb (gd_port, data); // Set data
- outportb (gd_port + PARPORT_CONTROL, 5); // Clock data out to SF3
- inportb (gd_port + PARPORT_CONTROL); // Let data "settle down"
- outportb (gd_port + PARPORT_CONTROL, 4);
- }
- static int
- gd3_send_bytes (unsigned char *data, int len)
- {
- int i;
- for (i = 0; i < len; i++)
- {
- gd3_send_byte (data[i]);
- gd_bytessent++;
- if ((gd_bytessent - GD_HEADER_LEN) % 8192 == 0)
- {
- ucon64_gauge (gd_starttime, gd_bytessent, gd_fsize);
- gd_checkabort (2); // 2 to return something other than 1
- }
- }
- return GD_OK;
- }
- static int
- gd6_sync_hardware (void)
- // Sets the SF7 up for an SF6/SF7 protocol transfer
- {
- int timeout, retries;
- volatile int delay;
- for (retries = GD6_SYNC_RETRIES; retries > 0; retries--)
- {
- timeout = GD6_TIMEOUT_ATTEMPTS;
- outportb (gd_port + PARPORT_CONTROL, 4);
- outportb (gd_port, 0);
- outportb (gd_port + PARPORT_CONTROL, 4);
- for (delay = 0x1000; delay > 0; delay--) // A delay may not be necessary here
- ;
- outportb (gd_port, 0xaa);
- outportb (gd_port + PARPORT_CONTROL, 0);
- while ((inportb (gd_port + PARPORT_CONTROL) & 0x08) == 0)
- if (--timeout == 0)
- break;
- if (timeout == 0)
- continue;
- outportb (gd_port + PARPORT_CONTROL, 4);
- while ((inportb (gd_port + PARPORT_CONTROL) & 0x08) != 0)
- if (--timeout == 0)
- break;
- if (timeout == 0)
- continue;
- outportb (gd_port, 0x55);
- outportb (gd_port + PARPORT_CONTROL, 0);
- while ((inportb (gd_port + PARPORT_CONTROL) & 0x08) == 0)
- if (--timeout == 0)
- break;
- if (timeout == 0)
- continue;
- outportb (gd_port + PARPORT_CONTROL, 4);
- while ((inportb (gd_port + PARPORT_CONTROL) & 0x08) != 0)
- if (--timeout == 0)
- break;
- if (timeout == 0)
- continue;
- return GD_OK;
- }
- return GD_ERROR;
- }
- static int
- gd6_sync_receive_start (void)
- // Sync with the start of the received data
- {
- int timeout = GD6_RX_SYNC_TIMEOUT_ATTEMPTS;
- for (;;)
- {
- if (((inportb (gd_port + PARPORT_CONTROL) & 0x03) == 0x03) ||
- ((inportb (gd_port + PARPORT_CONTROL) & 0x03) == 0))
- break;
- if (--timeout == 0)
- return GD_ERROR;
- }
- outportb (gd_port, 0);
- timeout = GD6_RX_SYNC_TIMEOUT_ATTEMPTS;
- while ((inportb (gd_port + PARPORT_STATUS) & 0x80) != 0)
- if (--timeout == 0)
- return GD_ERROR;
- return GD_OK;
- }
- static inline int
- gd6_send_byte_helper (unsigned char data, unsigned int timeout)
- {
- while ((inportb (gd_port + PARPORT_CONTROL) & 0x02) != gd6_send_toggle)
- if (--timeout == 0)
- return GD_ERROR;
- gd6_send_toggle = ~gd6_send_toggle & 0x02;
- outportb (gd_port, data);
- outportb (gd_port + PARPORT_CONTROL, 4 | (gd6_send_toggle >> 1));
- return GD_OK;
- }
- static int
- gd6_send_prolog_byte (unsigned char data)
- {
- unsigned int timeout = 0x100000;
- gd6_send_toggle = (inportb (gd_port + PARPORT_CONTROL) & 0x01) << 1;
- return gd6_send_byte_helper (data, timeout);
- }
- static int
- gd6_send_prolog_bytes (unsigned char *data, int len)
- {
- int i;
- unsigned int timeout = 0x1e00000;
- gd6_send_toggle = (inportb (gd_port + PARPORT_CONTROL) & 0x01) << 1;
- for (i = 0; i < len; i++)
- if (gd6_send_byte_helper (data[i], timeout) != GD_OK)
- return GD_ERROR;
- return GD_OK;
- }
- static int
- gd6_send_bytes (unsigned char *data, int len)
- {
- int i;
- unsigned int timeout = 0x1e0000;
- gd6_send_toggle = (inportb (gd_port + PARPORT_CONTROL) & 0x01) << 1;
- for (i = 0; i < len; i++)
- {
- if (gd6_send_byte_helper (data[i], timeout) != GD_OK)
- return GD_ERROR;
- gd_bytessent++;
- if ((gd_bytessent - GD_HEADER_LEN) % 8192 == 0)
- {
- ucon64_gauge (gd_starttime, gd_bytessent, gd_fsize);
- gd_checkabort (2); // 2 to return something other than 1
- }
- }
- return GD_OK;
- }
- static int
- gd6_receive_bytes (unsigned char *buffer, int len)
- {
- int i;
- unsigned char nibble1, nibble2;
- unsigned int timeout = 0x1e0000;
- outportb (gd_port, 0x80); // Signal the SF6/SF7 to send the next nibble
- for (i = 0; i < len; i++)
- {
- while ((inportb (gd_port + PARPORT_STATUS) & 0x80) == 0)
- if (--timeout == 0)
- return GD_ERROR;
- nibble1 = (inportb (gd_port + PARPORT_STATUS) >> 3) & 0x0f;
- outportb (gd_port, 0x00); // Signal the SF6/SF7 to send the next nibble
- while ((inportb (gd_port + PARPORT_STATUS) & 0x80) != 0)
- if (--timeout == 0)
- return GD_ERROR;
- nibble2 = (inportb (gd_port + PARPORT_STATUS) << 1) & 0xf0;
- buffer[i] = nibble2 | nibble1;
- outportb (gd_port, 0x80);
- }
- return GD_OK;
- }
- int
- gd_add_filename (const char *filename)
- {
- char buf[FILENAME_MAX], *p;
- if (gd_name_i < GD3_MAX_UNITS)
- {
- strcpy (buf, filename);
- p = strrchr (buf, '.');
- if (p)
- *p = 0;
- strncpy (gd_names[gd_name_i], basename2 (buf), 11);
- gd_names[gd_name_i][11] = 0;
- gd_name_i++;
- }
- return 0;
- }
- int
- gd3_read_rom (const char *filename, unsigned short parport)
- {
- (void) filename; // warning remover
- (void) parport; // warning remover
- return fputs ("ERROR: The function for dumping a cartridge is not yet implemented for the SF3\n", stderr);
- }
- int
- gd6_read_rom (const char *filename, unsigned short parport)
- {
- #if 0
- FILE *file;
- unsigned char *buffer;
- init_io (parport);
- if ((file = fopen (filename, "wb")) == NULL)
- {
- fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
- exit (1);
- }
- if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
- {
- fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
- exit (1);
- }
- free (buffer);
- fclose (file);
- return 0;
- #else
- (void) filename; // warning remover
- (void) parport; // warning remover
- return fputs ("ERROR: The function for dumping a cartridge is not yet implemented for the SF6\n", stderr);
- #endif
- }
- int
- gd3_write_rom (const char *filename, unsigned short parport, st_ucon64_nfo_t *rominfo)
- {
- gd_send_prolog_byte = gd3_send_prolog_byte; // for gd_send_unit_prolog()
- gd_send_prolog_bytes = gd3_send_prolog_bytes;
- gd_send_bytes = gd3_send_bytes;
- return gd_write_rom (filename, parport, rominfo, GD3_PROLOG_STRING);
- }
- int
- gd6_write_rom (const char *filename, unsigned short parport, st_ucon64_nfo_t *rominfo)
- {
- gd_send_prolog_byte = gd6_send_prolog_byte; // for gd_send_unit_prolog()
- gd_send_prolog_bytes = gd6_send_prolog_bytes;
- gd_send_bytes = gd6_send_bytes;
- return gd_write_rom (filename, parport, rominfo, GD6_READ_PROLOG_STRING);
- }
- /*
- Note: On most Game Doctor's the way you enter link mode to be able to upload
- the ROM to the unit is to hold down the R key on the controller while
- resetting the SNES. You will see the Game Doctor menu has a message that
- says "LINKING.."
- */
- static int
- gd_write_rom (const char *filename, unsigned short parport, st_ucon64_nfo_t *rominfo,
- const char *prolog_str)
- {
- FILE *file = NULL;
- unsigned char *buffer;
- char *names[GD3_MAX_UNITS], names_mem[GD3_MAX_UNITS][12],
- *filenames[GD3_MAX_UNITS], dir[FILENAME_MAX];
- int num_units, i, send_header, x, split = 1, hirom = snes_get_snes_hirom();
- init_io (parport);
- // We don't want to malloc() ridiculously small chunks (of 12 bytes)
- for (i = 0; i < GD3_MAX_UNITS; i++)
- names[i] = names_mem[i];
- gd_names = (char **) names;
- num_units = ucon64.split = ucon64_testsplit (filename, gd_add_filename);
- if (!ucon64.split)
- {
- split = 0;
- num_units = snes_make_gd_names (filename, rominfo, (char **) names);
- }
- dirname2 (filename, dir);
- gd_fsize = 0;
- for (i = 0; i < num_units; i++)
- {
- // No suffix is necessary but the name entry must be upper case and MUST
- // be 11 characters long, padded at the end with spaces if necessary.
- memset (gd3_dram_unit[i].name, ' ', 11); // "pad" with spaces
- gd3_dram_unit[i].name[11] = 0; // terminate string so we can print it (debug)
- // Use memcpy() instead of strcpy() so that the string terminator in
- // names[i] won't be copied.
- memcpy (gd3_dram_unit[i].name, strupr (names[i]), strlen (names[i]));
- x = strlen (dir) + strlen (names[i]) + 6; // file sep., suffix, ASCII-z => 6
- if ((filenames[i] = (char *) malloc (x)) == NULL)
- {
- fprintf (stderr, ucon64_msg[BUFFER_ERROR], x);
- exit (1);
- }
- sprintf (filenames[i], "%s" DIR_SEPARATOR_S "%s.078", dir, names[i]); // should match with what code of -s does
- if (split)
- {
- x = fsizeof (filenames[i]);
- gd_fsize += x;
- gd3_dram_unit[i].size = x;
- if (i == 0) // Correct for header of first file
- gd3_dram_unit[i].size -= GD_HEADER_LEN;
- }
- else
- {
- if (!gd_fsize)
- gd_fsize = ucon64.file_size;
- if (hirom)
- gd3_dram_unit[i].size = (gd_fsize - GD_HEADER_LEN) / num_units;
- else
- {
- if ((i + 1) * 8 * MBIT <= gd_fsize - GD_HEADER_LEN)
- gd3_dram_unit[i].size = 8 * MBIT;
- else
- gd3_dram_unit[i].size = gd_fsize - GD_HEADER_LEN - i * 8 * MBIT;
- }
- }
- }
- if ((buffer = (unsigned char *) malloc (8 * MBIT)) == NULL)
- { // a DRAM unit can hold 8 Mbit at maximum
- fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], 8 * MBIT);
- exit (1);
- }
- printf ("Send: %d Bytes (%.4f Mb)\n", gd_fsize, (float) gd_fsize / MBIT);
- // Put this just before the real transfer begins or else the ETA won't be
- // correct.
- gd_starttime = time (NULL);
- // Send the ROM to the hardware
- if (memcmp (prolog_str, GD6_READ_PROLOG_STRING, 4) == 0)
- if (gd6_sync_hardware () == GD_ERROR)
- io_error ();
- memcpy (buffer, prolog_str, 4);
- buffer[4] = (unsigned char) num_units;
- if (gd_send_prolog_bytes (buffer, 5) == GD_ERROR)
- io_error ();
- puts ("Press q to abort\n");
- for (i = 0; i < num_units; i++)
- {
- #ifdef DEBUG
- printf ("\nfilename (%d): \"%s\", ", split, (split ? (char *) filenames[i] : filename));
- printf ("name: \"%s\", size: %d\n", gd3_dram_unit[i].name, gd3_dram_unit[i].size);
- #endif
- if (split)
- {
- if ((file = fopen (filenames[i], "rb")) == NULL)
- {
- fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filenames[i]);
- exit (1);
- }
- }
- else
- if (file == NULL) // don't open the file more than once
- if ((file = fopen (filename, "rb")) == NULL)
- {
- fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
- exit (1);
- }
- /*
- When sending a "pre-split" file, opening the next file often causes
- enough of a delay for the GDSF7. When sending an unsplit file without
- an extra delay often results in the infamous "File Size Error !".
- Adding the delay also results in the transfer code catching an I/O
- error more often, instead of simply hanging (while the GDSF7 displays
- "File Size Error !"). The delay seems *much* more important for HiROM
- games than for LoROM games.
- There's more to the "File Size Error !" than this as the transfer
- reliability is still far from 100%, but the delay helps me a lot.
- Note that this applies to the SF3 protocol. Using the SF6 protocol does
- not work for me. - dbjh
- */
- wait2 (100); // 100 ms seems a good value
- send_header = i == 0 ? 1 : 0;
- if (gd_send_unit_prolog (send_header, gd3_dram_unit[i].size) == GD_ERROR)
- io_error ();
- if (gd_send_prolog_bytes ((unsigned char *) gd3_dram_unit[i].name, 11) == GD_ERROR)
- io_error ();
- if (send_header)
- {
- // Send the Game Doctor 512 byte header
- fread (buffer, 1, GD_HEADER_LEN, file);
- if (gd_send_prolog_bytes (buffer, GD_HEADER_LEN) == GD_ERROR)
- io_error ();
- gd_bytessent += GD_HEADER_LEN;
- }
- if (split == 0) // Not pre-split -- have to split it ourselves
- {
- if (hirom)
- fseek (file, i * gd3_dram_unit[0].size + GD_HEADER_LEN, SEEK_SET);
- else
- fseek (file, i * 8 * MBIT + GD_HEADER_LEN, SEEK_SET);
- }
- fread (buffer, 1, gd3_dram_unit[i].size, file);
- if (gd_send_bytes (buffer, gd3_dram_unit[i].size) == GD_ERROR)
- io_error ();
- if (split || i == num_units - 1)
- fclose (file);
- }
- for (i = 0; i < num_units; i++)
- free (filenames[i]);
- free (buffer);
- return 0;
- }
- int
- gd3_read_sram (const char *filename, unsigned short parport)
- {
- (void) filename; // warning remover
- (void) parport; // warning remover
- return fputs ("ERROR: The function for dumping SRAM is not yet implemented for the SF3\n", stderr);
- }
- int
- gd6_read_sram (const char *filename, unsigned short parport)
- {
- FILE *file;
- unsigned char *buffer, gdfilename[12];
- int len, bytesreceived = 0, transfer_size;
- time_t starttime;
- init_io (parport);
- if ((file = fopen (filename, "wb")) == NULL)
- {
- fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
- exit (1);
- }
- if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
- {
- fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
- exit (1);
- }
- // Be nice to the user and automatically remove the file on an error (or abortion)
- gd_destfname = filename;
- gd_destfile = file;
- register_func (remove_destfile);
- if (gd6_sync_hardware () == GD_ERROR)
- io_error ();
- if (gd6_send_prolog_bytes ((unsigned char *) GD6_WRITE_PROLOG_STRING, 4) == GD_ERROR)
- io_error ();
- /*
- The BRAM (SRAM) filename doesn't have to exactly match any game loaded in
- the SF7. It needs to match any valid Game Doctor filename AND have an
- extension of .B## (where # is a digit from 0-9)
- */
- strcpy ((char *) gdfilename, "SF16497 B00"); // TODO: We might need to make a GD filename from the real one
- if (gd6_send_prolog_bytes (gdfilename, 11) == GD_ERROR)
- io_error ();
- if (gd6_sync_receive_start () == GD_ERROR)
- io_error ();
- if (gd6_receive_bytes (buffer, 16) == GD_ERROR)
- io_error ();
- transfer_size = buffer[1] | (buffer[2] << 8) | (buffer[3] << 16) | (buffer[4] << 24);
- if (transfer_size != 0x8000)
- {
- fprintf (stderr, "ERROR: SRAM transfer size from Game Doctor != 0x8000 bytes\n");
- exit (1);
- }
- printf ("Receive: %d Bytes\n", transfer_size);
- puts ("Press q to abort\n");
- starttime = time (NULL);
- while (bytesreceived < transfer_size)
- {
- if (transfer_size - bytesreceived >= BUFFERSIZE)
- len = BUFFERSIZE;
- else
- len = transfer_size - bytesreceived;
- if (gd6_receive_bytes (buffer, len) == GD_ERROR)
- io_error ();
- fwrite (buffer, 1, len, file);
- bytesreceived += len;
- ucon64_gauge (starttime, bytesreceived, 32 * 1024);
- gd_checkabort (2);
- }
- unregister_func (remove_destfile);
- free (buffer);
- fclose (file);
- return 0;
- }
- int
- gd3_write_sram (const char *filename, unsigned short parport)
- {
- gd_send_prolog_bytes = gd3_send_prolog_bytes;
- gd_send_bytes = gd3_send_bytes;
- return gd_write_sram (filename, parport, GD3_PROLOG_STRING);
- }
- int
- gd6_write_sram (const char *filename, unsigned short parport)
- {
- gd_send_prolog_bytes = gd6_send_prolog_bytes;
- gd_send_bytes = gd6_send_bytes;
- return gd_write_sram (filename, parport, GD6_READ_PROLOG_STRING);
- }
- static int
- gd_write_sram (const char *filename, unsigned short parport, const char *prolog_str)
- {
- FILE *file;
- unsigned char *buffer, gdfilename[12];
- int bytesread, bytessent = 0, size, header_size;
- time_t starttime;
- init_io (parport);
- if ((file = fopen (filename, "rb")) == NULL)
- {
- fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
- exit (1);
- }
- if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
- {
- fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
- exit (1);
- }
- size = ucon64.file_size; // GD SRAM is 4*8 KB, emu SRAM often not
- if (size == 0x8000)
- header_size = 0;
- else if (size == 0x8200)
- {
- header_size = 0x200;
- size = 0x8000;
- }
- else
- {
- fputs ("ERROR: GD SRAM file size must be 32768 or 33280 bytes\n", stderr);
- exit (1);
- }
- printf ("Send: %d Bytes\n", size);
- fseek (file, header_size, SEEK_SET); // skip the header
- if (memcmp (prolog_str, GD6_READ_PROLOG_STRING, 4) == 0)
- if (gd6_sync_hardware () == GD_ERROR)
- io_error ();
- memcpy (buffer, prolog_str, 4);
- buffer[4] = 1;
- if (gd_send_prolog_bytes (buffer, 5) == GD_ERROR)
- io_error ();
- buffer[0] = 0x00;
- buffer[1] = 0x80;
- buffer[2] = 0x00;
- buffer[3] = 0x00;
- if (gd_send_prolog_bytes (buffer, 4) == GD_ERROR)
- io_error ();
- /*
- The BRAM (SRAM) filename doesn't have to exactly match any game loaded in
- the SF7. It needs to match any valid Game Doctor filename AND have an
- extension of .B## (where # is a digit from 0-9)
- */
- strcpy ((char *) gdfilename, "SF8123 B00"); // TODO: We might need to make a GD filename from the real one
- if (gd_send_prolog_bytes (gdfilename, 11) == GD_ERROR)
- io_error ();
- puts ("Press q to abort\n"); // print here, NOT before first GD I/O,
- // because if we get here q works ;-)
- starttime = time (NULL);
- while ((bytesread = fread (buffer, 1, BUFFERSIZE, file)) != 0)
- {
- if (gd_send_bytes (buffer, bytesread) == GD_ERROR)
- io_error ();
- bytessent += bytesread;
- ucon64_gauge (starttime, bytessent, size);
- gd_checkabort (2);
- }
- free (buffer);
- fclose (file);
- return 0;
- }
- int
- gd3_read_saver (const char *filename, unsigned short parport)
- {
- (void) filename; // warning remover
- (void) parport; // warning remover
- return fputs ("ERROR: The function for dumping saver data is not yet implemented for the SF3\n", stderr);
- }
- int
- gd6_read_saver (const char *filename, unsigned short parport)
- {
- FILE *file;
- unsigned char *buffer, gdfilename[12];
- int len, bytesreceived = 0, transfer_size;
- time_t starttime;
- init_io (parport);
- if ((file = fopen (filename, "wb")) == NULL)
- {
- fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], filename);
- exit (1);
- }
- if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
- {
- fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
- exit (1);
- }
- // Be nice to the user and automatically remove the file on an error (or abortion)
- gd_destfname = filename;
- gd_destfile = file;
- register_func (remove_destfile);
- if (gd6_sync_hardware () == GD_ERROR)
- io_error ();
- if (gd6_send_prolog_bytes ((unsigned char *) GD6_WRITE_PROLOG_STRING, 4) == GD_ERROR)
- io_error ();
- /*
- TODO: Graceful handling of an abort because of a name error?
- Currently we fail with a generic error.
- TODO: We could make a GD filename from the real one but a valid dummy name
- seems to work OK here. The user must have the proper game selected in
- the SF7 menu even if the real name is used.
- */
- strcpy ((char *) gdfilename, "SF16497 S00");
- if (gd6_send_prolog_bytes (gdfilename, 11) == GD_ERROR)
- io_error ();
- if (gd6_sync_receive_start () == GD_ERROR)
- io_error ();
- if (gd6_receive_bytes (buffer, 16) == GD_ERROR)
- io_error ();
- transfer_size = buffer[1] | (buffer[2] << 8) | (buffer[3] << 16) | (buffer[4] << 24);
- if (transfer_size != 0x38000)
- {
- fputs ("ERROR: Saver transfer size from Game Doctor != 0x38000 bytes\n", stderr);
- exit (1);
- }
- printf ("Receive: %d Bytes\n", transfer_size);
- puts ("Press q to abort\n");
- starttime = time (NULL);
- while (bytesreceived < transfer_size)
- {
- if (transfer_size - bytesreceived >= BUFFERSIZE)
- len = BUFFERSIZE;
- else
- len = transfer_size - bytesreceived;
- if (gd6_receive_bytes (buffer, len) == GD_ERROR)
- io_error ();
- fwrite (buffer, 1, len, file);
- bytesreceived += len;
- ucon64_gauge (starttime, bytesreceived, 32 * 1024);
- gd_checkabort (2);
- }
- unregister_func (remove_destfile);
- free (buffer);
- fclose (file);
- return 0;
- }
- int
- gd3_write_saver (const char *filename, unsigned short parport)
- {
- gd_send_prolog_bytes = gd3_send_prolog_bytes;
- gd_send_bytes = gd3_send_bytes;
- return gd_write_saver (filename, parport, GD3_PROLOG_STRING);
- }
- int
- gd6_write_saver (const char *filename, unsigned short parport)
- {
- gd_send_prolog_bytes = gd6_send_prolog_bytes;
- gd_send_bytes = gd6_send_bytes;
- return gd_write_saver (filename, parport, GD6_READ_PROLOG_STRING);
- }
- static int
- gd_write_saver (const char *filename, unsigned short parport, const char *prolog_str)
- {
- FILE *file;
- unsigned char *buffer, gdfilename[12];
- const char *p;
- int bytesread, bytessent = 0, size, fn_length;
- time_t starttime;
- init_io (parport);
- /*
- Check that filename is a valid Game Doctor saver filename.
- It should start with SF, followed by the game ID, followed by the extension.
- The extension is of the form .S## (where # is a digit from 0-9).
- E.g., SF16123.S00
- The saver base filename must match the base name of the game (loaded in the
- Game Doctor) that you are loading the saver data for.
- */
- // Strip the path out of filename for use in the GD
- p = basename2 (filename);
- fn_length = strlen (p);
- if (fn_length < 6 || fn_length > 11 // 7 ("base") + 1 (period) + 3 (extension)
- || toupper ((int) p[0]) != 'S' || toupper ((int) p[1]) != 'F'
- || p[fn_length - 4] != '.' || toupper ((int) p[fn_length - 3]) != 'S')
- {
- fprintf (stderr, "ERROR: Filename (%s) is not a saver filename (SF*.S##)\n", p);
- exit (1);
- }
- if ((file = fopen (filename, "rb")) == NULL)
- {
- fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], filename);
- exit (1);
- }
- if ((buffer = (unsigned char *) malloc (BUFFERSIZE)) == NULL)
- {
- fprintf (stderr, ucon64_msg[FILE_BUFFER_ERROR], BUFFERSIZE);
- exit (1);
- }
- size = ucon64.file_size;
- if (size != 0x38000) // GD saver size is always 0x38000 bytes -- no header
- {
- fputs ("ERROR: GD saver file size must be 229376 bytes\n", stderr);
- exit (1);
- }
- // Make a GD filename from the real one
- memset (gdfilename, ' ', 11); // "pad" with spaces
- gdfilename[11] = 0; // terminate string
- memcpy (gdfilename, p, fn_length - 4); // copy name except extension
- memcpy (&gdfilename[8], "S00", 3); // copy extension S00
- strupr ((char *) gdfilename);
- printf ("Send: %d Bytes\n", size);
- fseek (file, 0, SEEK_SET);
- if (memcmp (prolog_str, GD6_READ_PROLOG_STRING, 4) == 0)
- if (gd6_sync_hardware () == GD_ERROR)
- io_error ();
- memcpy (buffer, prolog_str, 4);
- buffer[4] = 1;
- if (gd_send_prolog_bytes (buffer, 5) == GD_ERROR)
- io_error ();
- // Transfer 0x38000 bytes
- buffer[0] = 0x00;
- buffer[1] = 0x80;
- buffer[2] = 0x03;
- buffer[3] = 0x00;
- if (gd_send_prolog_bytes (buffer, 4) == GD_ERROR)
- io_error ();
- if (gd_send_prolog_bytes (gdfilename, 11) == GD_ERROR)
- io_error ();
- puts ("Press q to abort\n"); // print here, NOT before first GD I/O,
- // because if we get here q works ;-)
- starttime = time (NULL);
- while ((bytesread = fread (buffer, 1, BUFFERSIZE, file)) != 0)
- {
- if (gd_send_bytes (buffer, bytesread) == GD_ERROR)
- io_error ();
- bytessent += bytesread;
- ucon64_gauge (starttime, bytessent, size);
- gd_checkabort (2);
- }
- free (buffer);
- fclose (file);
- return 0;
- }
- #endif // USE_PARALLEL
|