|
| 1 | +/* |
| 2 | + * test_rcheevos_e2e.c — Load Virtual Jaguar, feed rcheevos rc_libretro with the same |
| 3 | + * SET_MEMORY_MAPS descriptor RetroArch receives, then verify address resolution matches |
| 4 | + * host RAM (offline; no RetroAchievements server). |
| 5 | + */ |
| 6 | + |
| 7 | +#include <stdio.h> |
| 8 | +#include <stdlib.h> |
| 9 | +#include <string.h> |
| 10 | +#include <stdint.h> |
| 11 | +#include <stdbool.h> |
| 12 | + |
| 13 | +#ifdef __APPLE__ |
| 14 | +#include <dlfcn.h> |
| 15 | +#else |
| 16 | +#include <dlfcn.h> |
| 17 | +#endif |
| 18 | + |
| 19 | +#include "../../libretro-common/include/libretro.h" |
| 20 | +#include "rc_libretro.h" |
| 21 | +#include "rc_consoles.h" |
| 22 | + |
| 23 | +typedef void (*retro_init_t)(void); |
| 24 | +typedef void (*retro_deinit_t)(void); |
| 25 | +typedef void (*retro_set_environment_t)(retro_environment_t); |
| 26 | +typedef void (*retro_set_video_refresh_t)(retro_video_refresh_t); |
| 27 | +typedef void (*retro_set_audio_sample_t)(retro_audio_sample_t); |
| 28 | +typedef void (*retro_set_audio_sample_batch_t)(retro_audio_sample_batch_t); |
| 29 | +typedef void (*retro_set_input_poll_t)(retro_input_poll_t); |
| 30 | +typedef void (*retro_set_input_state_t)(retro_input_state_t); |
| 31 | +typedef bool (*retro_load_game_t)(const struct retro_game_info *); |
| 32 | +typedef void (*retro_unload_game_t)(void); |
| 33 | +typedef void *(*retro_get_memory_data_t)(unsigned); |
| 34 | +typedef size_t (*retro_get_memory_size_t)(unsigned); |
| 35 | + |
| 36 | +static const struct retro_memory_map *g_mmap; |
| 37 | +static retro_get_memory_data_t g_get_memory_data; |
| 38 | +static retro_get_memory_size_t g_get_memory_size; |
| 39 | + |
| 40 | +static bool env_cb(unsigned cmd, void *data) |
| 41 | +{ |
| 42 | + switch (cmd) |
| 43 | + { |
| 44 | + case RETRO_ENVIRONMENT_SET_MEMORY_MAPS: |
| 45 | + g_mmap = (const struct retro_memory_map *)data; |
| 46 | + return true; |
| 47 | + case RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS: |
| 48 | + return true; |
| 49 | + case RETRO_ENVIRONMENT_SET_PIXEL_FORMAT: |
| 50 | + case RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME: |
| 51 | + return true; |
| 52 | + case RETRO_ENVIRONMENT_GET_VARIABLE: |
| 53 | + return false; |
| 54 | + case RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE: |
| 55 | + if (data) *(bool *)data = false; |
| 56 | + return true; |
| 57 | + case RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY: |
| 58 | + case RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY: |
| 59 | + if (data) *(const char **)data = "."; |
| 60 | + return true; |
| 61 | + case RETRO_ENVIRONMENT_GET_CORE_OPTIONS_VERSION: |
| 62 | + if (data) *(unsigned *)data = 0; |
| 63 | + return true; |
| 64 | + case RETRO_ENVIRONMENT_GET_INPUT_BITMASKS: |
| 65 | + return true; |
| 66 | + default: |
| 67 | + return false; |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +static void libretro_get_core_memory_info(uint32_t id, rc_libretro_core_memory_info_t *info) |
| 72 | +{ |
| 73 | + if (!g_get_memory_data || !g_get_memory_size) |
| 74 | + { |
| 75 | + info->data = NULL; |
| 76 | + info->size = 0; |
| 77 | + return; |
| 78 | + } |
| 79 | + info->data = (uint8_t *)g_get_memory_data(id); |
| 80 | + info->size = g_get_memory_size(id); |
| 81 | +} |
| 82 | + |
| 83 | +static void video_cb(const void *d, unsigned w, unsigned h, size_t p) |
| 84 | +{ (void)d; (void)w; (void)h; (void)p; } |
| 85 | +static void audio_cb(int16_t l, int16_t r) |
| 86 | +{ (void)l; (void)r; } |
| 87 | +static size_t audio_batch(const int16_t *d, size_t f) |
| 88 | +{ (void)d; return f; } |
| 89 | +static void input_poll(void) {} |
| 90 | +static int16_t input_state(unsigned p, unsigned d, unsigned i, unsigned id) |
| 91 | +{ (void)p; (void)d; (void)i; (void)id; return 0; } |
| 92 | + |
| 93 | +static void *load_sym(void *handle, const char *name) |
| 94 | +{ |
| 95 | + void *sym = dlsym(handle, name); |
| 96 | + if (!sym) |
| 97 | + { |
| 98 | + fprintf(stderr, "ERROR: Missing symbol: %s\n", name); |
| 99 | + exit(1); |
| 100 | + } |
| 101 | + return sym; |
| 102 | +} |
| 103 | + |
| 104 | +static uint8_t *make_dummy_rom(size_t *size_out) |
| 105 | +{ |
| 106 | + size_t sz = 8192; |
| 107 | + uint8_t *rom = calloc(1, sz); |
| 108 | + if (!rom) { perror("calloc"); exit(1); } |
| 109 | + rom[0x404] = 0x00; rom[0x405] = 0x80; |
| 110 | + rom[0x406] = 0x20; rom[0x407] = 0x00; |
| 111 | + rom[0x2000] = 0x60; rom[0x2001] = 0xFE; |
| 112 | + *size_out = sz; |
| 113 | + return rom; |
| 114 | +} |
| 115 | + |
| 116 | +int main(int argc, char **argv) |
| 117 | +{ |
| 118 | + void *handle; |
| 119 | + retro_init_t core_init; |
| 120 | + retro_deinit_t core_deinit; |
| 121 | + retro_set_environment_t core_set_env; |
| 122 | + retro_set_video_refresh_t core_set_video; |
| 123 | + retro_set_audio_sample_t core_set_audio; |
| 124 | + retro_set_audio_sample_batch_t core_set_audio_batch; |
| 125 | + retro_set_input_poll_t core_set_input_poll; |
| 126 | + retro_set_input_state_t core_set_input_state; |
| 127 | + retro_load_game_t core_load_game; |
| 128 | + retro_unload_game_t core_unload_game; |
| 129 | + retro_get_memory_data_t core_get_memory_data; |
| 130 | + retro_get_memory_size_t core_get_memory_size; |
| 131 | + struct retro_game_info info; |
| 132 | + uint8_t *rom; |
| 133 | + size_t rom_size; |
| 134 | + rc_libretro_memory_regions_t regions; |
| 135 | + uint8_t *sysram; |
| 136 | + uint8_t buf[4]; |
| 137 | + uint32_t avail; |
| 138 | + uint8_t *pfind; |
| 139 | + int failures = 0; |
| 140 | + |
| 141 | + if (argc < 2) |
| 142 | + { |
| 143 | + fprintf(stderr, "Usage: %s <core.so|dylib>\n", argv[0]); |
| 144 | + return 1; |
| 145 | + } |
| 146 | + |
| 147 | + handle = dlopen(argv[1], RTLD_LAZY); |
| 148 | + if (!handle) { fprintf(stderr, "dlopen: %s\n", dlerror()); return 1; } |
| 149 | + |
| 150 | + core_init = (retro_init_t)load_sym(handle, "retro_init"); |
| 151 | + core_deinit = (retro_deinit_t)load_sym(handle, "retro_deinit"); |
| 152 | + core_set_env = (retro_set_environment_t)load_sym(handle, "retro_set_environment"); |
| 153 | + core_set_video = (retro_set_video_refresh_t)load_sym(handle, "retro_set_video_refresh"); |
| 154 | + core_set_audio = (retro_set_audio_sample_t)load_sym(handle, "retro_set_audio_sample"); |
| 155 | + core_set_audio_batch = (retro_set_audio_sample_batch_t)load_sym(handle, "retro_set_audio_sample_batch"); |
| 156 | + core_set_input_poll = (retro_set_input_poll_t)load_sym(handle, "retro_set_input_poll"); |
| 157 | + core_set_input_state = (retro_set_input_state_t)load_sym(handle, "retro_set_input_state"); |
| 158 | + core_load_game = (retro_load_game_t)load_sym(handle, "retro_load_game"); |
| 159 | + core_unload_game = (retro_unload_game_t)load_sym(handle, "retro_unload_game"); |
| 160 | + core_get_memory_data = (retro_get_memory_data_t)load_sym(handle, "retro_get_memory_data"); |
| 161 | + core_get_memory_size = (retro_get_memory_size_t)load_sym(handle, "retro_get_memory_size"); |
| 162 | + |
| 163 | + g_mmap = NULL; |
| 164 | + g_get_memory_data = core_get_memory_data; |
| 165 | + g_get_memory_size = core_get_memory_size; |
| 166 | + |
| 167 | + core_set_env(env_cb); |
| 168 | + core_set_video(video_cb); |
| 169 | + core_set_audio(audio_cb); |
| 170 | + core_set_audio_batch(audio_batch); |
| 171 | + core_set_input_poll(input_poll); |
| 172 | + core_set_input_state(input_state); |
| 173 | + core_init(); |
| 174 | + |
| 175 | + rom = make_dummy_rom(&rom_size); |
| 176 | + memset(&info, 0, sizeof(info)); |
| 177 | + info.path = "dummy.j64"; |
| 178 | + info.data = rom; |
| 179 | + info.size = rom_size; |
| 180 | + |
| 181 | + if (!core_load_game(&info)) |
| 182 | + { |
| 183 | + fprintf(stderr, "retro_load_game failed\n"); |
| 184 | + free(rom); |
| 185 | + core_deinit(); |
| 186 | + dlclose(handle); |
| 187 | + return 1; |
| 188 | + } |
| 189 | + |
| 190 | + printf("Test 1: SET_MEMORY_MAPS captured ... "); |
| 191 | + if (!g_mmap || !g_mmap->descriptors || g_mmap->num_descriptors < 1) |
| 192 | + { |
| 193 | + printf("FAIL\n"); |
| 194 | + failures++; |
| 195 | + } |
| 196 | + else |
| 197 | + printf("PASS\n"); |
| 198 | + |
| 199 | + memset(®ions, 0, sizeof(regions)); |
| 200 | + printf("Test 2: rc_libretro_memory_init(Jaguar) ... "); |
| 201 | + if (!rc_libretro_memory_init(®ions, g_mmap, libretro_get_core_memory_info, |
| 202 | + RC_CONSOLE_ATARI_JAGUAR)) |
| 203 | + { |
| 204 | + printf("FAIL\n"); |
| 205 | + failures++; |
| 206 | + } |
| 207 | + else |
| 208 | + printf("PASS\n"); |
| 209 | + |
| 210 | + sysram = (uint8_t *)core_get_memory_data(RETRO_MEMORY_SYSTEM_RAM); |
| 211 | + if (sysram) |
| 212 | + { |
| 213 | + sysram[0xABCD] = 0x42; |
| 214 | + sysram[0x1FFFFE] = 0x11; |
| 215 | + sysram[0x1FFFFF] = 0x22; |
| 216 | + } |
| 217 | + |
| 218 | + printf("Test 3: rc_libretro_memory_read(0xABCD) ... "); |
| 219 | + memset(buf, 0, sizeof(buf)); |
| 220 | + if (rc_libretro_memory_read(®ions, 0xABCDU, buf, 1) == 1 && buf[0] == 0x42) |
| 221 | + printf("PASS\n"); |
| 222 | + else |
| 223 | + { |
| 224 | + printf("FAIL\n"); |
| 225 | + failures++; |
| 226 | + } |
| 227 | + |
| 228 | + printf("Test 4: rc_libretro_memory_find(0xABCD) matches host ptr ... "); |
| 229 | + pfind = rc_libretro_memory_find(®ions, 0xABCDU); |
| 230 | + if (sysram && pfind == sysram + 0xABCD) |
| 231 | + printf("PASS\n"); |
| 232 | + else |
| 233 | + { |
| 234 | + printf("FAIL\n"); |
| 235 | + failures++; |
| 236 | + } |
| 237 | + |
| 238 | + printf("Test 5: cross-boundary read at end of RAM ... "); |
| 239 | + memset(buf, 0, sizeof(buf)); |
| 240 | + if (rc_libretro_memory_read(®ions, 0x1FFFFEU, buf, 2) == 2 |
| 241 | + && buf[0] == 0x11 && buf[1] == 0x22) |
| 242 | + printf("PASS\n"); |
| 243 | + else |
| 244 | + { |
| 245 | + printf("FAIL\n"); |
| 246 | + failures++; |
| 247 | + } |
| 248 | + |
| 249 | + printf("Test 6: rc_libretro_memory_find_avail ... "); |
| 250 | + pfind = rc_libretro_memory_find_avail(®ions, 0x1FFFFEU, &avail); |
| 251 | + if (pfind && avail >= 2 && pfind == sysram + 0x1FFFFE) |
| 252 | + printf("PASS\n"); |
| 253 | + else |
| 254 | + { |
| 255 | + printf("FAIL\n"); |
| 256 | + failures++; |
| 257 | + } |
| 258 | + |
| 259 | + rc_libretro_memory_destroy(®ions); |
| 260 | + |
| 261 | + core_unload_game(); |
| 262 | + core_deinit(); |
| 263 | + free(rom); |
| 264 | + dlclose(handle); |
| 265 | + |
| 266 | + printf("\n%s: %d test(s) failed.\n", failures ? "FAIL" : "OK", failures); |
| 267 | + return failures ? 1 : 0; |
| 268 | +} |
0 commit comments