X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=pico%2Fcart.c;h=9fcb01a4daf6323f824489efc7ba40b773a6aa32;hb=HEAD;hp=d86af41eaec738c4320e3736c1675a12a96738dc;hpb=4c2e35547fd6f849648234989419a4a02d2546b4;p=picodrive.git diff --git a/pico/cart.c b/pico/cart.c index d86af41e..a196dfdf 100644 --- a/pico/cart.c +++ b/pico/cart.c @@ -2,20 +2,25 @@ * PicoDrive * (c) Copyright Dave, 2004 * (C) notaz, 2006-2010 + * (C) irixxxx, 2020-2024 * * This work is licensed under the terms of MAME license. * See COPYING file in the top-level directory. */ #include "pico_int.h" -#include "../zlib/zlib.h" -#include "../cpu/debug.h" -#include "../unzip/unzip.h" -#include "../unzip/unzip_stream.h" +#include +#if defined(USE_LIBCHDR) +#include "libchdr/chd.h" +#include "libchdr/cdrom.h" +#endif + +#include +#include static int rom_alloc_size; -static const char *rom_exts[] = { "bin", "gen", "smd", "iso", "sms", "gg", "sg" }; +static const char *rom_exts[] = { "bin", "gen", "smd", "md", "32x", "pco", "iso", "sms", "gg", "sg", "sc" }; void (*PicoCartUnloadHook)(void); void (*PicoCartMemSetup)(void); @@ -26,6 +31,7 @@ void (*PicoCDLoadProgressCB)(const char *fname, int percent) = NULL; // handled int PicoGameLoaded; static void PicoCartDetect(const char *carthw_cfg); +static void PicoCartDetectMS(void); /* cso struct */ typedef struct _cso_struct @@ -50,7 +56,7 @@ typedef struct _cso_struct } cso_struct; -static int uncompress2(void *dest, int destLen, void *source, int sourceLen) +static int uncompress_buf(void *dest, int destLen, void *source, int sourceLen) { z_stream stream; int err; @@ -90,6 +96,29 @@ static const char *get_ext(const char *path) return ext; } +struct zip_file { + pm_file file; + ZIP *zip; + struct zipent *entry; + z_stream stream; + unsigned char inbuf[16384]; + long start; + unsigned int pos; +}; + +#if defined(USE_LIBCHDR) +struct chd_struct { + pm_file file; + int fpos; + int sectorsize; + chd_file *chd; + int unitbytes; + int hunkunits; + u8 *hunk; + int hunknum; +}; +#endif + pm_file *pm_open(const char *path) { pm_file *file = NULL; @@ -102,10 +131,10 @@ pm_file *pm_open(const char *path) ext = get_ext(path); if (strcasecmp(ext, "zip") == 0) { + struct zip_file *zfile = NULL; struct zipent *zipentry; - gzFile gzf = NULL; ZIP *zipfile; - int i; + int i, ret; zipfile = openzip(path); if (zipfile != NULL) @@ -127,37 +156,41 @@ pm_file *pm_open(const char *path) goto zip_failed; found_rom_zip: - /* try to convert to gzip stream, so we could use standard gzio functions from zlib */ - gzf = zip2gz(zipfile, zipentry); - if (gzf == NULL) goto zip_failed; - - file = calloc(1, sizeof(*file)); - if (file == NULL) goto zip_failed; - file->file = zipfile; - file->param = gzf; - file->size = zipentry->uncompressed_size; - file->type = PMT_ZIP; - strncpy(file->ext, ext, sizeof(file->ext) - 1); - return file; + zfile = calloc(1, sizeof(*zfile)); + if (zfile == NULL) + goto zip_failed; + ret = seekcompresszip(zipfile, zipentry); + if (ret != 0) + goto zip_failed; + ret = inflateInit2(&zfile->stream, -15); + if (ret != Z_OK) { + elprintf(EL_STATUS, "zip: inflateInit2 %d", ret); + goto zip_failed; + } + zfile->zip = zipfile; + zfile->entry = zipentry; + zfile->start = ftell(zipfile->fp); + zfile->file.file = zfile; + zfile->file.size = zipentry->uncompressed_size; + zfile->file.type = PMT_ZIP; + strncpy(zfile->file.ext, ext, sizeof(zfile->file.ext) - 1); + return &zfile->file; zip_failed: - if (gzf) { - gzclose(gzf); - zipfile->fp = NULL; // gzclose() closed it - } closezip(zipfile); + free(zfile); return NULL; } } else if (strcasecmp(ext, "cso") == 0) { cso_struct *cso = NULL, *tmp = NULL; - int size; + int i, size; f = fopen(path, "rb"); if (f == NULL) goto cso_failed; -#ifndef __EPOC32__ +#ifdef __GP2X__ /* we use our own buffering */ setvbuf(f, NULL, _IONBF, 0); #endif @@ -168,6 +201,9 @@ zip_failed: if (fread(&cso->header, 1, sizeof(cso->header), f) != sizeof(cso->header)) goto cso_failed; + cso->header.block_size = CPU_LE4(cso->header.block_size); + cso->header.total_bytes = CPU_LE4(cso->header.total_bytes); + cso->header.total_bytes_high = CPU_LE4(cso->header.total_bytes_high); if (strncmp(cso->header.magic, "CISO", 4) != 0) { elprintf(EL_STATUS, "cso: bad header"); @@ -191,6 +227,8 @@ zip_failed: elprintf(EL_STATUS, "cso: premature EOF"); goto cso_failed; } + for (i = 0; i < size/4; i++) + cso->index[i] = CPU_LE4(cso->index[i]); // all ok cso->fpos_in = ftell(f); @@ -202,6 +240,7 @@ zip_failed: file->param = cso; file->size = cso->header.total_bytes; file->type = PMT_CSO; + strncpy(file->ext, ext, sizeof(file->ext) - 1); return file; cso_failed: @@ -209,6 +248,50 @@ cso_failed: if (f != NULL) fclose(f); return NULL; } +#if defined(USE_LIBCHDR) + else if (strcasecmp(ext, "chd") == 0) + { + struct chd_struct *chd = NULL; + chd_file *cf = NULL; + const chd_header *head; + + if (chd_open(path, CHD_OPEN_READ, NULL, &cf) != CHDERR_NONE) + goto chd_failed; + + // sanity check + head = chd_get_header(cf); + if ((head->hunkbytes == 0) || (head->hunkbytes % CD_FRAME_SIZE)) + goto chd_failed; + + chd = calloc(1, sizeof(*chd)); + if (chd == NULL) + goto chd_failed; + chd->hunk = (u8 *)malloc(head->hunkbytes); + if (!chd->hunk) + goto chd_failed; + + chd->chd = cf; + chd->unitbytes = head->unitbytes; + chd->hunkunits = head->hunkbytes / head->unitbytes; + chd->sectorsize = CD_MAX_SECTOR_DATA; // default to RAW mode + + chd->fpos = 0; + chd->hunknum = -1; + + chd->file.file = chd; + chd->file.type = PMT_CHD; + // subchannel data is skipped, remove it from total size + chd->file.size = head->logicalbytes / CD_FRAME_SIZE * CD_MAX_SECTOR_DATA; + strncpy(chd->file.ext, ext, sizeof(chd->file.ext) - 1); + return &chd->file; + +chd_failed: + /* invalid CHD file */ + if (chd != NULL) free(chd); + if (cf != NULL) chd_close(cf); + return NULL; + } +#endif /* not a zip, treat as uncompressed file */ f = fopen(path, "rb"); @@ -227,7 +310,7 @@ cso_failed: strncpy(file->ext, ext, sizeof(file->ext) - 1); fseek(f, 0, SEEK_SET); -#ifndef __EPOC32__ // makes things worse on Symbian +#ifdef __GP2X__ if (file->size > 0x400000) /* we use our own buffering */ setvbuf(f, NULL, _IONBF, 0); @@ -236,23 +319,127 @@ cso_failed: return file; } +void pm_sectorsize(int length, pm_file *stream) +{ + // CHD reading needs to know how much binary data is in one data sector(=unit) +#if defined(USE_LIBCHDR) + if (stream->type == PMT_CHD) { + struct chd_struct *chd = stream->file; + chd->sectorsize = length; + if (chd->sectorsize > chd->unitbytes) + elprintf(EL_STATUS|EL_ANOMALY, "cd: sector size %d too large for unit %d", chd->sectorsize, chd->unitbytes); + } +#endif +} + +#if defined(USE_LIBCHDR) +static size_t _pm_read_chd(void *ptr, size_t bytes, pm_file *stream, int is_audio) +{ + int ret = 0; + + if (stream->type == PMT_CHD) { + struct chd_struct *chd = stream->file; + // calculate sector and offset in sector + int sectsz = is_audio ? CD_MAX_SECTOR_DATA : chd->sectorsize; + int sector = chd->fpos / sectsz; + int offset = chd->fpos - (sector * sectsz); + // calculate hunk and sector offset in hunk + int hunknum = sector / chd->hunkunits; + int hunksec = sector - (hunknum * chd->hunkunits); + int hunkofs = hunksec * chd->unitbytes; + + while (bytes != 0) { + // data left in current sector + int len = sectsz - offset; + + // update hunk cache if needed + if (hunknum != chd->hunknum) { + chd_read(chd->chd, hunknum, chd->hunk); + chd->hunknum = hunknum; + } + if (len > bytes) + len = bytes; + +#if CPU_IS_LE + if (is_audio) { + // convert big endian audio samples + u16 *dst = ptr, v; + u8 *src = chd->hunk + hunkofs + offset; + int i; + + for (i = 0; i < len; i += 4) { + v = *src++ << 8; *dst++ = v | *src++; + v = *src++ << 8; *dst++ = v | *src++; + } + } else +#endif + memcpy(ptr, chd->hunk + hunkofs + offset, len); + + // house keeping + ret += len; + chd->fpos += len; + bytes -= len; + + // no need to advance internals if there's no more data to read + if (bytes) { + ptr += len; + offset = 0; + + sector ++; + hunksec ++; + hunkofs += chd->unitbytes; + if (hunksec >= chd->hunkunits) { + hunksec = 0; + hunkofs = 0; + hunknum ++; + } + } + } + } + + return ret; +} +#endif + size_t pm_read(void *ptr, size_t bytes, pm_file *stream) { int ret; - if (stream->type == PMT_UNCOMPRESSED) + if (stream == NULL) + return -1; + else if (stream->type == PMT_UNCOMPRESSED) { ret = fread(ptr, 1, bytes, stream->file); } else if (stream->type == PMT_ZIP) { - gzFile gf = stream->param; - int err; - ret = gzread(gf, ptr, bytes); - err = gzerror2(gf); - if (ret > 0 && (err == Z_DATA_ERROR || err == Z_STREAM_END)) - /* we must reset stream pointer or else next seek/read fails */ - gzrewind(gf); + struct zip_file *z = stream->file; + + if (z->entry->compression_method == 0) { + int ret = fread(ptr, 1, bytes, z->zip->fp); + z->pos += ret; + return ret; + } + + z->stream.next_out = ptr; + z->stream.avail_out = bytes; + while (z->stream.avail_out != 0) { + if (z->stream.avail_in == 0) { + z->stream.avail_in = fread(z->inbuf, 1, sizeof(z->inbuf), z->zip->fp); + if (z->stream.avail_in == 0) + break; + z->stream.next_in = z->inbuf; + } + ret = inflate(&z->stream, Z_NO_FLUSH); + if (ret == Z_STREAM_END) + break; + if (ret != Z_OK) { + elprintf(EL_STATUS, "zip: inflate: %d", ret); + return 0; + } + } + z->pos += bytes - z->stream.avail_out; + return bytes - z->stream.avail_out; } else if (stream->type == PMT_CSO) { @@ -293,7 +480,7 @@ size_t pm_read(void *ptr, size_t bytes, pm_file *stream) } cso->block_in_buff = block; } - rret = uncompress2(tmp_dst, 2048, cso->in_buff, read_len); + rret = uncompress_buf(tmp_dst, 2048, cso->in_buff, read_len); if (rret != 0) { elprintf(EL_STATUS, "cso: uncompress failed @ %08x with %i", read_pos, rret); break; @@ -316,27 +503,98 @@ size_t pm_read(void *ptr, size_t bytes, pm_file *stream) index_end = cso->index[block+1]; } } +#if defined(USE_LIBCHDR) + else if (stream->type == PMT_CHD) + { + ret = _pm_read_chd(ptr, bytes, stream, 0); + } +#endif else ret = 0; return ret; } +size_t pm_read_audio(void *ptr, size_t bytes, pm_file *stream) +{ + if (stream == NULL) + return -1; +#if !(CPU_IS_LE) + else if (stream->type == PMT_UNCOMPRESSED) + { + // convert little endian audio samples from WAV file + int ret = pm_read(ptr, bytes, stream); + u16 *dst = ptr, v; + u8 *src = ptr; + int i; + + for (i = 0; i < ret; i += 4) { + v = *src++; *dst++ = v | (*src++ << 8); + v = *src++; *dst++ = v | (*src++ << 8); + } + return ret; + } + else +#endif +#if defined(USE_LIBCHDR) + if (stream->type == PMT_CHD) + { + return _pm_read_chd(ptr, bytes, stream, 1); + } +#endif + return pm_read(ptr, bytes, stream); +} + int pm_seek(pm_file *stream, long offset, int whence) { - if (stream->type == PMT_UNCOMPRESSED) + if (stream == NULL) + return -1; + else if (stream->type == PMT_UNCOMPRESSED) { fseek(stream->file, offset, whence); return ftell(stream->file); } else if (stream->type == PMT_ZIP) { - if (PicoMessage != NULL && offset > 6*1024*1024) { - long pos = gztell((gzFile) stream->param); - if (offset < pos || offset - pos > 6*1024*1024) - PicoMessage("Decompressing data..."); + struct zip_file *z = stream->file; + unsigned int pos = z->pos; + int ret; + + switch (whence) + { + case SEEK_CUR: pos += offset; break; + case SEEK_SET: pos = offset; break; + case SEEK_END: pos = stream->size - offset; break; + } + if (z->entry->compression_method == 0) { + ret = fseek(z->zip->fp, z->start + pos, SEEK_SET); + if (ret == 0) + return (z->pos = pos); + return -1; + } + offset = pos - z->pos; + if (pos < z->pos) { + // full decompress from the start + fseek(z->zip->fp, z->start, SEEK_SET); + z->stream.avail_in = 0; + z->stream.next_in = z->inbuf; + inflateReset(&z->stream); + z->pos = 0; + offset = pos; + } + + if (PicoIn.osdMessage != NULL && offset > 4 * 1024 * 1024) + PicoIn.osdMessage("Decompressing data..."); + + while (offset > 0) { + char buf[16 * 1024]; + size_t l = offset > sizeof(buf) ? sizeof(buf) : offset; + ret = pm_read(buf, l, stream); + if (ret != l) + break; + offset -= l; } - return gzseek((gzFile) stream->param, offset, whence); + return z->pos; } else if (stream->type == PMT_CSO) { @@ -349,6 +607,19 @@ int pm_seek(pm_file *stream, long offset, int whence) } return cso->fpos_out; } +#if defined(USE_LIBCHDR) + else if (stream->type == PMT_CHD) + { + struct chd_struct *chd = stream->file; + switch (whence) + { + case SEEK_CUR: chd->fpos += offset; break; + case SEEK_SET: chd->fpos = offset; break; + case SEEK_END: chd->fpos = stream->size - offset; break; + } + return chd->fpos; + } +#endif else return -1; } @@ -365,16 +636,24 @@ int pm_close(pm_file *fp) } else if (fp->type == PMT_ZIP) { - ZIP *zipfile = fp->file; - gzclose((gzFile) fp->param); - zipfile->fp = NULL; // gzclose() closed it - closezip(zipfile); + struct zip_file *z = fp->file; + inflateEnd(&z->stream); + closezip(z->zip); } else if (fp->type == PMT_CSO) { free(fp->param); fclose(fp->file); } +#if defined(USE_LIBCHDR) + else if (fp->type == PMT_CHD) + { + struct chd_struct *chd = fp->file; + chd_close(chd->chd); + if (chd->hunk) + free(chd->hunk); + } +#endif else ret = EOF; @@ -385,6 +664,7 @@ int pm_close(pm_file *fp) // byteswap, data needs to be int aligned, src can match dst void Byteswap(void *dst, const void *src, int len) { +#if CPU_IS_LE const unsigned int *ps = src; unsigned int *pd = dst; int i, m; @@ -397,14 +677,15 @@ void Byteswap(void *dst, const void *src, int len) unsigned int t = ps[i]; pd[i] = ((t & m) << 8) | ((t & ~m) >> 8); } +#endif } // Interleve a 16k block and byteswap static int InterleveBlock(unsigned char *dest,unsigned char *src) { int i=0; - for (i=0;i<0x2000;i++) dest[(i<<1) ]=src[ i]; // Odd - for (i=0;i<0x2000;i++) dest[(i<<1)+1]=src[0x2000+i]; // Even + for (i=0;i<0x2000;i++) dest[(i<<1)+MEM_BE2(1)]=src[ i]; // Odd + for (i=0;i<0x2000;i++) dest[(i<<1)+MEM_BE2(0)]=src[0x2000+i]; // Even return 0; } @@ -433,26 +714,22 @@ static unsigned char *PicoCartAlloc(int filesize, int is_sms) { unsigned char *rom; + // make size power of 2 for easier banking handling + int s = 0, tmp = filesize; + while ((tmp >>= 1) != 0) + s++; + if (filesize > (1 << s)) + s++; + rom_alloc_size = 1 << s; + if (is_sms) { - // make size power of 2 for easier banking handling - int s = 0, tmp = filesize; - while ((tmp >>= 1) != 0) - s++; - if (filesize > (1 << s)) - s++; - rom_alloc_size = 1 << s; // be sure we can cover all address space if (rom_alloc_size < 0x10000) rom_alloc_size = 0x10000; } else { - // make alloc size at least sizeof(mcd_state), - // in case we want to switch to CD mode - if (filesize < sizeof(mcd_state)) - filesize = sizeof(mcd_state); - // align to 512K for memhandlers - rom_alloc_size = (filesize + 0x7ffff) & ~0x7ffff; + rom_alloc_size = (rom_alloc_size + 0x7ffff) & ~0x7ffff; } if (rom_alloc_size - filesize < 4) @@ -464,65 +741,75 @@ static unsigned char *PicoCartAlloc(int filesize, int is_sms) return rom; } -int PicoCartLoad(pm_file *f,unsigned char **prom,unsigned int *psize,int is_sms) +int PicoCartLoad(pm_file *f, const unsigned char *rom, unsigned int romsize, + unsigned char **prom, unsigned int *psize, int is_sms) { - unsigned char *rom; + unsigned char *rom_data = NULL; int size, bytes_read; - if (f == NULL) + if (!f && !rom) return 1; - size = f->size; + if (!rom) + size = f->size; + else + size = romsize; + if (size <= 0) return 1; size = (size+3)&~3; // Round up to a multiple of 4 // Allocate space for the rom plus padding - rom = PicoCartAlloc(size, is_sms); - if (rom == NULL) { + rom_data = PicoCartAlloc(size, is_sms); + if (rom_data == NULL) { elprintf(EL_STATUS, "out of memory (wanted %i)", size); return 2; } - if (PicoCartLoadProgressCB != NULL) - { - // read ROM in blocks, just for fun - int ret; - unsigned char *p = rom; - bytes_read=0; - do + if (!rom) { + if (PicoCartLoadProgressCB != NULL) { - int todo = size - bytes_read; - if (todo > 256*1024) todo = 256*1024; - ret = pm_read(p,todo,f); - bytes_read += ret; - p += ret; - PicoCartLoadProgressCB(bytes_read * 100 / size); + // read ROM in blocks, just for fun + int ret; + unsigned char *p = rom_data; + bytes_read=0; + do + { + int todo = size - bytes_read; + if (todo > 256*1024) todo = 256*1024; + ret = pm_read(p,todo,f); + bytes_read += ret; + p += ret; + PicoCartLoadProgressCB(bytes_read * 100LL / size); + } + while (ret > 0); + } + else + bytes_read = pm_read(rom_data,size,f); // Load up the rom + + if (bytes_read <= 0) { + elprintf(EL_STATUS, "read failed"); + plat_munmap(rom_data, rom_alloc_size); + return 3; } - while (ret > 0); } else - bytes_read = pm_read(rom,size,f); // Load up the rom - if (bytes_read <= 0) { - elprintf(EL_STATUS, "read failed"); - free(rom); - return 3; - } + memcpy(rom_data, rom, romsize); if (!is_sms) { // maybe we are loading MegaCD BIOS? - if (!(PicoAHW & PAHW_MCD) && size == 0x20000 && (!strncmp((char *)rom+0x124, "BOOT", 4) || - !strncmp((char *)rom+0x128, "BOOT", 4))) { - PicoAHW |= PAHW_MCD; + if (!(PicoIn.AHW & PAHW_MCD) && size == 0x20000 && (!strncmp((char *)rom_data+0x124, "BOOT", 4) || + !strncmp((char *)rom_data+0x128, "BOOT", 4))) { + PicoIn.AHW |= PAHW_MCD; } // Check for SMD: if (size >= 0x4200 && (size&0x3fff) == 0x200 && - ((rom[0x2280] == 'S' && rom[0x280] == 'E') || (rom[0x280] == 'S' && rom[0x2281] == 'E'))) { + ((rom_data[0x2280] == 'S' && rom_data[0x280] == 'E') || (rom_data[0x280] == 'S' && rom_data[0x2281] == 'E'))) { elprintf(EL_STATUS, "SMD format detected."); - DecodeSmd(rom,size); size-=0x200; // Decode and byteswap SMD + DecodeSmd(rom_data,size); size-=0x200; // Decode and byteswap SMD } - else Byteswap(rom, rom, size); // Just byteswap + else Byteswap(rom_data, rom_data, size); // Just byteswap } else { @@ -530,11 +817,11 @@ int PicoCartLoad(pm_file *f,unsigned char **prom,unsigned int *psize,int is_sms) elprintf(EL_STATUS, "SMD format detected."); // at least here it's not interleaved size -= 0x200; - memmove(rom, rom + 0x200, size); + memmove(rom_data, rom_data + 0x200, size); } } - if (prom) *prom = rom; + if (prom) *prom = rom_data; if (psize) *psize = size; return 0; @@ -547,14 +834,14 @@ int PicoCartInsert(unsigned char *rom, unsigned int romsize, const char *carthw_ // This will hang the emu, but will prevent nasty crashes. // note: 4 bytes are padded to every ROM if (rom != NULL) - *(unsigned long *)(rom+romsize) = 0xFFFE4EFA; // 4EFA FFFE byteswapped + *(u32 *)(rom+romsize) = CPU_BE2(0x4EFAFFFE); Pico.rom=rom; Pico.romsize=romsize; - if (SRam.data) { - free(SRam.data); - SRam.data = NULL; + if (Pico.sv.data) { + free(Pico.sv.data); + Pico.sv.data = NULL; } if (PicoCartUnloadHook != NULL) { @@ -563,7 +850,7 @@ int PicoCartInsert(unsigned char *rom, unsigned int romsize, const char *carthw_ } pdb_cleanup(); - PicoAHW &= PAHW_MCD|PAHW_SMS; + PicoIn.AHW &= ~(PAHW_32X|PAHW_SVP); PicoCartMemSetup = NULL; PicoDmaHook = NULL; @@ -572,13 +859,19 @@ int PicoCartInsert(unsigned char *rom, unsigned int romsize, const char *carthw_ PicoLoadStateHook = NULL; carthw_chunks = NULL; - if (!(PicoAHW & (PAHW_MCD|PAHW_SMS))) + if (!(PicoIn.AHW & (PAHW_MCD|PAHW_SMS|PAHW_PICO))) PicoCartDetect(carthw_cfg); + if (PicoIn.AHW & PAHW_SMS) + PicoCartDetectMS(); + if (PicoIn.AHW & PAHW_SVP) + PicoSVPStartup(); + if (PicoIn.AHW & PAHW_PICO) + PicoInitPico(); // setup correct memory map for loaded ROM - switch (PicoAHW) { + switch (PicoIn.AHW & ~(PAHW_GG|PAHW_SG|PAHW_SC)) { default: - elprintf(EL_STATUS|EL_ANOMALY, "starting in unknown hw configuration: %x", PicoAHW); + elprintf(EL_STATUS|EL_ANOMALY, "starting in unknown hw configuration: %x", PicoIn.AHW); case 0: case PAHW_SVP: PicoMemSetup(); break; case PAHW_MCD: PicoMemSetupCD(); break; @@ -589,7 +882,7 @@ int PicoCartInsert(unsigned char *rom, unsigned int romsize, const char *carthw_ if (PicoCartMemSetup != NULL) PicoCartMemSetup(); - if (PicoAHW & PAHW_SMS) + if (PicoIn.AHW & PAHW_SMS) PicoPowerMS(); else PicoPower(); @@ -616,8 +909,7 @@ void PicoCartUnload(void) PicoCartUnloadHook = NULL; } - if (PicoAHW & PAHW_32X) - PicoUnload32x(); + PicoUnload32x(); if (Pico.rom != NULL) { SekFinishIdleDet(); @@ -627,15 +919,16 @@ void PicoCartUnload(void) PicoGameLoaded = 0; } -static unsigned int rom_crc32(void) +static unsigned int rom_crc32(int size) { unsigned int crc; elprintf(EL_STATUS, "caclulating CRC32.."); + if (size <= 0 || size > Pico.romsize) size = Pico.romsize; // have to unbyteswap for calculation.. - Byteswap(Pico.rom, Pico.rom, Pico.romsize); - crc = crc32(0, Pico.rom, Pico.romsize); - Byteswap(Pico.rom, Pico.rom, Pico.romsize); + Byteswap(Pico.rom, Pico.rom, size); + crc = crc32(0, Pico.rom, size); + Byteswap(Pico.rom, Pico.rom, size); return crc; } @@ -643,8 +936,10 @@ static int rom_strcmp(int rom_offset, const char *s1) { int i, len = strlen(s1); const char *s_rom = (const char *)Pico.rom; + if (rom_offset + len > Pico.romsize) + return 0; for (i = 0; i < len; i++) - if (s1[i] != s_rom[(i + rom_offset) ^ 1]) + if (s1[i] != s_rom[MEM_BE2(i + rom_offset)]) return 1; return 0; } @@ -709,21 +1004,46 @@ static int is_expr(const char *expr, char **pr) return 1; } -static void parse_carthw(const char *carthw_cfg, int *fill_sram) +#include "carthw_cfg.c" + +static void parse_carthw(const char *carthw_cfg, int *fill_sram, + int *hw_detected) { int line = 0, any_checks_passed = 0, skip_sect = 0; + const char *s, *builtin = builtin_carthw_cfg; int tmp, rom_crc = 0; char buff[256], *p, *r; FILE *f; f = fopen(carthw_cfg, "r"); - if (f == NULL) { + if (f == NULL) + f = fopen("pico/carthw.cfg", "r"); + if (f == NULL) elprintf(EL_STATUS, "couldn't open carthw.cfg!"); - return; - } - while ((p = fgets(buff, sizeof(buff), f))) + for (;;) { + if (f != NULL) { + p = fgets(buff, sizeof(buff), f); + if (p == NULL) + break; + } + else { + if (*builtin == 0) + break; + for (s = builtin; *s != 0 && *s != '\n'; s++) + ; + while (*s == '\n') + s++; + tmp = s - builtin; + if (tmp > sizeof(buff) - 1) + tmp = sizeof(buff) - 1; + memcpy(buff, builtin, tmp); + buff[tmp] = 0; + p = buff; + builtin = s; + } + line++; p = sskip(p); if (*p == 0 || *p == '#') @@ -799,7 +1119,7 @@ static void parse_carthw(const char *carthw_cfg, int *fill_sram) goto bad; if (rom_crc == 0) - rom_crc = rom_crc32(); + rom_crc = rom_crc32(64*1024); if (crc == rom_crc) any_checks_passed = 1; else @@ -811,12 +1131,13 @@ static void parse_carthw(const char *carthw_cfg, int *fill_sram) if (is_expr("hw", &p)) { if (!any_checks_passed) goto no_checks; + *hw_detected = 1; rstrip(p); if (strcmp(p, "svp") == 0) - PicoSVPStartup(); + PicoIn.AHW = PAHW_SVP; else if (strcmp(p, "pico") == 0) - PicoInitPico(); + PicoIn.AHW = PAHW_PICO; else if (strcmp(p, "prot") == 0) carthw_sprot_startup(); else if (strcmp(p, "ssf2_mapper") == 0) @@ -829,11 +1150,20 @@ static void parse_carthw(const char *carthw_cfg, int *fill_sram) carthw_radica_startup(); else if (strcmp(p, "piersolar_mapper") == 0) carthw_pier_startup(); - else if (strcmp(p, "prot_lk3") == 0) - carthw_prot_lk3_startup(); + else if (strcmp(p, "sf001_mapper") == 0) + carthw_sf001_startup(); + else if (strcmp(p, "sf002_mapper") == 0) + carthw_sf002_startup(); + else if (strcmp(p, "sf004_mapper") == 0) + carthw_sf004_startup(); + else if (strcmp(p, "lk3_mapper") == 0) + carthw_lk3_startup(); + else if (strcmp(p, "smw64_mapper") == 0) + carthw_smw64_startup(); else { elprintf(EL_STATUS, "carthw:%d: unsupported mapper: %s", line, p); skip_sect = 1; + *hw_detected = 0; } continue; } @@ -858,8 +1188,8 @@ static void parse_carthw(const char *carthw_cfg, int *fill_sram) elprintf(EL_STATUS, "carthw:%d: bad sram_range: %08x - %08x", line, start, end); goto bad_nomsg; } - SRam.start = start; - SRam.end = end; + Pico.sv.start = start; + Pico.sv.end = end; continue; } else if (is_expr("prop", &p)) { @@ -868,15 +1198,26 @@ static void parse_carthw(const char *carthw_cfg, int *fill_sram) rstrip(p); if (strcmp(p, "no_sram") == 0) - SRam.flags &= ~SRF_ENABLED; + Pico.sv.flags &= ~SRF_ENABLED; else if (strcmp(p, "no_eeprom") == 0) - SRam.flags &= ~SRF_EEPROM; + Pico.sv.flags &= ~SRF_EEPROM; else if (strcmp(p, "filled_sram") == 0) *fill_sram = 1; + else if (strcmp(p, "wwfraw_hack") == 0) + PicoIn.quirks |= PQUIRK_WWFRAW_HACK; + else if (strcmp(p, "blackthorne_hack") == 0) + PicoIn.quirks |= PQUIRK_BLACKTHORNE_HACK; + else if (strcmp(p, "marscheck_hack") == 0) + PicoIn.quirks |= PQUIRK_MARSCHECK_HACK; + else if (strcmp(p, "force_6btn") == 0) + PicoIn.quirks |= PQUIRK_FORCE_6BTN; + else if (strcmp(p, "no_z80_bus_lock") == 0) + PicoIn.quirks |= PQUIRK_NO_Z80_BUS_LOCK; else { elprintf(EL_STATUS, "carthw:%d: unsupported prop: %s", line, p); goto bad_nomsg; } + elprintf(EL_STATUS, "game prop: %s", p); continue; } else if (is_expr("eeprom_type", &p)) { @@ -888,8 +1229,8 @@ static void parse_carthw(const char *carthw_cfg, int *fill_sram) type = strtoul(p, &r, 0); if (r == p || type < 0) goto bad; - SRam.eeprom_type = type; - SRam.flags |= SRF_EEPROM; + Pico.sv.eeprom_type = type; + Pico.sv.flags |= SRF_EEPROM; continue; } else if (is_expr("eeprom_lines", &p)) { @@ -904,9 +1245,9 @@ static void parse_carthw(const char *carthw_cfg, int *fill_sram) sda_out < 0 || sda_out > 15) goto bad; - SRam.eeprom_bit_cl = scl; - SRam.eeprom_bit_in = sda_in; - SRam.eeprom_bit_out= sda_out; + Pico.sv.eeprom_bit_cl = scl; + Pico.sv.eeprom_bit_in = sda_in; + Pico.sv.eeprom_bit_out= sda_out; continue; } else if ((tmp = is_expr("prot_ro_value16", &p)) || is_expr("prot_rw_value16", &p)) { @@ -934,7 +1275,9 @@ no_checks: skip_sect = 1; continue; } - fclose(f); + + if (f != NULL) + fclose(f); } /* @@ -942,60 +1285,114 @@ no_checks: */ static void PicoCartDetect(const char *carthw_cfg) { + int carthw_detected = 0; int fill_sram = 0; - memset(&SRam, 0, sizeof(SRam)); - if (Pico.rom[0x1B1] == 'R' && Pico.rom[0x1B0] == 'A') + memset(&Pico.sv, 0, sizeof(Pico.sv)); + if (Pico.rom[MEM_BE2(0x1B0)] == 'R' && Pico.rom[MEM_BE2(0x1B1)] == 'A') { - SRam.start = rom_read32(0x1B4) & ~0xff000001; // align - SRam.end = (rom_read32(0x1B8) & ~0xff000000) | 1; - if (Pico.rom[0x1B2] & 0x40) + Pico.sv.start = rom_read32(0x1B4) & ~0xff000001; // align + Pico.sv.end = (rom_read32(0x1B8) & ~0xff000000) | 1; + if (Pico.rom[MEM_BE2(0x1B3)] & 0x40) // EEPROM - SRam.flags |= SRF_EEPROM; - SRam.flags |= SRF_ENABLED; + Pico.sv.flags |= SRF_EEPROM; + Pico.sv.flags |= SRF_ENABLED; } - if (SRam.end == 0 || SRam.start > SRam.end) + if (Pico.sv.end == 0 || Pico.sv.start > Pico.sv.end) { // some games may have bad headers, like S&K and Sonic3 // note: majority games use 0x200000 as starting address, but there are some which // use something else (0x300000 by HardBall '95). Luckily they have good headers. - SRam.start = 0x200000; - SRam.end = 0x203FFF; - SRam.flags |= SRF_ENABLED; + Pico.sv.start = 0x200000; + Pico.sv.end = 0x203FFF; + Pico.sv.flags |= SRF_ENABLED; } // set EEPROM defaults, in case it gets detected - SRam.eeprom_type = 0; // 7bit (24C01) - SRam.eeprom_bit_cl = 1; - SRam.eeprom_bit_in = 0; - SRam.eeprom_bit_out= 0; + Pico.sv.eeprom_type = 0; // 7bit (24C01) + Pico.sv.eeprom_bit_cl = 1; + Pico.sv.eeprom_bit_in = 0; + Pico.sv.eeprom_bit_out= 0; if (carthw_cfg != NULL) - parse_carthw(carthw_cfg, &fill_sram); + parse_carthw(carthw_cfg, &fill_sram, &carthw_detected); - if (SRam.flags & SRF_ENABLED) + // assume the standard mapper for large roms + if (!carthw_detected && Pico.romsize > 0x400000) + carthw_ssf2_startup(); + + if (Pico.sv.flags & SRF_ENABLED) { - if (SRam.flags & SRF_EEPROM) - SRam.size = 0x2000; + if (Pico.sv.flags & SRF_EEPROM) + Pico.sv.size = 0x2000; else - SRam.size = SRam.end - SRam.start + 1; + Pico.sv.size = Pico.sv.end - Pico.sv.start + 1; - SRam.data = calloc(SRam.size, 1); - if (SRam.data == NULL) - SRam.flags &= ~SRF_ENABLED; + Pico.sv.data = calloc(Pico.sv.size, 1); + if (Pico.sv.data == NULL) + Pico.sv.flags &= ~SRF_ENABLED; - if (SRam.eeprom_type == 1) // 1 == 0 in PD EEPROM code - SRam.eeprom_type = 0; + if (Pico.sv.eeprom_type == 1) // 1 == 0 in PD EEPROM code + Pico.sv.eeprom_type = 0; } - if ((SRam.flags & SRF_ENABLED) && fill_sram) + if ((Pico.sv.flags & SRF_ENABLED) && fill_sram) { elprintf(EL_STATUS, "SRAM fill"); - memset(SRam.data, 0xff, SRam.size); + memset(Pico.sv.data, 0xff, Pico.sv.size); } - // Unusual region 'code' - if (rom_strcmp(0x1f0, "EUROPE") == 0 || rom_strcmp(0x1f0, "Europe") == 0) - *(int *) (Pico.rom + 0x1f0) = 0x20204520; + // tweak for Blackthorne: master SH2 overwrites stack of slave SH2 being in PWM + // interrupt. On real hardware, nothing happens since slave fetches the values + // it has written from its cache, but picodrive doesn't emulate caching. + // move master memory area down by 0x100 bytes. + // XXX replace this abominable hack. It might cause other problems in the game! + if (PicoIn.quirks & PQUIRK_BLACKTHORNE_HACK) { + int i; + unsigned a = 0; + for (i = 0; i < Pico.romsize; i += 4) { + unsigned v = CPU_BE2(*(u32 *) (Pico.rom + i)); + if (a && v == a + 0x400) { // patch if 2 pointers with offset 0x400 are found + elprintf(EL_STATUS, "auto-patching @%06x: %08x->%08x\n", i, v, v - 0x100); + *(u32 *) (Pico.rom + i) = CPU_BE2(v - 0x100); + } + // detect a pointer into the incriminating area + a = 0; + if (v >> 12 == 0x0603f000 >> 12 && !(v & 3)) + a = v; + } + } + + // tweak for Mars Check Program: copies 32K longwords (128KB) from a 64KB buffer + // in ROM or DRAM to SDRAM with DMA in 4-longword mode, overwriting an SDRAM comm + // area in turn. This crashes the test on emulators without CPU cache emulation. + // This may be a bug in Mars Check, since it's only checking for the 64KB result. + // Patch the DMA transfers so that they transfer only 64KB. + if (PicoIn.quirks & PQUIRK_MARSCHECK_HACK) { + int i; + unsigned a = 0; + for (i = 0; i < Pico.romsize; i += 4) { + unsigned v = CPU_BE2(*(u32 *) (Pico.rom + i)); + if (a == 0xffffff8c && v == 0x5ee1) { // patch if 4-long xfer written to CHCR + elprintf(EL_STATUS, "auto-patching @%06x: %08x->%08x\n", i, v, v & ~0x800); + *(u32 *) (Pico.rom + i) = CPU_BE2(v & ~0x800); // change to half-sized xfer + } + a = v; + } + } } +static void PicoCartDetectMS(void) +{ + memset(&Pico.sv, 0, sizeof(Pico.sv)); + + // Always map SRAM, since there's no indicator in ROM if it's needed or not + // TODO: this should somehow be coming from a cart database! + + Pico.sv.size = 0x8000; // Sega mapper, 2 banks of 16 KB each + Pico.sv.flags |= SRF_ENABLED; + Pico.sv.data = calloc(Pico.sv.size, 1); + if (Pico.sv.data == NULL) + Pico.sv.flags &= ~SRF_ENABLED; +} +// vim:shiftwidth=2:expandtab