src/platform/libretro/libretro.c (view raw)
1#include "libretro.h"
2
3#include "gba/gba.h"
4#include "gba/renderers/video-software.h"
5#include "gba/serialize.h"
6#include "gba/video.h"
7#include "util/vfs.h"
8
9static retro_environment_t environCallback;
10static retro_video_refresh_t videoCallback;
11static retro_input_poll_t inputPollCallback;
12static retro_input_state_t inputCallback;
13static retro_log_printf_t logCallback;
14
15static void GBARetroLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args);
16
17static struct GBA gba;
18static struct ARMCore cpu;
19static struct GBAVideoSoftwareRenderer renderer;
20static struct VFile* rom;
21static struct VFile* save;
22static void* savedata;
23
24unsigned retro_api_version(void) {
25 return RETRO_API_VERSION;
26}
27
28void retro_set_environment(retro_environment_t environ) {
29 environCallback = environ;
30}
31
32void retro_set_video_refresh(retro_video_refresh_t video) {
33 videoCallback = video;
34}
35
36void retro_set_audio_sample(retro_audio_sample_t audio) {
37 UNUSED(audio);
38}
39
40void retro_set_audio_sample_batch(retro_audio_sample_batch_t audioBatch) {
41 UNUSED(audioBatch);
42}
43
44void retro_set_input_poll(retro_input_poll_t inputPoll) {
45 inputPollCallback = inputPoll;
46}
47
48void retro_set_input_state(retro_input_state_t input) {
49 inputCallback = input;
50}
51
52void retro_get_system_info(struct retro_system_info* info) {
53 info->need_fullpath = false;
54 info->valid_extensions = "gba";
55 info->library_version = PROJECT_VERSION;
56 info->library_name = PROJECT_NAME;
57 info->block_extract = false;
58}
59
60void retro_get_system_av_info(struct retro_system_av_info* info) {
61 info->geometry.base_width = VIDEO_HORIZONTAL_PIXELS;
62 info->geometry.base_height = VIDEO_VERTICAL_PIXELS;
63 info->geometry.max_width = VIDEO_HORIZONTAL_PIXELS;
64 info->geometry.max_height = VIDEO_VERTICAL_PIXELS;
65 info->timing.fps = GBA_ARM7TDMI_FREQUENCY / (float) VIDEO_TOTAL_LENGTH;
66 info->timing.sample_rate = 32768;
67}
68
69void retro_init(void) {
70 enum retro_pixel_format fmt;
71#ifdef COLOR_16_BIT
72#ifdef COLOR_5_6_5
73 fmt = RETRO_PIXEL_FORMAT_RGB565;
74#else
75 fmt = RETRO_PIXEL_FORMAT_0RGB1555;
76#endif
77#else
78 fmt = RETRO_PIXEL_FORMAT_XRGB8888;
79#endif
80 environCallback(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt);
81
82 struct retro_input_descriptor inputDescriptors[] = {
83 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
84 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
85 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
86 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
87 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" },
88 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
89 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" },
90 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" },
91 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R" },
92 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L" }
93 };
94 environCallback(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, &inputDescriptors);
95
96 // TODO: RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME when BIOS booting is supported
97 // TODO: RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE
98
99 struct retro_log_callback log;
100 if (environCallback(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log)) {
101 logCallback = log.log;
102 } else {
103 logCallback = 0;
104 }
105
106 GBACreate(&gba);
107 ARMSetComponents(&cpu, &gba.d, 0, 0);
108 ARMInit(&cpu);
109 gba.logLevel = 0; // TODO: Settings
110 gba.logHandler = GBARetroLog;
111 gba.idleOptimization = IDLE_LOOP_REMOVE; // TODO: Settings
112 rom = 0;
113
114 GBAVideoSoftwareRendererCreate(&renderer);
115 renderer.outputBuffer = malloc(256 * VIDEO_VERTICAL_PIXELS * BYTES_PER_PIXEL);
116 renderer.outputBufferStride = 256;
117 GBAVideoAssociateRenderer(&gba.video, &renderer.d);
118}
119
120void retro_deinit(void) {
121 GBADestroy(&gba);
122}
123
124void retro_run(void) {
125 int keys;
126 gba.keySource = &keys;
127 inputPollCallback();
128
129 keys = 0;
130 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A)) << 0;
131 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B)) << 1;
132 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT)) << 2;
133 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START)) << 3;
134 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT)) << 4;
135 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT)) << 5;
136 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP)) << 6;
137 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN)) << 7;
138 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R)) << 8;
139 keys |= (!!inputCallback(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L)) << 9;
140
141 int frameCount = gba.video.frameCounter;
142 while (gba.video.frameCounter == frameCount) {
143 ARMRunLoop(&cpu);
144 }
145 videoCallback(renderer.outputBuffer, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, BYTES_PER_PIXEL * 256);
146}
147
148void retro_reset(void) {
149 ARMReset(&cpu);
150}
151
152bool retro_load_game(const struct retro_game_info* game) {
153 if (game->data) {
154 rom = VFileFromMemory((void*) game->data, game->size); // TODO: readonly VFileMem
155 } else {
156 rom = VFileOpen(game->path, O_RDONLY);
157 }
158 if (!rom) {
159 return false;
160 }
161 if (!GBAIsROM(rom)) {
162 return false;
163 }
164
165 // TODO
166 save = 0;
167 savedata = 0;
168
169 GBALoadROM(&gba, rom, save, game->path);
170 ARMReset(&cpu);
171 return true;
172}
173
174void retro_unload_game(void) {
175 // TODO
176}
177
178size_t retro_serialize_size(void) {
179 return sizeof(struct GBASerializedState);
180}
181
182bool retro_serialize(void* data, size_t size) {
183 if (size != retro_serialize_size()) {
184 return false;
185 }
186 GBASerialize(&gba, data);
187 return true;
188}
189
190bool retro_unserialize(const void* data, size_t size) {
191 if (size != retro_serialize_size()) {
192 return false;
193 }
194 GBADeserialize(&gba, data);
195 return true;
196}
197
198void retro_cheat_reset(void) {
199 // TODO: Cheats
200}
201
202void retro_cheat_set(unsigned index, bool enabled, const char* code) {
203 // TODO: Cheats
204 UNUSED(index);
205 UNUSED(enabled);
206 UNUSED(code);
207}
208
209unsigned retro_get_region(void) {
210 return RETRO_REGION_NTSC; // TODO: This isn't strictly true
211}
212
213void retro_set_controller_port_device(unsigned port, unsigned device) {
214 UNUSED(port);
215 UNUSED(device);
216}
217
218bool retro_load_game_special(unsigned game_type, const struct retro_game_info* info, size_t num_info) {
219 UNUSED(game_type);
220 UNUSED(info);
221 UNUSED(num_info);
222 return false;
223}
224
225void* retro_get_memory_data(unsigned id) {
226 // TODO
227 UNUSED(id);
228 return 0;
229}
230
231size_t retro_get_memory_size(unsigned id) {
232 // TODO
233 UNUSED(id);
234 return 0;
235}
236
237void GBARetroLog(struct GBAThread* thread, enum GBALogLevel level, const char* format, va_list args) {
238 UNUSED(thread);
239 if (!logCallback) {
240 return;
241 }
242
243 char message[128];
244 vsnprintf(message, sizeof(message), format, args);
245
246 enum retro_log_level retroLevel = RETRO_LOG_INFO;
247 switch (level) {
248 case GBA_LOG_ALL:
249 case GBA_LOG_ERROR:
250 case GBA_LOG_FATAL:
251 retroLevel = RETRO_LOG_ERROR;
252 break;
253 case GBA_LOG_WARN:
254 retroLevel = RETRO_LOG_WARN;
255 break;
256 case GBA_LOG_INFO:
257 case GBA_LOG_GAME_ERROR:
258 case GBA_LOG_SWI:
259 retroLevel = RETRO_LOG_INFO;
260 break;
261 case GBA_LOG_DEBUG:
262 case GBA_LOG_STUB:
263 retroLevel = RETRO_LOG_DEBUG;
264 break;
265 }
266 logCallback(retroLevel, "%s\n", message);
267}