X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?p=pcsx_rearmed.git;a=blobdiff_plain;f=frontend%2Flibretro.c;h=f212cdd8d0b519d733d35da4bf347dc25ac761f3;hp=4305aa72df66bd9cc39a7766292406a34a1536ca;hb=7d68d030b6a2c294ab6f18892f61e1861ecdd40f;hpb=9f766dbe7543819bdf44bb326665998af45c102d diff --git a/frontend/libretro.c b/frontend/libretro.c index 4305aa72..f212cdd8 100644 --- a/frontend/libretro.c +++ b/frontend/libretro.c @@ -11,8 +11,13 @@ #include "../libpcsxcore/misc.h" #include "../libpcsxcore/psxcounters.h" +#include "../libpcsxcore/psxmem_map.h" #include "../libpcsxcore/new_dynarec/new_dynarec.h" +#include "../libpcsxcore/cdrom.h" +#include "../libpcsxcore/cdriso.h" +#include "../libpcsxcore/cheat.h" #include "../plugins/dfsound/out.h" +#include "cspace.h" #include "main.h" #include "plugin.h" #include "plugin_lib.h" @@ -27,9 +32,18 @@ static retro_audio_sample_batch_t audio_batch_cb; static void *vout_buf; static int vout_width, vout_height; +static int vout_doffs_old, vout_fb_dirty; +static bool vout_can_dupe; + static int samples_sent, samples_to_send; static int plugins_opened; -static int native_rgb565; +static int is_pal_mode; + +extern int soft_filter; + +/* memory card data */ +extern char Mcd1Data[MCD_SIZE]; +extern char McdDisable[2]; /* PCSX ReARMed core calls and stuff */ int in_type1, in_type2; @@ -37,19 +51,51 @@ int in_a1[2] = { 127, 127 }, in_a2[2] = { 127, 127 }; int in_keystate; int in_enable_vibration; +static void init_memcard(char *mcd_data) +{ + unsigned off = 0; + unsigned i; + + memset(mcd_data, 0, MCD_SIZE); + + mcd_data[off++] = 'M'; + mcd_data[off++] = 'C'; + off += 0x7d; + mcd_data[off++] = 0x0e; + + for (i = 0; i < 15; i++) { + mcd_data[off++] = 0xa0; + off += 0x07; + mcd_data[off++] = 0xff; + mcd_data[off++] = 0xff; + off += 0x75; + mcd_data[off++] = 0xa0; + } + + for (i = 0; i < 20; i++) { + mcd_data[off++] = 0xff; + mcd_data[off++] = 0xff; + mcd_data[off++] = 0xff; + mcd_data[off++] = 0xff; + off += 0x04; + mcd_data[off++] = 0xff; + mcd_data[off++] = 0xff; + off += 0x76; + } +} + static int vout_open(void) { return 0; } -static void *vout_set_mode(int w, int h, int bpp) +static void vout_set_mode(int w, int h, int raw_w, int raw_h, int bpp) { vout_width = w; vout_height = h; - return vout_buf; } -/* FIXME: either teach PCSX to blit to RGB1555 or RetroArch to support RGB565 */ +#ifndef FRONTEND_SUPPORTS_RGB565 static void convert(void *buf, size_t bytes) { unsigned int i, v, *p = buf; @@ -59,26 +105,75 @@ static void convert(void *buf, size_t bytes) p[i] = (v & 0x001f001f) | ((v >> 1) & 0x7fe07fe0); } } +#endif -static void *vout_flip(void) +static void vout_flip(const void *vram, int stride, int bgr24, int w, int h) { - pl_rearmed_cbs.flip_cnt++; - if (!native_rgb565) - convert(vout_buf, vout_width * vout_height * 2); - video_cb(vout_buf, vout_width, vout_height, vout_width * 2); + unsigned short *dest = vout_buf; + const unsigned short *src = vram; + int dstride = vout_width, h1 = h; + int doffs; + + if (vram == NULL) { + // blanking + memset(vout_buf, 0, dstride * h * 2); + goto out; + } + + doffs = (vout_height - h) * dstride; + doffs += (dstride - w) / 2 & ~1; + if (doffs != vout_doffs_old) { + // clear borders + memset(vout_buf, 0, dstride * h * 2); + vout_doffs_old = doffs; + } + dest += doffs; + + if (bgr24) + { + // XXX: could we switch to RETRO_PIXEL_FORMAT_XRGB8888 here? + for (; h1-- > 0; dest += dstride, src += stride) + { + bgr888_to_rgb565(dest, src, w * 3); + } + } + else + { + for (; h1-- > 0; dest += dstride, src += stride) + { + bgr555_to_rgb565(dest, src, w * 2); + } + } - return vout_buf; +out: +#ifndef FRONTEND_SUPPORTS_RGB565 + convert(vout_buf, vout_width * vout_height * 2); +#endif + vout_fb_dirty = 1; + pl_rearmed_cbs.flip_cnt++; } static void vout_close(void) { } +static void *pl_mmap(unsigned int size) +{ + return psxMap(0, size, 0, MAP_TAG_VRAM); +} + +static void pl_munmap(void *ptr, unsigned int size) +{ + psxUnmap(ptr, size, MAP_TAG_VRAM); +} + struct rearmed_cbs pl_rearmed_cbs = { .pl_vout_open = vout_open, .pl_vout_set_mode = vout_set_mode, .pl_vout_flip = vout_flip, .pl_vout_close = vout_close, + .mmap = pl_mmap, + .munmap = pl_munmap, /* from psxcounters */ .gpu_hcnt = &hSyncCount, .gpu_frame_count = &frame_counter, @@ -92,13 +187,14 @@ void pl_frame_limit(void) void pl_timing_prepare(int is_pal) { + is_pal_mode = is_pal; } void plat_trigger_vibrate(int is_strong) { } -void pl_update_gun(int *xn, int *xres, int *y, int *in) +void pl_update_gun(int *xn, int *yn, int *xres, int *yres, int *in) { } @@ -136,7 +232,22 @@ void out_register_libretro(struct out_driver *drv) } /* libretro */ -void retro_set_environment(retro_environment_t cb) { environ_cb = cb; } +void retro_set_environment(retro_environment_t cb) +{ + static const struct retro_variable vars[] = { + { "frameskip", "Frameskip; 0|1|2|3" }, +#ifdef __ARM_NEON__ + { "soft_filter", "Software filter; none|scale2x|eagle2x" }, + { "neon_enhancement_enable", "Enhanced resolution (slow); disabled|enabled" }, +#endif + { NULL, NULL }, + }; + + environ_cb = cb; + + cb(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars); +} + void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; } void retro_set_audio_sample(retro_audio_sample_t cb) { (void)cb; } void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; } @@ -156,15 +267,15 @@ void retro_get_system_info(struct retro_system_info *info) { memset(info, 0, sizeof(*info)); info->library_name = "PCSX-ReARMed"; - info->library_version = REV; - info->valid_extensions = "bin|cue|img|mdf|pbp|cbn"; + info->library_version = "r19"; + info->valid_extensions = "bin|cue|img|mdf|pbp|toc|cbn|m3u"; info->need_fullpath = true; } void retro_get_system_av_info(struct retro_system_av_info *info) { memset(info, 0, sizeof(*info)); - info->timing.fps = 60; + info->timing.fps = is_pal_mode ? 50 : 60; info->timing.sample_rate = 44100; info->geometry.base_width = 320; info->geometry.base_height = 240; @@ -173,36 +284,322 @@ void retro_get_system_av_info(struct retro_system_av_info *info) info->geometry.aspect_ratio = 4.0 / 3.0; } -/* TODO */ +/* savestates */ size_t retro_serialize_size(void) { - return 0; + // it's currently 4380651 bytes, but have some reserved for future + return 0x430000; +} + +struct save_fp { + char *buf; + size_t pos; + int is_write; +}; + +static void *save_open(const char *name, const char *mode) +{ + struct save_fp *fp; + + if (name == NULL || mode == NULL) + return NULL; + + fp = malloc(sizeof(*fp)); + if (fp == NULL) + return NULL; + + fp->buf = (char *)name; + fp->pos = 0; + fp->is_write = (mode[0] == 'w' || mode[1] == 'w'); + + return fp; +} + +static int save_read(void *file, void *buf, u32 len) +{ + struct save_fp *fp = file; + if (fp == NULL || buf == NULL) + return -1; + + memcpy(buf, fp->buf + fp->pos, len); + fp->pos += len; + return len; +} + +static int save_write(void *file, const void *buf, u32 len) +{ + struct save_fp *fp = file; + if (fp == NULL || buf == NULL) + return -1; + + memcpy(fp->buf + fp->pos, buf, len); + fp->pos += len; + return len; +} + +static long save_seek(void *file, long offs, int whence) +{ + struct save_fp *fp = file; + if (fp == NULL) + return -1; + + switch (whence) { + case SEEK_CUR: + fp->pos += offs; + return fp->pos; + case SEEK_SET: + fp->pos = offs; + return fp->pos; + default: + return -1; + } +} + +static void save_close(void *file) +{ + struct save_fp *fp = file; + size_t r_size = retro_serialize_size(); + if (fp == NULL) + return; + + if (fp->pos > r_size) + SysPrintf("ERROR: save buffer overflow detected\n"); + else if (fp->is_write && fp->pos < r_size) + // make sure we don't save trash in leftover space + memset(fp->buf + fp->pos, 0, r_size - fp->pos); + free(fp); } bool retro_serialize(void *data, size_t size) { - return false; + int ret = SaveState(data); + return ret == 0 ? true : false; } bool retro_unserialize(const void *data, size_t size) { - return false; + int ret = LoadState(data); + return ret == 0 ? true : false; } +/* cheats */ void retro_cheat_reset(void) { + ClearAllCheats(); } void retro_cheat_set(unsigned index, bool enabled, const char *code) { + char buf[256]; + int ret; + + // cheat funcs are destructive, need a copy.. + strncpy(buf, code, sizeof(buf)); + buf[sizeof(buf) - 1] = 0; + + if (index < NumCheats) + ret = EditCheat(index, "", buf); + else + ret = AddCheat("", buf); + + if (ret != 0) + SysPrintf("Failed to set cheat %#u\n", index); + else if (index < NumCheats) + Cheats[index].Enabled = enabled; +} + +/* multidisk support */ +static bool disk_ejected; +static unsigned int disk_current_index; +static unsigned int disk_count; +static struct disks_state { + char *fname; + int internal_index; // for multidisk eboots +} disks[8]; + +static bool disk_set_eject_state(bool ejected) +{ + // weird PCSX API.. + SetCdOpenCaseTime(ejected ? -1 : (time(NULL) + 2)); + LidInterrupt(); + + disk_ejected = ejected; + return true; +} + +static bool disk_get_eject_state(void) +{ + /* can't be controlled by emulated software */ + return disk_ejected; +} + +static unsigned int disk_get_image_index(void) +{ + return disk_current_index; +} + +static bool disk_set_image_index(unsigned int index) +{ + if (index >= sizeof(disks) / sizeof(disks[0])) + return false; + + CdromId[0] = '\0'; + CdromLabel[0] = '\0'; + + if (disks[index].fname == NULL) { + SysPrintf("missing disk #%u\n", index); + CDR_shutdown(); + + // RetroArch specifies "no disk" with index == count, + // so don't fail here.. + disk_current_index = index; + return true; + } + + SysPrintf("switching to disk %u: \"%s\" #%d\n", index, + disks[index].fname, disks[index].internal_index); + + cdrIsoMultidiskSelect = disks[index].internal_index; + set_cd_image(disks[index].fname); + if (ReloadCdromPlugin() < 0) { + SysPrintf("failed to load cdr plugin\n"); + return false; + } + if (CDR_open() < 0) { + SysPrintf("failed to open cdr plugin\n"); + return false; + } + + if (!disk_ejected) { + SetCdOpenCaseTime(time(NULL) + 2); + LidInterrupt(); + } + + disk_current_index = index; + return true; +} + +static unsigned int disk_get_num_images(void) +{ + return disk_count; +} + +static bool disk_replace_image_index(unsigned index, + const struct retro_game_info *info) +{ + char *old_fname; + bool ret = true; + + if (index >= sizeof(disks) / sizeof(disks[0])) + return false; + + old_fname = disks[index].fname; + disks[index].fname = NULL; + disks[index].internal_index = 0; + + if (info != NULL) { + disks[index].fname = strdup(info->path); + if (index == disk_current_index) + ret = disk_set_image_index(index); + } + + if (old_fname != NULL) + free(old_fname); + + return ret; +} + +static bool disk_add_image_index(void) +{ + if (disk_count >= 8) + return false; + + disk_count++; + return true; +} + +static struct retro_disk_control_callback disk_control = { + .set_eject_state = disk_set_eject_state, + .get_eject_state = disk_get_eject_state, + .get_image_index = disk_get_image_index, + .set_image_index = disk_set_image_index, + .get_num_images = disk_get_num_images, + .replace_image_index = disk_replace_image_index, + .add_image_index = disk_add_image_index, +}; + +// just in case, maybe a win-rt port in the future? +#ifdef _WIN32 +#define SLASH '\\' +#else +#define SLASH '/' +#endif + +static char base_dir[PATH_MAX]; + +static bool read_m3u(const char *file) +{ + char line[PATH_MAX]; + char name[PATH_MAX]; + FILE *f = fopen(file, "r"); + if (!f) + return false; + + while (fgets(line, sizeof(line), f) && disk_count < sizeof(disks) / sizeof(disks[0])) { + if (line[0] == '#') + continue; + char *carrige_return = strchr(line, '\r'); + if (carrige_return) + *carrige_return = '\0'; + char *newline = strchr(line, '\n'); + if (newline) + *newline = '\0'; + + if (line[0] != '\0') + { + snprintf(name, sizeof(name), "%s%c%s", base_dir, SLASH, line); + disks[disk_count++].fname = strdup(name); + } + } + + fclose(f); + return (disk_count != 0); +} + +static void extract_directory(char *buf, const char *path, size_t size) +{ + char *base; + strncpy(buf, path, size - 1); + buf[size - 1] = '\0'; + + base = strrchr(buf, '/'); + if (!base) + base = strrchr(buf, '\\'); + + if (base) + *base = '\0'; + else + { + buf[0] = '.'; + buf[1] = '\0'; + } } bool retro_load_game(const struct retro_game_info *info) { + size_t i; + bool is_m3u = (strcasestr(info->path, ".m3u") != NULL); + +#ifdef FRONTEND_SUPPORTS_RGB565 enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565; if (environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) { - native_rgb565 = 1; - fprintf(stderr, "RGB565 supported, using it\n"); + SysPrintf("RGB565 supported, using it\n"); + } +#endif + + if (info == NULL || info->path == NULL) { + SysPrintf("info->path required\n"); + return false; } if (plugins_opened) { @@ -210,11 +607,32 @@ bool retro_load_game(const struct retro_game_info *info) plugins_opened = 0; } - set_cd_image(info->path); + for (i = 0; i < sizeof(disks) / sizeof(disks[0]); i++) { + if (disks[i].fname != NULL) { + free(disks[i].fname); + disks[i].fname = NULL; + } + disks[i].internal_index = 0; + } + + disk_current_index = 0; + extract_directory(base_dir, info->path, sizeof(base_dir)); + + if (is_m3u) { + if (!read_m3u(info->path)) { + SysPrintf("failed to read m3u file\n"); + return false; + } + } else { + disk_count = 1; + disks[0].fname = strdup(info->path); + } + + set_cd_image(disks[0].fname); /* have to reload after set_cd_image for correct cdr plugin */ if (LoadPlugins() == -1) { - printf("faled to load plugins\n"); + SysPrintf("failed to load plugins\n"); return false; } @@ -222,7 +640,7 @@ bool retro_load_game(const struct retro_game_info *info) NetOpened = 0; if (OpenPlugins() == -1) { - printf("faled to open plugins\n"); + SysPrintf("failed to open plugins\n"); return false; } @@ -230,17 +648,26 @@ bool retro_load_game(const struct retro_game_info *info) Config.PsxAuto = 1; if (CheckCdrom() == -1) { - printf("unsupported/invalid CD image: %s\n", info->path); + SysPrintf("unsupported/invalid CD image: %s\n", info->path); return false; } SysReset(); if (LoadCdrom() == -1) { - printf("could not load CD-ROM!\n"); + SysPrintf("could not load CD-ROM!\n"); return false; } - emu_on_new_cd(); + emu_on_new_cd(0); + + // multidisk images + if (!is_m3u) { + disk_count = cdrIsoMultidiskCount < 8 ? cdrIsoMultidiskCount : 8; + for (i = 1; i < sizeof(disks) / sizeof(disks[0]) && i < cdrIsoMultidiskCount; i++) { + disks[i].fname = strdup(info->path); + disks[i].internal_index = i; + } + } return true; } @@ -256,17 +683,23 @@ void retro_unload_game(void) unsigned retro_get_region(void) { - return RETRO_REGION_NTSC; + return is_pal_mode ? RETRO_REGION_PAL : RETRO_REGION_NTSC; } void *retro_get_memory_data(unsigned id) { - return NULL; + if (id == RETRO_MEMORY_SAVE_RAM) + return Mcd1Data; + else + return NULL; } size_t retro_get_memory_size(unsigned id) { - return 0; + if (id == RETRO_MEMORY_SAVE_RAM) + return MCD_SIZE; + else + return 0; } void retro_reset(void) @@ -294,11 +727,52 @@ static const unsigned short retro_psx_map[] = { }; #define RETRO_PSX_MAP_LEN (sizeof(retro_psx_map) / sizeof(retro_psx_map[0])) +static void update_variables(void) +{ + struct retro_variable var; + + var.value = NULL; + var.key = "frameskip"; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value) + pl_rearmed_cbs.frameskip = atoi(var.value); +#ifdef __ARM_NEON__ + var.value = NULL; + var.key = "soft_filter"; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value) + { + if (strcmp(var.value, "none")) + soft_filter = 0; + else if (strcmp(var.value, "scale2x")) + soft_filter = 1; + else if (strcmp(var.value, "eagle2x")) + soft_filter = 2; + } + + var.value = NULL; + var.key = "neon_enhancement_enable"; + + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value) + { + if (strcmp(var.value, "disabled") == 0) + pl_rearmed_cbs.gpu_neon.enhancement_enable = 0; + else if (strcmp(var.value, "enabled") == 0) + pl_rearmed_cbs.gpu_neon.enhancement_enable = 1; + } +#endif +} + void retro_run(void) { int i; input_poll_cb(); + + bool updated = false; + if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) + update_variables(); + in_keystate = 0; for (i = 0; i < RETRO_PSX_MAP_LEN; i++) if (input_state_cb(1, RETRO_DEVICE_JOYPAD, 0, i)) @@ -311,7 +785,11 @@ void retro_run(void) stop = 0; psxCpu->Execute(); - samples_to_send += 44100 / 60; + samples_to_send += is_pal_mode ? 44100 / 50 : 44100 / 60; + + video_cb((vout_fb_dirty || !vout_can_dupe) ? vout_buf : NULL, + vout_width, vout_height, vout_width * 2); + vout_fb_dirty = 0; } void retro_init(void) @@ -325,7 +803,7 @@ void retro_init(void) ret = emu_core_preinit(); ret |= emu_core_init(); if (ret != 0) { - printf("PCSX init failed, sorry\n"); + SysPrintf("PCSX init failed.\n"); exit(1); } @@ -345,14 +823,38 @@ void retro_init(void) } } if (f != NULL) { - printf("found BIOS file: %s\n", Config.Bios); + SysPrintf("found BIOS file: %s\n", Config.Bios); fclose(f); } else - printf("no BIOS files found.\n"); + SysPrintf("no BIOS files found.\n"); level = 1; environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level); + + environ_cb(RETRO_ENVIRONMENT_GET_CAN_DUPE, &vout_can_dupe); + environ_cb(RETRO_ENVIRONMENT_SET_DISK_CONTROL_INTERFACE, &disk_control); + + /* Set how much slower PSX CPU runs * 100 (so that 200 is 2 times) + * we have to do this because cache misses and some IO penalties + * are not emulated. Warning: changing this may break compatibility. */ +#ifdef __ARM_ARCH_7A__ + cycle_multiplier = 175; +#else + cycle_multiplier = 200; +#endif + + McdDisable[0] = 0; + McdDisable[1] = 1; + init_memcard(Mcd1Data); + + SaveFuncs.open = save_open; + SaveFuncs.read = save_read; + SaveFuncs.write = save_write; + SaveFuncs.seek = save_seek; + SaveFuncs.close = save_close; + + update_variables(); } void retro_deinit(void) @@ -361,4 +863,3 @@ void retro_deinit(void) free(vout_buf); vout_buf = NULL; } -