int lynx_init (st_rominfo_t *rominfo) { int result = -1; rominfo->console_usage = lynx_usage[0].help; rominfo->copier_usage = unknown_usage[0].help; ucon64_fread (&lnx_header, 0, LNX_HEADER_LEN, ucon64.rom); if (!strncmp (lnx_header.magic, "LYNX", 4)) result = 0; else result = -1; if (ucon64.console == UCON64_LYNX) result = 0; if (!strncmp (lnx_header.magic, "LYNX", 4)) { rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? ucon64.buheader_len : (int) LNX_HEADER_LEN; if (UCON64_ISSET (ucon64.buheader_len) && !ucon64.buheader_len) return ucon64.console == UCON64_LYNX ? 0 : result; ucon64_fread (&lnx_header, 0, LNX_HEADER_LEN, ucon64.rom); rominfo->buheader = &lnx_header; // internal ROM name strcpy (rominfo->name, lnx_header.cartname); // ROM maker rominfo->maker = lnx_header.manufname; // misc stuff sprintf (rominfo->misc, "Internal Size: Bank0 %hd Bytes (%.4f Mb)\n" " Bank1 %hd Bytes (%.4f Mb)\n" "Version: %hd\n" "Rotation: %s", #ifdef WORDS_BIGENDIAN bswap_16 (lnx_header.page_size_bank0) * 256, TOMBIT_F (bswap_16 (lnx_header.page_size_bank0) * 256), bswap_16 (lnx_header.page_size_bank1) * 256, TOMBIT_F (bswap_16 (lnx_header.page_size_bank1) * 256), bswap_16 (lnx_header.version), #else lnx_header.page_size_bank0 * 256, TOMBIT_F (lnx_header.page_size_bank0 * 256), lnx_header.page_size_bank1 * 256, TOMBIT_F (lnx_header.page_size_bank1 * 256), lnx_header.version, #endif (!lnx_header.rotation) ? "No" : ((lnx_header.rotation == 1) ? "Left" : "Right")); } return result; }
int ppf_set_fid (const char *ppf, const char *fidname) { int fidsize, ppfsize, pos; char ppfname[FILENAME_MAX], fidbuf[MAX_ID_SIZE + 34 + 1] = "@BEGIN_FILE_ID.DIZ"; // +1 for string terminator strcpy (ppfname, ppf); ucon64_file_handler (ppfname, NULL, 0); fcopy (ppf, 0, fsizeof (ppf), ppfname, "wb"); // no copy if one file printf ("Adding FILE_ID.DIZ (%s)...\n", fidname); fidsize = ucon64_fread (fidbuf + 18, 0, MAX_ID_SIZE, fidname); memcpy (fidbuf + 18 + fidsize, "@END_FILE_ID.DIZ", 16); ppfsize = fsizeof (ppfname); pos = ucon64_find (ppfname, 0, ppfsize, "@BEGIN_FILE_ID.DIZ", 18, MEMCMP2_CASE | UCON64_FIND_QUIET); if (pos == -1) pos = ppfsize; truncate (ppfname, pos); ucon64_fwrite (fidbuf, pos, fidsize + 18 + 16, ppfname, "r+b"); pos += fidsize + 18 + 16; #ifdef WORDS_BIGENDIAN fidsize = bswap_32 (fidsize); // Write file size in little-endian format #endif ucon64_fwrite (&fidsize, pos, 4, ppfname, "r+b"); printf (ucon64_msg[WROTE], ppfname); return 0; }
static int lynx_b (st_rominfo_t *rominfo, int bank, const char *value) { st_lnx_header_t header; short int *bankvar; char dest_name[FILENAME_MAX]; if (!rominfo->buheader_len) { fprintf (stderr, "ERROR: This is no LNX file\n\n"); return -1; } ucon64_fread (&header, 0, sizeof (st_lnx_header_t), ucon64.rom); bankvar = (bank == 0 ? &header.page_size_bank0 : &header.page_size_bank1); if ((atol (value) % 64) != 0 || (atol (value) > 512)) *bankvar = 0; else #ifdef WORDS_BIGENDIAN *bankvar = bswap_16 (atol (value) * 4); #else *bankvar = atol (value) * 4; #endif strcpy (dest_name, ucon64.rom); ucon64_file_handler (dest_name, NULL, 0); fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "r+b"); printf (ucon64_msg[WROTE], dest_name); return 0; }
int pce_swap (st_ucon64_nfo_t *rominfo) { char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; unsigned char *rom_buffer; int size = ucon64.file_size - rominfo->backup_header_len; if ((rom_buffer = (unsigned char *) malloc (size)) == NULL) { fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); return -1; } strcpy (src_name, ucon64.fname); strcpy (dest_name, ucon64.fname); ucon64_file_handler (dest_name, src_name, 0); if (rominfo->backup_header_len) // copy header (if present) fcopy (src_name, 0, rominfo->backup_header_len, dest_name, "wb"); ucon64_fread (rom_buffer, rominfo->backup_header_len, size, src_name); swapbits (rom_buffer, size); ucon64_fwrite (rom_buffer, rominfo->backup_header_len, size, dest_name, rominfo->backup_header_len ? "ab" : "wb"); free (rom_buffer); printf (ucon64_msg[WROTE], dest_name); remove_temp_file (); return 0; }
int lynx_n (st_rominfo_t *rominfo, const char *name) { st_lnx_header_t header; char dest_name[FILENAME_MAX]; if (!rominfo->buheader_len) { fprintf (stderr, "ERROR: This is no LNX file\n\n"); return -1; } ucon64_fread (&header, 0, sizeof (st_lnx_header_t), ucon64.rom); memset (header.cartname, 0, sizeof (header.cartname)); strncpy (header.cartname, name, sizeof (header.cartname)); strcpy (dest_name, ucon64.rom); ucon64_file_handler (dest_name, NULL, 0); fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "r+b"); printf (ucon64_msg[WROTE], dest_name); return 0; }
int pce_f (st_ucon64_nfo_t *rominfo) /* Region protection codes are found in (American) TurboGrafx-16 games. It prevents those games from running on a PC-Engine. One search pattern seems sufficient to fix/crack all TG-16 games. In addition to that, the protection code appears to be always somewhere in the first 32 kB. */ { char src_name[FILENAME_MAX], dest_name[FILENAME_MAX], buffer[32 * 1024]; int bytesread, n; puts ("Attempting to fix region protection code..."); strcpy (src_name, ucon64.fname); strcpy (dest_name, ucon64.fname); ucon64_file_handler (dest_name, src_name, 0); fcopy (src_name, 0, ucon64.file_size, dest_name, "wb"); // no copy if one file if ((bytesread = ucon64_fread (buffer, rominfo->backup_header_len, 32 * 1024, src_name)) <= 0) return -1; // '!' == ASCII 33 (\x21), '*' == 42 (\x2a) if (rominfo->interleaved) n = change_mem (buffer, bytesread, "\x94\x02\x0f", 3, '*', '!', "\x01", 1, 0); else n = change_mem (buffer, bytesread, "\x29\x40\xf0", 3, '*', '!', "\x80", 1, 0); ucon64_fwrite (buffer, rominfo->backup_header_len, 32 * 1024, dest_name, "r+b"); printf ("Found %d pattern%s\n", n, n != 1 ? "s" : ""); printf (ucon64_msg[WROTE], dest_name); remove_temp_file (); return n; }
int jaguar_init (st_rominfo_t *rominfo) { int result = -1, x, value; rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? ucon64.buheader_len : 0; ucon64_fread (&jaguar_header, JAGUAR_HEADER_START + rominfo->buheader_len, JAGUAR_HEADER_LEN, ucon64.rom); value = 0; for (x = 0; x < 12; x++) value += OFFSET (jaguar_header, x); if (value == 0xb0) result = 0; else { rominfo->buheader_len = UCON64_ISSET (ucon64.buheader_len) ? ucon64.buheader_len : (int) UNKNOWN_HEADER_LEN; ucon64_fread (&jaguar_header, JAGUAR_HEADER_START + rominfo->buheader_len, JAGUAR_HEADER_LEN, ucon64.rom); value = 0; for (x = 0; x < 12; x++) value += OFFSET (jaguar_header, x); if (value == 0xb0) result = 0; else result = -1; } if (ucon64.console == UCON64_JAG) result = 0; rominfo->header_start = JAGUAR_HEADER_START; rominfo->header_len = JAGUAR_HEADER_LEN; rominfo->header = &jaguar_header; rominfo->console_usage = jaguar_usage[0].help; rominfo->copier_usage = unknown_usage[0].help; return result; }
// header format is specified in src/backup/ffe.h int pce_msg (st_ucon64_nfo_t *rominfo) { char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; unsigned char *rom_buffer = NULL; st_msg_header_t header; int size = ucon64.file_size - rominfo->backup_header_len; if (rominfo->interleaved) if ((rom_buffer = (unsigned char *) malloc (size)) == NULL) { fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); return -1; } memset (&header, 0, MSG_HEADER_LEN); header.size = (unsigned char) (size / 8192); header.emulation = size == 3 * MBIT ? 1 : 0; header.id1 = 0xaa; header.id2 = 0xbb; header.type = 2; strcpy (src_name, ucon64.fname); strcpy (dest_name, ucon64.fname); set_suffix (dest_name, ".msg"); ucon64_file_handler (dest_name, src_name, 0); ucon64_fwrite (&header, 0, MSG_HEADER_LEN, dest_name, "wb"); if (rominfo->interleaved) { // Magic Super Griffin files should not be "interleaved" ucon64_fread (rom_buffer, rominfo->backup_header_len, size, src_name); swapbits (rom_buffer, size); ucon64_fwrite (rom_buffer, MSG_HEADER_LEN, size, dest_name, "ab"); free (rom_buffer); } else fcopy (src_name, rominfo->backup_header_len, size, dest_name, "ab"); printf (ucon64_msg[WROTE], dest_name); remove_temp_file (); return 0; }
static int lynx_rot (st_rominfo_t *rominfo, int rotation) { st_lnx_header_t header; char dest_name[FILENAME_MAX]; if (!rominfo->buheader_len) { fprintf (stderr, "ERROR: This is no LNX file\n\n"); return -1; } ucon64_fread (&header, 0, sizeof (st_lnx_header_t), ucon64.rom); header.rotation = rotation; // header.rotation is an 8-bit field strcpy (dest_name, ucon64.rom); ucon64_file_handler (dest_name, NULL, 0); fcopy (ucon64.rom, 0, ucon64.file_size, dest_name, "wb"); ucon64_fwrite (&header, 0, sizeof (st_lnx_header_t), dest_name, "r+b"); printf (ucon64_msg[WROTE], dest_name); return 0; }
// see src/backup/mgd.h for the file naming scheme int pce_mgd (st_ucon64_nfo_t *rominfo) { char src_name[FILENAME_MAX], dest_name[FILENAME_MAX]; unsigned char *rom_buffer = NULL; int size = ucon64.file_size - rominfo->backup_header_len; if (!rominfo->interleaved) if ((rom_buffer = (unsigned char *) malloc (size)) == NULL) { fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); return -1; } strcpy (src_name, ucon64.fname); mgd_make_name (ucon64.fname, UCON64_PCE, size, dest_name); ucon64_file_handler (dest_name, src_name, OF_FORCE_BASENAME); // bit-swapping images for the MGD2 only makes sense for owners of a TG-16 // (American version of the PCE) if (!rominfo->interleaved) { ucon64_fread (rom_buffer, rominfo->backup_header_len, size, src_name); swapbits (rom_buffer, size); ucon64_fwrite (rom_buffer, 0, size, dest_name, "wb"); free (rom_buffer); } else fcopy (src_name, rominfo->backup_header_len, size, dest_name, "wb"); printf (ucon64_msg[WROTE], dest_name); remove_temp_file (); mgd_write_index_file ((char *) basename2 (dest_name), 1); return 0; }
// based on sourcecode of MakePPF v2.0 Linux/Unix by Icarus/Paradox int ppf_create (const char *orgname, const char *modname) { FILE *orgfile, *modfile, *ppffile; char ppfname[FILENAME_MAX], buffer[MAX_ID_SIZE], obuf[512], mbuf[512]; #if 0 char *fidname = "FILE_ID.DIZ"; #endif int x, osize, msize, blocksize, n_changes, total_changes = 0; unsigned int seekpos = 0, pos; osize = fsizeof (orgname); msize = fsizeof (modname); #ifndef DIFF_FSIZE if (osize != msize) { fprintf (stderr, "ERROR: File sizes do not match\n"); return -1; } #endif if ((orgfile = fopen (orgname, "rb")) == NULL) { fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], orgname); exit (1); } if ((modfile = fopen (modname, "rb")) == NULL) { fprintf (stderr, ucon64_msg[OPEN_READ_ERROR], modname); exit (1); } strcpy (ppfname, modname); set_suffix (ppfname, ".ppf"); ucon64_file_handler (ppfname, NULL, 0); if ((ppffile = fopen (ppfname, "wb")) == NULL) { fprintf (stderr, ucon64_msg[OPEN_WRITE_ERROR], ppfname); exit (1); } // creating PPF 2.0 header fwrite ("PPF20", 5, 1, ppffile); // magic fputc (1, ppffile); // encoding method memset (buffer, ' ', 50); fwrite (buffer, 50, 1, ppffile); // description line #ifdef WORDS_BIGENDIAN x = bswap_32 (osize); fwrite (&x, 4, 1, ppffile); #else fwrite (&osize, 4, 1, ppffile); // orgfile size #endif fseek (orgfile, 0x9320, SEEK_SET); memset (buffer, 0, 1024); // one little hack that makes PPF fread (buffer, 1024, 1, orgfile); // suitable for files < 38688 bytes fwrite (buffer, 1024, 1, ppffile); // 1024 byte block printf ("Writing patch data, please wait...\n"); // finding changes fseek (orgfile, 0, SEEK_SET); fseek (modfile, 0, SEEK_SET); while ((blocksize = fread (obuf, 1, 255, orgfile))) { blocksize = fread (mbuf, 1, blocksize, modfile); #ifdef DIFF_FSIZE if (blocksize == 0) break; #endif pos = seekpos; x = 0; while (x != blocksize) { if (obuf[x] != mbuf[x]) { pos = seekpos + x; n_changes = 0; do { buffer[n_changes] = mbuf[x]; n_changes++; x++; } while (x != blocksize && obuf[x] != mbuf[x]); total_changes += n_changes; #ifdef WORDS_BIGENDIAN pos = bswap_32 (pos); #endif fwrite (&pos, 4, 1, ppffile); fputc (n_changes, ppffile); fwrite (buffer, n_changes, 1, ppffile); } else x++; } seekpos += blocksize; } #ifdef DIFF_FSIZE if (msize > osize) { pos = seekpos; while ((blocksize = fread (buffer, 1, 255, modfile))) { total_changes += blocksize; #ifdef WORDS_BIGENDIAN x = bswap_32 (pos); fwrite (&x, 4, 1, ppffile); #else fwrite (&pos, 4, 1, ppffile); #endif fputc (blocksize, ppffile); fwrite (buffer, blocksize, 1, ppffile); pos += blocksize; } } else if (msize < osize) printf ("WARNING: %s is smaller than %s\n" " PPF can't store information about that fact\n", modname, orgname); #endif fclose (orgfile); fclose (modfile); if (total_changes == 0) { printf ("%s and %s are identical\n" "Removing: %s\n", orgname, modname, ppfname); fclose (ppffile); remove (ppfname); return -1; } #if 0 if (fidname) { int fsize = fsizeof (fidname); if (fsize > MAX_ID_SIZE) fsize = MAX_ID_SIZE; // File id only up to 3072 bytes! printf ("Adding FILE_ID.DIZ (%s)...\n", fidname); ucon64_fread (buffer, 0, fsize, fidname); fwrite ("@BEGIN_FILE_ID.DIZ", 18, 1, ppffile); fwrite (buffer, fsize, 1, ppffile); fwrite ("@END_FILE_ID.DIZ", 16, 1, ppffile); #ifdef WORDS_BIGENDIAN fsize = bswap_32 (fsize); // Write file size in little-endian format #endif fwrite (&fsize, 4, 1, ppffile); } #endif fclose (ppffile); printf (ucon64_msg[WROTE], ppfname); return 0; }
int pce_init (st_ucon64_nfo_t *rominfo) { int result = -1, size, swapped, x; unsigned char *rom_buffer; st_pce_data_t *info, key; x = ucon64.file_size % (16 * 1024); rominfo->backup_header_len = UCON64_ISSET (ucon64.backup_header_len) ? ucon64.backup_header_len : (ucon64.file_size > x ? x : 0); size = ucon64.file_size - rominfo->backup_header_len; if ((rom_buffer = (unsigned char *) malloc (size)) == NULL) { fprintf (stderr, ucon64_msg[ROM_BUFFER_ERROR], size); return -1; } ucon64_fread (rom_buffer, rominfo->backup_header_len, size, ucon64.fname); if (pce_check (rom_buffer, size) == 1) result = 0; swapped = -1; /* 29 40 f0 => unhacked 29 40 80 => U.S.A. -> Jap hack 94 02 0f => bit-swapped and unhacked 94 02 01 => bit-swapped and hacked According to Cowering 2 or 3 games don't use these opcodes to check if a Japanese game is running on an American console. If they are present then it is in the first 500 (or so) bytes. The check for "A..Z" is a fix for Puzznic (J). The check for "HESM" a fix for: Fire Pro Wrestling - 2nd Bout Sounds Fire Pro Wrestling 3 - Legend Bout Sounds */ x = size > 32768 ? 32768 : size; if ((memmem2 (rom_buffer, x, "\x94\x02\x0f", 3, 0) || memmem2 (rom_buffer, x, "\x94\x02\x01", 3, 0)) && memmem2 (rom_buffer, x, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26, 0) == 0 && memcmp (rom_buffer, "HESM", 4)) swapped = 1; if (UCON64_ISSET (ucon64.interleaved)) swapped = ucon64.interleaved; if ((result == -1 && swapped != 0) || swapped == 1) { // don't swap the bits if -nint is specified if (!UCON64_ISSET (ucon64.do_not_calc_crc) || swapped == 1) ucon64.fcrc32 = crc32 (0, rom_buffer, size); swapbits (rom_buffer, size); if (pce_check (rom_buffer, size) == 1) { swapped = 1; result = 0; } if (swapped != 1) { ucon64.crc32 = ucon64.fcrc32; ucon64.fcrc32 = 0; } } if (swapped != -1) rominfo->interleaved = swapped; if (ucon64.console == UCON64_PCE) result = 0; rominfo->header_start = PCE_HEADER_START; rominfo->header_len = PCE_HEADER_LEN; rominfo->console_usage = pce_usage[0].help; rominfo->backup_usage = rominfo->backup_header_len ? msg_usage[0].help : mgd_usage[0].help; if (!UCON64_ISSET (ucon64.do_not_calc_crc) && result == 0) { if (!ucon64.crc32) ucon64.crc32 = crc32 (0, rom_buffer, size); // additional info key.crc32 = ucon64.crc32; info = (st_pce_data_t *) bsearch (&key, pce_data, sizeof pce_data / sizeof (st_pce_data_t), sizeof (st_pce_data_t), pce_compare); if (info) { if (info->maker) rominfo->maker = NULL_TO_UNKNOWN_S (pce_maker[MIN (info->maker, PCE_MAKER_MAX - 1)]); if (info->serial) if (info->serial[0]) { strcat (rominfo->misc, "\nSerial: "); strcat (rominfo->misc, info->serial); } if (info->date) { int day = info->date / 10000, month = (info->date % 10000) / 100, year = info->date % 100; char date[80]; date[0] = 0; if (day) sprintf (date, "\nDate: %d/%d/19%d", day, month, year); else if (month) sprintf (date, "\nDate: %d/19%d", month, year); else if (year) sprintf (date, "\nDate: 19%d", year); strcat (rominfo->misc, date); } if (info->comment) if (info->comment[0]) { strcat (rominfo->misc, "\nComment: "); strcat (rominfo->misc, info->comment); } } } free (rom_buffer); return result; }
int atari_init (st_ucon64_nfo_t * rominfo) { int i, j, bsmode, size = ucon64.file_size; unsigned int crc32; static char backup_usage[80]; unsigned char first, image[ATARI_ROM_SIZE], buffer[0x200]; char md5[32]; if (size > ATARI_ROM_SIZE) return -1; if (size != 0x800 && size != 0x1000 && size != 0x2000 && size != 0x2100 && size != 0x2800 && size != 0x28ff && size != 0x3000 && size != 0x4000 && size != 0x8000 && size != 0x10000 && size != 0x20000) return -1; ucon64_fread (image, 0, size, ucon64.fname); ucon64_chksum (NULL, md5, &crc32, ucon64.fname, ucon64.file_size, 0); bsmode = get_game_bsmode_by_crc (crc32); if (bsmode == -1) bsmode = get_game_bsmode_by_md5 (md5); if (bsmode == -1) { if (!(size % 8448)) bsmode = BSM_AR; else if (size == 2048 || !memcmp (image, image + 2048, 2048)) bsmode = BSM_2K; else if (size == 4096 || !memcmp (image, image + 4096, 4096)) bsmode = BSM_4K; else if (size == 8192 || !memcmp (image, image + 8192, 8192)) bsmode = is_probably_3f (image, size) ? BSM_3F : BSM_F8; else if (size == 10495 || (size == 10240)) bsmode = BSM_DPC; else if (size == 12288) bsmode = BSM_FASC; else if (size == 32768) { // Assume this is a 32K super-cart then check to see if it is bsmode = BSM_F4SC; first = image[0]; for (i = 0; i < 0x100; i++) if (image[i] != first) { // It's not a super cart (probably) bsmode = is_probably_3f (image, size) ? BSM_3F : BSM_F4; break; } } else if (size == 65536) bsmode = is_probably_3f (image, size) ? BSM_3F : BSM_MB; else if (size == 131072) bsmode = is_probably_3f (image, size) ? BSM_3F : BSM_MC; else { // Assume this is a 16K super-cart then check to see if it is bsmode = BSM_F6SC; first = image[0]; for (i = 0; i < 0x100; i++) if (image[i] != first) { // It's not a super cart (probably) bsmode = is_probably_3f (image, size) ? BSM_3F : BSM_F6; break; } } } if (bsmode > 0) { memset (&atari_rominfo, 0, sizeof (st_atari_rominfo_t)); atari_rominfo.bsm = bsmode; // set game_page_count and empty_page[] for (i = 0; i < size / 0x100; i++) { ucon64_fread (buffer, i * 0x100, 0x100, ucon64.fname); atari_rominfo.empty_page[i] = 1; for (j = 0; j < 0x100 - 1; j++) if (buffer[j] != buffer[j + 1]) { atari_rominfo.empty_page[i] = 0; atari_rominfo.game_page_count++; break; } } atari_rominfo.speed_hi = (unsigned char) (atari_rominfo.game_page_count / 21 + 1); atari_rominfo.speed_low = (unsigned char) (atari_rominfo.game_page_count * 0x100 / 21 - (atari_rominfo.speed_hi - 1) * 0x100); atari_rominfo.ctrl_byte = get_bsmode_by_id (atari_rominfo.bsm)->ctrl_byte; // the first two bytes of data indicate the beginning address of the code if (atari_rominfo.bsm != BSM_3F) { ucon64_fread (buffer, get_bsmode_by_id (atari_rominfo.bsm)->start_page * 0x100, 0x100, ucon64.fname); atari_rominfo.start_low = buffer[0xfc]; atari_rominfo.start_hi = buffer[0xfd]; } rominfo->console_usage = atari_usage[0].help; // "Cuttle Card (2)/Starpath) Supercharger/YOKO backup unit" snprintf (backup_usage, 80, "%s/%s/%s", cc2_usage[0].help, spsc_usage[0].help, yoko_usage[0].help); backup_usage[80 - 1] = 0; rominfo->backup_usage = backup_usage; sprintf (rominfo->misc, "Bankswitch type: %s\n" "Start address: 0x%02x%02x\n" "Speed: 0x%02x%02x\n" "Control byte: 0x%02x\n" "Page count: %d\n" "Blank pages: %d\n" "Start page: %d", get_bsmode_by_id (atari_rominfo.bsm)->bsm_s, atari_rominfo.start_hi, atari_rominfo.start_low, atari_rominfo.speed_hi, atari_rominfo.speed_low, atari_rominfo.ctrl_byte, atari_rominfo.game_page_count, size / 0x100 - atari_rominfo.game_page_count, get_bsmode_by_id (atari_rominfo.bsm)->start_page); return 0; } return -1; }