From d981092caa310335934649eb569f942f9704686c Mon Sep 17 00:00:00 2001 From: Alan Steremberg Date: Wed, 11 Dec 2019 08:33:27 -0800 Subject: [PATCH] Add support for /arcade folder, and MRA xml decoding * round trip xml launch * roms load * removed debug * added binary data and changed rom format * changed hex format * fixed bug * added structure and start/length * removed base64 support * removed base64 support * fixed parsing bugs * fixed hex parse bug * fixed parser * fixing paths * fixed rbf parser * fixed initialization bug * fixed extension removal for when there are multiple extension options * fixed core path and cleaned up filelength * added md5 checks to arcade roms * fixed rbf search * added error support * Simplify error checking code * fixed arcade error message pop up * fixed bug in part zip initialization * removed dtdt * don't load second rom0 if first works * fixed directory problem * added more comments * added / to zip path * fixed / bug, truncate mame zip error message * fixed scrolling RBF * fixed scrolling name and error message * added code to remove _date in scrolling text * remove redundant / --- Makefile | 5 +- file_io.cpp | 1 - fpga_io.cpp | 15 +- fpga_io.h | 4 +- lib/md5/md5.c | 330 +++++++++++++++++++++++++ lib/md5/md5.h | 41 ++++ main.cpp | 2 +- menu.cpp | 149 ++++++++---- support.h | 5 +- support/arcade/buffer.cpp | 195 +++++++++++++++ support/arcade/buffer.h | 15 ++ support/arcade/romutils.cpp | 470 ++++++++++++++++++++++++++++++++++++ support/arcade/romutils.h | 5 + user_io.cpp | 119 ++++++++- user_io.h | 9 +- video.cpp | 2 + 16 files changed, 1313 insertions(+), 54 deletions(-) create mode 100644 lib/md5/md5.c create mode 100644 lib/md5/md5.h create mode 100644 support/arcade/buffer.cpp create mode 100644 support/arcade/buffer.h create mode 100644 support/arcade/romutils.cpp create mode 100644 support/arcade/romutils.h diff --git a/Makefile b/Makefile index ccbddd3..49dd3dd 100644 --- a/Makefile +++ b/Makefile @@ -18,11 +18,13 @@ INCLUDE = -I./ INCLUDE += -I./support/minimig INCLUDE += -I./lib/libco INCLUDE += -I./lib/miniz +INCLUDE += -I./lib/md5 PRJ = MiSTer C_SRC = $(wildcard *.c) \ $(wildcard ./lib/miniz/*.c) \ - lib/libco/arm.c + $(wildcard ./lib/md5/*.c) \ + lib/libco/arm.c CPP_SRC = $(wildcard *.cpp) \ $(wildcard ./support/minimig/*.cpp) \ @@ -32,6 +34,7 @@ CPP_SRC = $(wildcard *.cpp) \ $(wildcard ./support/x86/*.cpp) \ $(wildcard ./support/snes/*.cpp) \ $(wildcard ./support/neogeo/*.cpp) \ + $(wildcard ./support/arcade/*.cpp) \ $(wildcard ./support/megacd/*.cpp) \ lib/lodepng/lodepng.cpp diff --git a/file_io.cpp b/file_io.cpp index 043892e..c0a7848 100644 --- a/file_io.cpp +++ b/file_io.cpp @@ -238,7 +238,6 @@ void FileClose(fileTYPE *file) int FileOpenEx(fileTYPE *file, const char *name, int mode, char mute) { make_fullpath((char*)name, mode); - FileClose(file); file->mode = 0; file->type = 0; diff --git a/fpga_io.cpp b/fpga_io.cpp index caa75ab..1e1e1fa 100644 --- a/fpga_io.cpp +++ b/fpga_io.cpp @@ -14,6 +14,7 @@ #include "file_io.h" #include "input.h" #include "osd.h" +#include "menu.h" #include "fpga_base_addr_ac5.h" #include "fpga_manager.h" @@ -448,7 +449,7 @@ static int make_env(const char *name, const char *cfg) return 0; } -int fpga_load_rbf(const char *name, const char *cfg) +int fpga_load_rbf(const char *name, const char *cfg, const char *xml) { OsdDisable(); static char path[1024]; @@ -470,7 +471,10 @@ int fpga_load_rbf(const char *name, const char *cfg) int rbf = open(path, O_RDONLY); if (rbf < 0) { + char error[4096]; + snprintf(error,4096,"%s\nNot Found", name); printf("Couldn't open file %s\n", path); + Info(error,5000); return -1; } else @@ -524,7 +528,7 @@ int fpga_load_rbf(const char *name, const char *cfg) } } close(rbf); - app_restart(!strcasecmp(name, "menu.rbf") ? "menu.rbf" : path); + app_restart(!strcasecmp(name, "menu.rbf") ? "menu.rbf" : path,xml); return ret; } @@ -637,7 +641,7 @@ char *getappname() return dest; } -void app_restart(const char *path) +void app_restart(const char *path, const char *xml) { sync(); fpga_core_reset(1); @@ -647,7 +651,10 @@ void app_restart(const char *path) char *appname = getappname(); printf("restarting the %s\n", appname); - execl(appname, appname, path, NULL); + if (xml) + execl(appname, appname, path, xml,NULL); + else + execl(appname, appname, path, NULL); printf("Something went wrong. Rebooting...\n"); reboot(0); diff --git a/fpga_io.h b/fpga_io.h index 5eca6ad..17861df 100644 --- a/fpga_io.h +++ b/fpga_io.h @@ -26,10 +26,10 @@ int is_fpga_ready(int quick); int fpga_get_fio_size(); int fpga_get_io_version(); -int fpga_load_rbf(const char *name, const char *cfg = NULL); +int fpga_load_rbf(const char *name, const char *cfg = NULL, const char *xml=NULL); void reboot(int cold); -void app_restart(const char *path); +void app_restart(const char *path, const char *xml=NULL); char *getappname(); #endif diff --git a/lib/md5/md5.c b/lib/md5/md5.c new file mode 100644 index 0000000..949fe93 --- /dev/null +++ b/lib/md5/md5.c @@ -0,0 +1,330 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* This code was modified in 1997 by Jim Kingdon of Cyclic Software to + not require an integer type which is exactly 32 bits. This work + draws on the changes for the same purpose by Tatu Ylonen + as part of SSH, but since I didn't actually use + that code, there is no copyright issue. I hereby disclaim + copyright in any changes I have made; this code remains in the + public domain. */ +#define STDC_HEADERS 1 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if HAVE_STRING_H || STDC_HEADERS +#include /* for memcpy() */ +#endif + +/* Add prototype support. */ +#ifndef PROTO +#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__) +#define PROTO(ARGS) ARGS +#else +#define PROTO(ARGS) () +#endif +#endif + +#include "md5.h" + +/* Little-endian byte-swapping routines. Note that these do not + depend on the size of datatypes such as uint32, nor do they require + us to detect the endianness of the machine we are running on. It + is possible they should be macros for speed, but I would be + surprised if they were a performance bottleneck for MD5. */ + +static uint32 +getu32 (addr) + const unsigned char *addr; +{ + return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) + | addr[1]) << 8 | addr[0]; +} + +static void +putu32 (data, addr) + uint32 data; + unsigned char *addr; +{ + addr[0] = (unsigned char)data; + addr[1] = (unsigned char)(data >> 8); + addr[2] = (unsigned char)(data >> 16); + addr[3] = (unsigned char)(data >> 24); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(ctx) + struct MD5Context *ctx; +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(ctx, buf, len) + struct MD5Context *ctx; + unsigned char const *buf; + unsigned len; +{ + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = (t + ((uint32)len << 3)) & 0xffffffff) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) { + unsigned char *p = ctx->in + t; + + t = 64-t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(digest, ctx) + unsigned char digest[16]; + struct MD5Context *ctx; +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + MD5Transform(ctx->buf, ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count-8); + } + + /* Append length in bits and transform */ + putu32(ctx->bits[0], ctx->in + 56); + putu32(ctx->bits[1], ctx->in + 60); + + MD5Transform(ctx->buf, ctx->in); + putu32(ctx->buf[0], digest); + putu32(ctx->buf[1], digest + 4); + putu32(ctx->buf[2], digest + 8); + putu32(ctx->buf[3], digest + 12); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w &= 0xffffffff, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(buf, inraw) + uint32 buf[4]; + const unsigned char inraw[64]; +{ + register uint32 a, b, c, d; + uint32 in[16]; + int i; + + for (i = 0; i < 16; ++i) + in[i] = getu32 (inraw + 4 * i); + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +#endif + +#ifdef TEST +/* Simple test program. Can use it to manually run the tests from + RFC1321 for example. */ +#include + +int +main (int argc, char **argv) +{ + struct MD5Context context; + unsigned char checksum[16]; + int i; + int j; + + if (argc < 2) + { + fprintf (stderr, "usage: %s string-to-hash\n", argv[0]); + exit (1); + } + for (j = 1; j < argc; ++j) + { + printf ("MD5 (\"%s\") = ", argv[j]); + MD5Init (&context); + MD5Update (&context, argv[j], strlen (argv[j])); + MD5Final (checksum, &context); + for (i = 0; i < 16; i++) + { + printf ("%02x", (unsigned int) checksum[i]); + } + printf ("\n"); + } + return 0; +} +#endif /* TEST */ diff --git a/lib/md5/md5.h b/lib/md5/md5.h new file mode 100644 index 0000000..d8dec5d --- /dev/null +++ b/lib/md5/md5.h @@ -0,0 +1,41 @@ +/* See md5.c for explanation and copyright information. */ + +#ifndef MD5_H +#define MD5_H +#ifdef __cplusplus +extern "C" { +#endif + +/* Unlike previous versions of this code, uint32 need not be exactly + 32 bits, merely 32 bits or more. Choosing a data type which is 32 + bits instead of 64 is not important; speed is considerably more + important. ANSI guarantees that "unsigned long" will be big enough, + and always using it seems to have few disadvantages. */ +typedef unsigned long uint32; + +struct MD5Context { + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; +}; +#ifndef PROTO +#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__) +#define PROTO(ARGS) ARGS +#else +#define PROTO(ARGS) () +#endif +#endif + +void MD5Init PROTO((struct MD5Context *context)); +void MD5Update PROTO((struct MD5Context *context, unsigned char const *buf, unsigned len)); +void MD5Final PROTO((unsigned char digest[16], struct MD5Context *context)); +void MD5Transform PROTO((uint32 buf[4], const unsigned char in[64])); + +/* + * This is needed to make RSAREF happy on some MS-DOS compilers. + */ +typedef struct MD5Context MD5_CTX; +#ifdef __cplusplus +} +#endif +#endif /* !MD5_H */ diff --git a/main.cpp b/main.cpp index 01bc569..7086f74 100644 --- a/main.cpp +++ b/main.cpp @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) } FindStorage(); - user_io_init((argc > 1) ? argv[1] : ""); + user_io_init((argc > 1) ? argv[1] : "",(argc > 2) ? argv[2] : ""); #ifdef USE_SCHEDULER scheduler_init(); diff --git a/menu.cpp b/menu.cpp index 79d8e05..4fa4b51 100644 --- a/menu.cpp +++ b/menu.cpp @@ -313,7 +313,7 @@ static void SelectFile(const char* pFileExt, unsigned char Options, unsigned cha strcat(SelectedPath, "/"); strcat(SelectedPath, get_rbf_name()); } - pFileExt = "RBF"; + pFileExt = "RBFMRA"; } else if (Options & SCANO_TXT) { @@ -4481,9 +4481,21 @@ void HandleUI(void) break; } } - - // close OSD now as the new core may not even have one - fpga_load_rbf(SelectedRBF); + if (!strcasecmp(".mra",&(SelectedRBF[strlen(SelectedRBF) - 4]))) + { + char rbfname[4096]; + char rbfpath[4096]; + fprintf(stderr,"MRA FILE LOADED - write code\n"); + // find the RBF file from the XML + arcade_scan_xml_for_rbf(getFullPath(SelectedRBF),rbfname); + fprintf(stderr,"MRA SelectedRBF: [%s]\n",SelectedRBF); + fprintf(stderr,"MRA rbf: [%s]\n",rbfname); + sprintf(rbfpath,"arcade/%s",rbfname); + fpga_load_rbf(getFullPath(rbfpath),NULL,SelectedRBF); + } + else + // close OSD now as the new core may not even have one + fpga_load_rbf(SelectedRBF); break; case MENU_CORE_FILE_SELECTED2: @@ -4597,6 +4609,84 @@ void open_joystick_setup() joymap_first = 1; } + +/* + * CalculateFileNameLengthWithoutExtension + * + * This function takes a filename and length, and returns + * the length. It will remove the .rbf or .mra from the length + * based on the fs_pFileExt global + * + * If the fs_pFileExt has multiple extensions, it will look through + * each to try to find a match. + */ +int CalculateFileNameLengthWithoutExtension(char *name,char *possibleextensions) +{ + + char *ext = possibleextensions; + int found=0; + /* the default length is the whole string */ + int len = strlen(name); + /* find the extension on the end of the name*/ + char *fext = strrchr(name, '.'); + /* we want to push past the period - and just have rbf instead of .rbf*/ + if (fext) fext++; + + /* walk through each extension and see if it matches */ + while (!found && *ext && fext) + { + char e[4]; + memcpy(e, ext, 3); + if (e[2] == ' ') + { + e[2] = 0; + if (e[1] == ' ') e[1] = 0; + } + + e[3] = 0; + found = 1; + for (int i = 0; i < 4; i++) + { + if (e[i] == '*') break; + if (e[i] == '?' && fext[i]) continue; + + if (tolower(e[i]) != tolower(fext[i])) found = 0; + + if (!e[i] || !found) break; + } + if (found) break; + + if (strlen(ext) < 3) break; + ext += 3; + } + + /* if we haven't found a match, then the answer is the full length of the string */ + if (!found) return len; + + /* we have a match, now we need to handle extensions that are less than 3 characters */ + char e[5]; + memcpy(e + 1, ext, 3); + /* 0x20 is a space in ascii*/ + if (e[3] == 0x20) + { + e[3] = 0; + if (e[2] == 0x20) + { + e[2] = 0; + } + } + e[0] = '.'; + e[4] = 0; + int l = strlen(e); + + if ((len>l) && !strncasecmp(name + len - l, e, l)) len -= l; + + //printf("len: %d l: %d str[%s] e[%s] ext[%s]\n",len,l,name,e,ext); + return len; + +} + + void ScrollLongName(void) { // this function is called periodically when file selection window is displayed @@ -4608,32 +4698,28 @@ void ScrollLongName(void) len = strlen(flist_SelectedItem()->altname); // get name length if (flist_SelectedItem()->de.d_type == DT_REG) // if a file { - if (fs_ExtLen <= 3) - { - char e[5]; - memcpy(e + 1, fs_pFileExt, 3); - if (e[3] == 0x20) - { - e[3] = 0; - if (e[2] == 0x20) - { - e[2] = 0; - } - } - e[0] = '.'; - e[4] = 0; - int l = strlen(e); - if ((len>l) && !strncasecmp(flist_SelectedItem()->altname + len - l, e, l)) len -= l; - } + len=CalculateFileNameLengthWithoutExtension(flist_SelectedItem()->altname,fs_pFileExt); } + max_len = 30; // number of file name characters to display (one more required for scrolling) if (flist_SelectedItem()->de.d_type == DT_DIR) max_len = 25; // number of directory name characters to display + // if we are in a core, we might need to resize for the fixed date string at the end + if (!cfg.rbf_hide_datecode && (fs_Options & SCANO_CORES)) { + if (len > 9 && !strncmp(flist_SelectedItem()->altname+ len - 9, "_20", 3)) { + len -= 9; + } + max_len=21; // __.__.__ remove that from the end + } + + //printf("ScrollLongName: len %d max_len %d [%s]\n",len,max_len,flist_SelectedItem()->altname); ScrollText(flist_iSelectedEntry()-flist_iFirstEntry(), flist_SelectedItem()->altname, 0, len, max_len, 1); } + + void PrintFileName(char *name, int row, int maxinv) { int len; @@ -4705,26 +4791,7 @@ void PrintDirectory(void) if (!(flist_DirItem(k)->de.d_type == DT_DIR)) // if a file { - if (fs_ExtLen <= 3) - { - char e[5]; - memcpy(e + 1, fs_pFileExt, 3); - if (e[3] == 0x20) - { - e[3] = 0; - if (e[2] == 0x20) - { - e[2] = 0; - } - } - e[0] = '.'; - e[4] = 0; - int l = strlen(e); - if ((len>l) && !strncasecmp(flist_DirItem(k)->altname + len - l, e, l)) - { - len -= l; - } - } + len=CalculateFileNameLengthWithoutExtension(flist_DirItem(k)->altname,fs_pFileExt); } char *p = 0; diff --git a/support.h b/support.h index bc3c52d..0050092 100644 --- a/support.h +++ b/support.h @@ -23,5 +23,8 @@ // NeoGeo support #include "support/neogeo/loader.h" +// Arcade support +#include "support/arcade/romutils.h" + // MEGACD support -#include "support/megacd/megacd.h" \ No newline at end of file +#include "support/megacd/megacd.h" diff --git a/support/arcade/buffer.cpp b/support/arcade/buffer.cpp new file mode 100644 index 0000000..34225ca --- /dev/null +++ b/support/arcade/buffer.cpp @@ -0,0 +1,195 @@ + +#include +#include +#include +#include +#include +#include + +#include "buffer.h" + + +//////////////////////////////////////////////////////////////////////////////// +// +// Function buffer_init +// +// Purpose: +// +// This function will return a struct with the values it needs before it +// can be used with buffer_append. The default "expand_length" is +// +// Parameters: +// +// initial_size (IN) This parameter will preallocate the buffer with this +// size in bytes. +// +// Returns: +// +// returns a struct, or NULL on error +// +// Error Definitions: +// +// NULL = Couldn't create the structure +// +//////////////////////////////////////////////////////////////////////////////// + +buffer_data * buffer_init(unsigned int initial_size) { + + buffer_data * buffer = NULL; + + // only allow max initial size of SIZE_MAX + if (initial_size > 65535) { + return NULL; + } + + // malloc the space for a pointer to a buffer_data + buffer = (buffer_data *)malloc(sizeof(buffer_data)); + if (buffer == NULL) { + return NULL; + } + + // fill the struct up + buffer->expand_length = initial_size; + buffer->capacity = initial_size; + buffer->length = 0; + + // allocate the buffer space + buffer->content = (char *)malloc(sizeof(char) * initial_size); + buffer->content[0]=0; + if (buffer->content == NULL) { + // can't allocate the buffer, therefore we need to clean everything up + free(buffer); + return NULL; + } + + // return a pointer to our buffer_data structure + return buffer; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Function buffer_append +// +// Purpose: +// +// This function will take data and append it into a buffer in the struct +// given. It will growing as required (or as allowed by realloc) by the +// length desired in the structure element "expand_length" plus the length +// of the data being appended. If additional expansion is not desired, you +// can set the struct element "expand_length" to zero. +// +// Parameters: +// +// buffer_data (IN/OUT) This parameter is a struct (mapped below) to +// keep track of the internal buffer and its working parameters. +// +// typedef struct { +// unsigned int length; // the current length of the content in bytes +// unsigned int capacity; // the total capacity of "content" in bytes +// unsigned int expand_length; // the amount to expand the buffer on resize +// char * content; // the target for appending data +// } buffer_data; +// +// +// append_data (IN) This parameter is a const char * that is the data +// to be appended. +// +// Returns: +// +// This function will return the amount of bytes appended. If the +// return value is <= 0 there was an error. +// +// Error Definitions: +// +// -1 = No data was given to append +// -2 = Additional space could not be allocated for the buffer. +// +//////////////////////////////////////////////////////////////////////////////// + +int buffer_append(buffer_data * buffer, const char * append_data) { + + // ensure there IS something to append.. + const unsigned int append_len = strlen(append_data); + if (append_len < 1) { + return -1; + } + + // is there enough space, or should we reallocate memory? + if (buffer->length + append_len > buffer->capacity) { + + const unsigned int realloc_size = + buffer->capacity + append_len + buffer->expand_length; + + // allocate the new buffer + char *new_buffer = (char *)realloc(buffer->content, realloc_size); + + // resize success.. + if (new_buffer == NULL) { + return -2; + } + + // copy the data to append into the new content buffer + strcat(new_buffer, append_data); + + // update the struct with the new values + buffer->content = new_buffer; // new buffer content + buffer->capacity = realloc_size; // add the amount to expand beyond our initial needs + buffer->length = buffer->length + append_len; + + } else { + // append the string + strcat(buffer->content, append_data); + } + + // update the buffer length + buffer->length += append_len; + + // return the amount appended + return append_len; +} + + +//////////////////////////////////////////////////////////////////////////////// +// +// Function buffer_destroy +// +// Purpose: +// +// This function will destroy the inner buffer and reset all of the data +// if the struct was dynamically allocated, it is expected that you will +// take care of freeing the data. +// +// Parameters: +// +// buffer_data (IN) This parameter is a struct (mapped below) to +// keep track of the internal buffer and its working parameters. +// +// typedef struct { +// unsigned int length; // the current length of the content in bytes +// unsigned int capacity; // the total capacity of "content" in bytes +// unsigned int expand_length; // the amount to expand the buffer on resize +// char * content; // the target for appending data +// } buffer_data; +// +// Returns: +// +// returns nothing +// +//////////////////////////////////////////////////////////////////////////////// + +void buffer_destroy(buffer_data * buffer) { + + // only work with valid data.. + if (buffer != NULL) { + + // if the content is not null, free it. + if (buffer->content != NULL) { + free(buffer->content); + } + + // free the buffer + free(buffer); + } + +} diff --git a/support/arcade/buffer.h b/support/arcade/buffer.h new file mode 100644 index 0000000..4a0fe3d --- /dev/null +++ b/support/arcade/buffer.h @@ -0,0 +1,15 @@ +#ifndef STRING_UTILS_H_ +#define STRING_UTILS_H_ + +typedef struct { + unsigned int length; + unsigned int capacity; + unsigned int expand_length; + char * content; +} buffer_data; + +buffer_data * buffer_init(const unsigned int initial_size); +int buffer_append(buffer_data *buffer, const char *append_data); +void buffer_destroy(buffer_data * buffer); + +#endif // STRING_UTILS_H_ diff --git a/support/arcade/romutils.cpp b/support/arcade/romutils.cpp new file mode 100644 index 0000000..ea5d558 --- /dev/null +++ b/support/arcade/romutils.cpp @@ -0,0 +1,470 @@ +#include +#include +#include +#include +#include + + +#include "sxmlc.h" + +#include "../../user_io.h" +#include "../../file_io.h" +#include "../../menu.h" + +#include "buffer.h" + + +#include "md5.h" +/* + * adapted from https://gist.github.com/xsleonard/7341172 + * + * hexstr_to_char will take hex strings in two types: + * + * 00 01 02 + * 000102 + * 00 01 2 03 + * 0001 0203 + * + * and return an array and a length of binary values + * + * caller must free string that is returned + * + * */ +unsigned char* hexstr_to_char(const char* hexstr, size_t *out_len) +{ + size_t len = strlen(hexstr); + unsigned char* chrs = (unsigned char*)malloc((len+1) * sizeof(*chrs)); + int dest=0; + // point to the beginning of the array + const char *ptr = hexstr; + while (*ptr) { + // check to see if we have a space + while (*ptr=='\n' || *ptr=='\r' || *ptr==' ' || *ptr=='\t' || *ptr==9 /*horiz tab*/) ptr++; + if (*ptr==0) break; + + // pull two characters off + int val1= (*ptr % 32 + 9) % 25 * 16; + ptr++; + /* check to odd numbers of characters*/ + if (*ptr==0) { + int val= (ptr[-1] % 32 + 9) % 25; + chrs[dest++] = val; + break; + } + int val2= (*ptr % 32 + 9) % 25; + ptr++; + chrs[dest++] = val1+val2; + } + chrs[dest]=0; + *out_len = dest; /* dest is 0 based, so we don't need to subtract 1*/ + return chrs; +} + + +/* +user_io.h:int user_io_file_tx_start(const char *name,unsigned char index=0); +user_io.h:int user_io_file_tx_body(const uint8_t *buf,uint16_t chunk); +user_io.h:int user_io_file_tx_body_filepart(const char *name,int start=0, int len=0); +user_io.h:int user_io_file_tx_finish(); +*/ + + +#define kBigTextSize 4096 +struct arc_struct { + char md5[kBigTextSize]; + char zipname[kBigTextSize]; + char partzipname[kBigTextSize]; + char partname[kBigTextSize]; + char romname[kBigTextSize]; + char error_msg[kBigTextSize]; + int romindex; + int offset; + int length; + int repeat; + int insiderom; + int validrom0; + buffer_data *data; + struct MD5Context context; +}; + +char global_error_msg[kBigTextSize]; + + +/* + * xml_send_rom + * + * This is a callback from the XML parser of the MRA file + * + * It parses the MRA, and sends commands to send rom parts to the fpga + * */ +static int xml_send_rom(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, const int n, SAX_Data* sd) +{ + struct arc_struct *arc_info = (struct arc_struct *)sd->user; + (void)(sd); + + switch (evt) + { + case XML_EVENT_START_NODE: + + /* initialization */ + + + // initialize things for each tag (node): + buffer_destroy(arc_info->data); + arc_info->data=buffer_init(kBigTextSize); + arc_info->partname[0]=0; + arc_info->offset=0; + arc_info->length=-1; + arc_info->repeat=1; + + /* on the beginning of a rom tag, we need to reset the state*/ + if (!strcasecmp(node->tag,"rom")) + { + arc_info->insiderom=1; + arc_info->romname[0]=0; + arc_info->romindex=0; + arc_info->md5[0]=0; + MD5Init (&arc_info->context); + } + + // for each part tag, we clear the partzipname since it is optional and may not appear in the part tag + if (!strcasecmp(node->tag,"part")) + arc_info->partzipname[0]=0; + + + //printf("XML_EVENT_START_NODE: tag [%s]\n",node->tag); + // walk the attributes and save them in the data structure as appropriate + for (int i = 0; i < node->n_attributes; i++) + { + //printf("attribute %d name [%s] value [%s]\n",i,node->attributes[i].name,node->attributes[i].value); + if (!strcasecmp(node->attributes[i].name,"zip") && !strcasecmp(node->tag,"rom")) + { + strcpy(arc_info->zipname,node->attributes[i].value); + } + if (!strcasecmp(node->attributes[i].name,"name") && !strcasecmp(node->tag,"rom")) + { + strcpy(arc_info->romname,node->attributes[i].value); + } + if (!strcasecmp(node->attributes[i].name,"md5") && !strcasecmp(node->tag,"rom")) + { + strcpy(arc_info->md5,node->attributes[i].value); + } + if (!strcasecmp(node->attributes[i].name,"index") && !strcasecmp(node->tag,"rom")) + { + arc_info->romindex=atoi(node->attributes[i].value); + } + /* these only exist if we are inside the rom tag, and in a part tag*/ + if (arc_info->insiderom) { + if (!strcasecmp(node->attributes[i].name,"zip") && !strcasecmp(node->tag,"part")) + { + strcpy(arc_info->partzipname,node->attributes[i].value); + } + if (!strcasecmp(node->attributes[i].name,"name") && !strcasecmp(node->tag,"part")) + { + strcpy(arc_info->partname,node->attributes[i].value); + } + if (!strcasecmp(node->attributes[i].name,"offset") && !strcasecmp(node->tag,"part")) + { + arc_info->offset=atoi(node->attributes[i].value); + } + if (!strcasecmp(node->attributes[i].name,"length") && !strcasecmp(node->tag,"part")) + { + arc_info->length=atoi(node->attributes[i].value); + } + if (!strcasecmp(node->attributes[i].name,"repeat") && !strcasecmp(node->tag,"part")) + { + arc_info->repeat=atoi(node->attributes[i].value); + } + } + } + + /* at the beginning of each rom - tell the user_io to start a new message */ + if (!strcasecmp(node->tag,"rom")) + { + + // clear an error message if we have a second rom0 + // this is kind of a problem - you will never see the + // error from the first rom0? + // + if (arc_info->romindex==0 && strlen(arc_info->zipname)) + arc_info->error_msg[0]=0; + + user_io_file_tx_start(arc_info->romname,arc_info->romindex); + } + break; + + case XML_EVENT_TEXT: + /* the text node is the data between tags, ie: this text + * + * the buffer_append is part of a buffer library that will realloc automatically + */ + buffer_append(arc_info->data, text); + //printf("XML_EVENT_TEXT: text [%s]\n",text); + break; + case XML_EVENT_END_NODE: + //printf("XML_EVENT_END_NODE: tag [%s]\n",node->tag ); + + // At the end of a rom node (when it is closed) we need to calculate hash values and clean up + if (!strcasecmp(node->tag,"rom")) { + if (arc_info->insiderom) + { + unsigned char checksum[16]; + int checksumsame=1; + char *md5=arc_info->md5; + user_io_file_tx_finish(); + MD5Final (checksum, &arc_info->context); + printf("md5[%s]\n",arc_info->md5); + printf("md5-calc["); + for (int i = 0; i < 16; i++) + { + char hex[10]; + snprintf(hex,10,"%02x", (unsigned int) checksum[i]); + printf ("%02x", (unsigned int) checksum[i]); + if (md5[0]!=hex[0] || md5[1]!=hex[1]) { + checksumsame=0; + } + md5+=2; + } + printf ("]\n"); + if (checksumsame==0) + { + printf("mismatch\n"); + if (!strlen(arc_info->error_msg)) + snprintf(arc_info->error_msg,kBigTextSize,"md5 mismatch for rom %d",arc_info->romindex); + } + else + { + // this code sets the validerom0 and clears the message + // if a rom with index 0 has a correct md5. It supresses + // sending any further rom0 messages + if (arc_info->romindex==0) + { + arc_info->validrom0=1; + arc_info->error_msg[0]=0; + } + } + } + arc_info->insiderom=0; + } + + // At the end of a part node, send the rom part if we are inside a rom tag + //int user_io_file_tx_body_filepart(const char *name,int start, int len) + if (!strcasecmp(node->tag,"part") && arc_info->insiderom) + { + // suppress rom0 if we already sent a valid one + // this is useful for merged rom sets - if the first one was valid, use it + // the second might not be + if (arc_info->romindex==0 && arc_info->validrom0==1) + break; + char fname[kBigTextSize*2+16]; + int start,length,repeat; + repeat=arc_info->repeat; + start=arc_info->offset; + length=0; + if (arc_info->length>0) length = arc_info->length; + //printf("partname[%s]\n",arc_info->partname); + //printf("zipname [%s]\n",arc_info->zipname); + //printf("offset[%d]\n",arc_info->offset); + //printf("length[%d]\n",arc_info->length); + //printf("repeat[%d]\n",arc_info->repeat); + // + if (strlen(arc_info->partzipname)) + { + if (arc_info->partzipname[0]=='/') + sprintf(fname,"arcade%s/%s",arc_info->partzipname,arc_info->partname); + else + sprintf(fname,"arcade/mame/%s/%s",arc_info->partzipname,arc_info->partname); + } + else + { + if (arc_info->zipname[0]=='/') + sprintf(fname,"arcade%s/%s",arc_info->zipname,arc_info->partname); + else + sprintf(fname,"arcade/mame/%s/%s",arc_info->zipname,arc_info->partname); + } + + + + //user_io_file_tx_body_filepart(getFullPath(fname),0,0); + if (strlen(arc_info->partname)) { + printf("user_io_file_tx_body_filepart(const char *name[%s],int start[%d], int len[%d])\n",fname,start,length); + for (int i=0;icontext); + // we should check file not found error for the zip + if (result==0) + { + int skip=0; + if (!strncasecmp(fname,"arcade/mame/",strlen("arcade/mame/"))) + skip=strlen("arcade/mame/"); + printf("%s does not exist\n",fname); + snprintf(arc_info->error_msg,kBigTextSize,"%s\n" + "\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\n" + "File Not Found",fname+skip); + } + } + } + else // we have binary data? + { + //printf("we have bin.hex data [%s]\n",arc_info->data->content); + size_t len=0; + unsigned char* binary=hexstr_to_char(arc_info->data->content,&len); + //printf("len %d:\n",len); + //for (size_t i=0;icontext); + } + if (binary) free(binary); + } + + + } + break; + + case XML_EVENT_ERROR: + printf("XML parse: %s: ERROR %d\n", text, n); + snprintf(arc_info->error_msg,kBigTextSize,"XML parse: %s: ERROR %d\n", text, n); + break; + default: + break; + } + + return true; +} +static int xml_scan_rbf(XMLEvent evt, const XMLNode* node, SXML_CHAR* text, const int n, SAX_Data* sd) +{ + static int insiderbf=0; + char bigtext[kBigTextSize]; + char *rbf = (char *)sd->user; + + switch (evt) + { + case XML_EVENT_START_NODE: + bigtext[0]=0; + if (!strcasecmp(node->tag,"rbf")) { + insiderbf=1; + } + printf("XML_EVENT_START_NODE: tag [%s]\n",node->tag); + for (int i = 0; i < node->n_attributes; i++) + { + printf("attribute %d name [%s] value [%s]\n",i,node->attributes[i].name,node->attributes[i].value); + } + + break; + case XML_EVENT_TEXT: + if (insiderbf) { + strncat(bigtext,text,kBigTextSize-strlen(bigtext)-1); + printf("XML_EVENT_TEXT: text [%s]\n",text); + } + break; + case XML_EVENT_END_NODE: + if (!strcasecmp(node->tag,"rbf")) + { + insiderbf=0; + //printf("bigtext [%s]\n",bigtext); + strncpy(rbf,bigtext,kBigTextSize); + //printf("got rbf tag [%s]\n",rbf); + } + printf("XML_EVENT_END_NODE: tag [%s]\n",node->tag ); + break; + + case XML_EVENT_ERROR: + printf("XML parse: %s: ERROR %d\n", text, n); + break; + default: + break; + } + + return true; +} + + +int arcade_send_rom(const char *xml) +{ + SAX_Callbacks sax; + SAX_Callbacks_init(&sax); + + sax.all_event = xml_send_rom; + + // create the structure we use for the XML parser + struct arc_struct arc_info; + arc_info.data=buffer_init(kBigTextSize); + arc_info.error_msg[0]=0; + arc_info.validrom0=0; + + // parse + XMLDoc_parse_file_SAX(xml, &sax, &arc_info); + if (arc_info.validrom0==0 && strlen(arc_info.error_msg)) + { + strcpy(global_error_msg,arc_info.error_msg); + printf("arcade_send_rom: pretty error: [%s]\n",global_error_msg); + } + buffer_destroy(arc_info.data); + return 0; +} + +int CheckArcadeError(void) +{ + if (global_error_msg[0]!=0) { + printf("ERROR: [%s]\n",global_error_msg); + Info(global_error_msg,1000*30); + global_error_msg[0]=0; + } + + return 0; +} + +int arcade_scan_xml_for_rbf(const char *xml,char *rbfname) +{ + char rbfname_fragment[kBigTextSize]; + rbfname_fragment[0]=0; + rbfname[0]=0; + SAX_Callbacks sax; + SAX_Callbacks_init(&sax); + + sax.all_event = xml_scan_rbf; + XMLDoc_parse_file_SAX(xml, &sax, rbfname_fragment); + strcpy(rbfname,rbfname_fragment); + + //printf("arcade_scan_xml_for_rbf [%s]\n",xml); + /* once we have the rbfname fragment from the MRA xml file + * search the arcade folder for the match */ + struct dirent *entry; + DIR *dir=NULL; + //printf("opendir(%s)\n",getFullPath("arcade")); + if (!(dir = opendir(getFullPath("arcade")))) + { + printf("arcade directory not found\n"); + return 0; + } + + while ((entry = readdir(dir)) != NULL) { + if (entry->d_type == DT_DIR) { + } + else { + char newstring[kBigTextSize]; + //printf("entry name: %s\n",entry->d_name); + snprintf(newstring,kBigTextSize,"Arcade-%s_",rbfname_fragment); + if (!strncasecmp(newstring,entry->d_name,strlen(newstring))) { + closedir(dir); + strcpy(rbfname,entry->d_name); + return 0; + + } + snprintf(newstring,kBigTextSize,"%s_",rbfname_fragment); + if (!strncasecmp(newstring,entry->d_name,strlen(newstring))) { + closedir(dir); + strcpy(rbfname,entry->d_name); + return 0; + } + + } + } + + if (dir) closedir(dir); + strcpy(rbfname,rbfname_fragment); + return 0; +} + diff --git a/support/arcade/romutils.h b/support/arcade/romutils.h new file mode 100644 index 0000000..b367b23 --- /dev/null +++ b/support/arcade/romutils.h @@ -0,0 +1,5 @@ +#include "../../file_io.h" + +int arcade_send_rom(const char *xml); +int arcade_scan_xml_for_rbf(const char *xml,char *rbfname); +int CheckArcadeError(void); diff --git a/user_io.cpp b/user_io.cpp index 9901ffa..dbf0fbe 100644 --- a/user_io.cpp +++ b/user_io.cpp @@ -12,6 +12,8 @@ #include #include "lib/lodepng/lodepng.h" +#include "md5.h" + #include "hardware.h" #include "osd.h" #include "user_io.h" @@ -691,16 +693,26 @@ int user_io_is_dualsdr() return dual_sdr; } -void user_io_init(const char *path) +void user_io_init(const char *path, const char *xml) { char *name; static char mainpath[512]; core_name[0] = 0; disable_osd = 0; + // we need to set the directory to where the XML file (MRA) is + // not the RBF. The RBF will be in arcade, which the user shouldn't + // browse + if (strlen(xml)) { + //printf("USER_IO_INIT got XML: [%s] [%s]\n",path,xml); + strcpy(core_path, getFullPath(xml)); + } else { + strcpy(core_path, path); + } + //printf("USER_IO_INIT core_path: [%s] \n",core_path); + memset(sd_image, 0, sizeof(sd_image)); - strcpy(core_path, path); core_type = (fpga_core_id() & 0xFF); fio_size = fpga_get_fio_size(); io_ver = fpga_get_io_version(); @@ -857,6 +869,14 @@ void user_io_init(const char *path) if (user_io_use_cheats()) cheats_init("", user_io_get_file_crc()); } + /* AJS -- NOT SURE THIS IS THE BEST PLACE */ + if (strlen(xml)) { + //sprintf(mainpath, "%s/%s", user_io_get_core_path(), xml); + printf("USER_IO_INIT got XML: [%s]\n",xml); + //printf("USER_IO_INIT got XML: [%s]\n",mainpath); + arcade_send_rom(getFullPath(xml)); + } + if (is_cpc_core()) { for (int m = 0; m < 3; m++) @@ -1579,6 +1599,101 @@ static void check_status_change() } } +#define DEBUG_ROM_BINARY 0 +#if DEBUG_ROM_BINARY +FILE *rombinary; +#endif + +int user_io_file_tx_start(const char *name,unsigned char index) +{ + // set index byte (0=bios rom, 1-n=OSD entry index) + user_io_set_index(index); + + int len = strlen(name); + const char *p = name + len - 4; + EnableFpga(); + spi8(UIO_FILE_INFO); + spi_w(toupper(p[0]) << 8 | toupper(p[1])); + spi_w(toupper(p[2]) << 8 | toupper(p[3])); + DisableFpga(); + + // prepare transmission of new file + EnableFpga(); + spi8(UIO_FILE_TX); + spi8(0xff); + DisableFpga(); + + file_crc = 0; +#if DEBUG_ROM_BINARY + rombinary=fopen("/media/fat/this.rom","wb"); +#endif + return 1; +} +int user_io_file_tx_body(const uint8_t *buf,uint16_t chunk,struct MD5Context *md5context) +{ + printf("."); + + EnableFpga(); + spi8(UIO_FILE_TX_DAT); + + spi_write(buf, chunk, fio_size); + DisableFpga(); + + file_crc = crc32(file_crc, buf ,chunk ); + if (md5context) MD5Update (md5context, buf,chunk); +#if DEBUG_ROM_BINARY + fwrite(buf,1,chunk,rombinary); +#endif + + return 1; +} +int user_io_file_tx_body_filepart(const char *name,int start, int len,struct MD5Context *md5context) +{ + char mute=0; + fileTYPE f = {}; + static uint8_t buf[4096]; + if (!FileOpen(&f, name, mute)) return 0; + if (start) FileSeek(&f, start, SEEK_SET); + unsigned long bytes2send = f.size; + if (len>0 && len < bytes2send) bytes2send=len; + /* transmit the entire file using one transfer */ + printf("Selected file %s with %lu bytes to send \n", name, bytes2send); + while (bytes2send) + { + printf("."); + + uint16_t chunk = (bytes2send > sizeof(buf)) ? sizeof(buf) : bytes2send; + + FileReadAdv(&f, buf, chunk); + user_io_file_tx_body(buf,chunk,md5context); + + + bytes2send -= chunk; + } + + + return 1; +} +int user_io_file_tx_finish() +{ + // check if core requests some change while downloading + check_status_change(); + + printf("\n"); + printf("CRC32: %08X\n", file_crc); +#if DEBUG_ROM_BINARY + fclose(rombinary); +#endif + + // signal end of transmission + EnableFpga(); + spi8(UIO_FILE_TX); + spi8(0x00); + DisableFpga(); + printf("\n"); + return 1; +} + static char pchar[] = { 0x8C, 0x8F, 0x7F }; #define PROGRESS_CNT 28 diff --git a/user_io.h b/user_io.h index 82a6e2b..6e04c36 100644 --- a/user_io.h +++ b/user_io.h @@ -199,7 +199,7 @@ typedef struct { uint8_t fifo_stat; // space in cores input fifo } __attribute__((packed)) serial_status_t; -void user_io_init(const char *path); +void user_io_init(const char *path, const char *xml); unsigned char user_io_core_type(); void user_io_poll(); char user_io_menu_button(); @@ -210,6 +210,13 @@ void user_io_read_confstr(); char *user_io_get_confstr(int index); uint32_t user_io_8bit_set_status(uint32_t, uint32_t, int ex = 0); int user_io_file_tx(const char* name, unsigned char index = 0, char opensave = 0, char mute = 0, char composite = 0); + +int user_io_file_tx_start(const char *name,unsigned char index=0); +int user_io_file_tx_body(const uint8_t *buf,uint16_t chunk,struct MD5Context *md5context=NULL); +int user_io_file_tx_body_filepart(const char *name,int start=0, int len=0,struct MD5Context *md5context=NULL); +int user_io_file_tx_finish(); + + uint32_t user_io_get_file_crc(); int user_io_file_mount(char *name, unsigned char index = 0, char pre = 0); char user_io_serial_status(serial_status_t *, uint8_t); diff --git a/video.cpp b/video.cpp index 5b0699e..b32ab3c 100644 --- a/video.cpp +++ b/video.cpp @@ -603,6 +603,8 @@ static uint32_t show_video_info(int force) Info(str, cfg.video_info * 1000); } + CheckArcadeError(); + uint32_t scrh = v_cur.item[5]; if (height && scrh) {