1 /*
2 * This file is part of EasyRPG Player.
3 *
4 * EasyRPG Player is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * EasyRPG Player is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with EasyRPG Player. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #ifdef USE_LIBRETRO
19
20 // Headers
21 #include "libretro_ui.h"
22 #include "libretro_clock.h"
23 #include "bitmap.h"
24 #include "color.h"
25 #include "graphics.h"
26 #include "input.h"
27 #include "keys.h"
28 #include "main_data.h"
29 #include "options.h"
30 #include "output.h"
31 #include "player.h"
32 #include "scene.h"
33 #include "version.h"
34
35 #include <cstring>
36 #include <stdio.h>
37 #include <stdint.h>
38 #include <stdlib.h>
39 #include <stdarg.h>
40 #include <string>
41 #include <math.h>
42
43 namespace Options {
44 const char* retropad_x = "easyrpg_retropad_x";
45 const char* retropad_l = "easyrpg_retropad_l";
46 const char* retropad_r = "easyrpg_retropad_r";
47 const char* retropad_l2 = "easyrpg_retropad_l2";
48 const char* retropad_r2 = "easyrpg_retropad_r2";
49 const char* retropad_l3 = "easyrpg_retropad_l3";
50 const char* retropad_r3 = "easyrpg_retropad_r3";
51 const char* input = "easyrpg_input";
52 const char* debug_mode = "easyrpg_debug_mode";
53 }
54
55 #ifdef SUPPORT_AUDIO
56 #include "libretro_audio.h"
GetAudio()57 AudioInterface& LibretroUi::GetAudio() {
58 return *audio_;
59 }
60 #endif
61
62 retro_environment_t LibretroUi::environ_cb = nullptr;
63 retro_input_poll_t LibretroUi::input_poll_cb = nullptr;
64 bool LibretroUi::player_exit_called = false;
65
66 #if defined(USE_KEYBOARD) && defined(SUPPORT_KEYBOARD)
67 static Input::Keys::InputKey RetroKey2InputKey(int retrokey);
68 #endif
69
70 #if defined(USE_JOYSTICK) && defined(SUPPORT_JOYSTICK)
71 static Input::Keys::InputKey RetroJKey2InputKey(int button_index);
72 static int button_remapper[7];
73 #endif
74
75 enum PadInputState {
76 PadInputState_RetroPad = 1,
77 PadInputState_Keyboard = 2
78 };
79
LibretroUi(int width,int height,const Game_ConfigVideo & cfg)80 LibretroUi::LibretroUi(int width, int height, const Game_ConfigVideo& cfg) : BaseUi(cfg)
81 {
82 // Handled by libretro
83 // FIXME: There is currently no callback from libretro telling us whether or not fullscreen is enabled.
84 SetIsFullscreen(false);
85
86 current_display_mode.width = width;
87 current_display_mode.height = height;
88 current_display_mode.bpp = 32;
89
90 // libretro always owns main loop
91 SetFrameRateSynchronized(true);
92
93 const DynamicFormat format(
94 32,
95 0x00FF0000,
96 0x0000FF00,
97 0x000000FF,
98 0xFF000000,
99 PF::NoAlpha);
100
101 Bitmap::SetFormat(Bitmap::ChooseFormat(format));
102 main_surface.reset();
103 main_surface = Bitmap::Create(current_display_mode.width,
104 current_display_mode.height,
105 false,
106 current_display_mode.bpp
107 );
108
109 #ifdef SUPPORT_AUDIO
110 audio_.reset(new LibretroAudio());
111 #endif
112
113 UpdateVariables();
114 }
115
ToggleFullscreen()116 void LibretroUi::ToggleFullscreen() {
117 // no-op
118 }
119
ToggleZoom()120 void LibretroUi::ToggleZoom() {
121 // no-op
122 }
123
UpdateDisplay()124 void LibretroUi::UpdateDisplay() {
125 if (UpdateWindow == nullptr) {
126 return;
127 }
128
129 UpdateWindow(main_surface->pixels(), current_display_mode.width, current_display_mode.height, main_surface->pitch());
130 }
131
SetTitle(const std::string & title)132 void LibretroUi::SetTitle(const std::string &title){
133 // no-op
134 }
135
ShowCursor(bool flag)136 bool LibretroUi::ShowCursor(bool flag) {
137 return false;
138 }
139
ProcessEvents()140 void LibretroUi::ProcessEvents() {
141 # if defined(USE_JOYSTICK) && defined(SUPPORT_JOYSTICK)
142 if (CheckInputState == nullptr) {
143 return;
144 }
145
146 LibretroUi::input_poll_cb();
147
148 bool any_var_changed;
149 LibretroUi::environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &any_var_changed);
150 if (any_var_changed) {
151 UpdateVariables();
152 }
153
154 if ((keyboard_retropad_state & PadInputState_RetroPad) == 0) {
155 return;
156 }
157
158 auto check_pressed = [this] (int button_id) {
159 keys[RetroJKey2InputKey(button_id)] = CheckInputState(0, RETRO_DEVICE_JOYPAD, 0, button_id) != 0;
160 };
161
162 # if defined(USE_JOYSTICK_HAT) && defined(SUPPORT_JOYSTICK_HAT)
163 check_pressed(RETRO_DEVICE_ID_JOYPAD_UP);
164 check_pressed(RETRO_DEVICE_ID_JOYPAD_DOWN);
165 check_pressed(RETRO_DEVICE_ID_JOYPAD_LEFT);
166 check_pressed(RETRO_DEVICE_ID_JOYPAD_RIGHT);
167 # endif
168
169 check_pressed(RETRO_DEVICE_ID_JOYPAD_A);
170 check_pressed(RETRO_DEVICE_ID_JOYPAD_B);
171 check_pressed(RETRO_DEVICE_ID_JOYPAD_Y);
172 check_pressed(RETRO_DEVICE_ID_JOYPAD_START);
173 check_pressed(RETRO_DEVICE_ID_JOYPAD_SELECT);
174
175 check_pressed(RETRO_DEVICE_ID_JOYPAD_X);
176 check_pressed(RETRO_DEVICE_ID_JOYPAD_L);
177 check_pressed(RETRO_DEVICE_ID_JOYPAD_R);
178 check_pressed(RETRO_DEVICE_ID_JOYPAD_L2);
179 check_pressed(RETRO_DEVICE_ID_JOYPAD_R2);
180 check_pressed(RETRO_DEVICE_ID_JOYPAD_L3);
181 check_pressed(RETRO_DEVICE_ID_JOYPAD_R3);
182
183 # if defined(USE_JOYSTICK_AXIS) && defined(SUPPORT_JOYSTICK_AXIS)
184 int16_t axis = CheckInputState(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X);
185 if (axis < -JOYSTICK_AXIS_SENSIBILITY) {
186 keys[Input::Keys::JOY_AXIS_X_LEFT] = true;
187 keys[Input::Keys::JOY_AXIS_X_RIGHT] = false;
188 } else if (axis > JOYSTICK_AXIS_SENSIBILITY) {
189 keys[Input::Keys::JOY_AXIS_X_LEFT] = false;
190 keys[Input::Keys::JOY_AXIS_X_RIGHT] = true;
191 } else {
192 keys[Input::Keys::JOY_AXIS_X_LEFT] = false;
193 keys[Input::Keys::JOY_AXIS_X_RIGHT] = false;
194 }
195
196 axis = CheckInputState(0, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y);
197 if (axis < -JOYSTICK_AXIS_SENSIBILITY) {
198 keys[Input::Keys::JOY_AXIS_Y_UP] = true;
199 keys[Input::Keys::JOY_AXIS_Y_DOWN] = false;
200 } else if (axis > JOYSTICK_AXIS_SENSIBILITY) {
201 keys[Input::Keys::JOY_AXIS_Y_UP] = false;
202 keys[Input::Keys::JOY_AXIS_Y_DOWN] = true;
203 } else {
204 keys[Input::Keys::JOY_AXIS_Y_UP] = false;
205 keys[Input::Keys::JOY_AXIS_Y_DOWN] = false;
206 }
207 # endif
208 # endif
209 }
210
211 retro_video_refresh_t LibretroUi::UpdateWindow = nullptr;
SetRetroVideoCallback(retro_video_refresh_t cb)212 void LibretroUi::SetRetroVideoCallback(retro_video_refresh_t cb) {
213 UpdateWindow = cb;
214 }
215
216 retro_input_state_t LibretroUi::CheckInputState = nullptr;
SetRetroInputStateCallback(retro_input_state_t cb)217 void LibretroUi::SetRetroInputStateCallback(retro_input_state_t cb) {
218 CheckInputState = cb;
219 }
220
UpdateKeyboardCallback(bool down,unsigned keycode)221 void LibretroUi::UpdateKeyboardCallback(bool down, unsigned keycode) {
222 if ((keyboard_retropad_state & PadInputState_Keyboard) == PadInputState_Keyboard) {
223 keys[RetroKey2InputKey(keycode)] = down;
224 }
225 }
226
UpdateVariables()227 void LibretroUi::UpdateVariables() {
228 static const char* none = "None (See Core Options)";
229
230 static struct retro_variable debug = { Options::debug_mode, nullptr };
231 static struct retro_variable input = { Options::input, nullptr };
232 static struct retro_variable variables[] = {
233 { Options::retropad_x, nullptr },
234 { Options::retropad_l, nullptr },
235 { Options::retropad_r, nullptr },
236 { Options::retropad_l2, nullptr },
237 { Options::retropad_r2, nullptr },
238 { Options::retropad_l3, nullptr },
239 { Options::retropad_r3, nullptr }
240 };
241
242 static const char buttons[][24] = {
243 "0", "1", "2", "3", "4",
244 "5", "6", "7", "8", "9",
245 "+", "-", "*", "/", ".",
246 "Open Debug Menu", "Walk through walls"
247 };
248
249 LibretroUi::environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &debug);
250 Player::debug_flag = strcmp(debug.value, "Enabled") == 0;
251
252 LibretroUi::environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &input);
253 if (strcmp(input.value, "Use Both") == 0) {
254 keyboard_retropad_state = PadInputState_Keyboard | PadInputState_RetroPad;
255 } else if (strcmp(input.value, "Only Keyboard") == 0) {
256 keyboard_retropad_state = PadInputState_Keyboard;
257 } else if (strcmp(input.value, "Only RetroPad") == 0) {
258 keyboard_retropad_state = PadInputState_RetroPad;
259 }
260
261 // Button remapping from settings
262 for (int i = 0; i < 7; ++i) {
263 LibretroUi::environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &variables[i]);
264 button_remapper[i] = Input::Keys::NONE;
265 for (int j = 0; j < 17; ++j) {
266 if (strcmp(variables[i].value, buttons[j]) == 0) {
267 button_remapper[i] = Input::Keys::JOY_10 + j;
268 break;
269 }
270 }
271 if (button_remapper[i] == Input::Keys::NONE) {
272 variables[i].value = none;
273 }
274 }
275
276 struct retro_input_descriptor desc[] = {
277 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" },
278 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" },
279 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
280 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" },
281 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "Confirm" },
282 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "Cancel" },
283 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Shift" },
284 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Reset" },
285
286 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, variables[0].value },
287 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, variables[1].value },
288 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, variables[2].value },
289 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, variables[3].value },
290 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, variables[4].value },
291 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, variables[5].value },
292 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, variables[6].value },
293
294 { 0 }
295 };
296
297 LibretroUi::environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, &desc);
298 }
299
300 #if defined(USE_KEYBOARD) && defined(SUPPORT_KEYBOARD)
RetroKey2InputKey(int retrokey)301 Input::Keys::InputKey RetroKey2InputKey(int retrokey) {
302 switch (retrokey) {
303 case RETROK_BACKSPACE : return Input::Keys::BACKSPACE;
304 case RETROK_TAB : return Input::Keys::TAB;
305 case RETROK_CLEAR : return Input::Keys::CLEAR;
306 case RETROK_RETURN : return Input::Keys::RETURN;
307 case RETROK_PAUSE : return Input::Keys::PAUSE;
308 case RETROK_ESCAPE : return Input::Keys::ESCAPE;
309 case RETROK_SPACE : return Input::Keys::SPACE;
310 case RETROK_PAGEUP : return Input::Keys::PGUP;
311 case RETROK_PAGEDOWN : return Input::Keys::PGDN;
312 case RETROK_END : return Input::Keys::ENDS;
313 case RETROK_HOME : return Input::Keys::HOME;
314 case RETROK_LEFT : return Input::Keys::LEFT;
315 case RETROK_UP : return Input::Keys::UP;
316 case RETROK_RIGHT : return Input::Keys::RIGHT;
317 case RETROK_DOWN : return Input::Keys::DOWN;
318 case RETROK_PRINT : return Input::Keys::SNAPSHOT;
319 case RETROK_INSERT : return Input::Keys::INSERT;
320 case RETROK_DELETE : return Input::Keys::DEL;
321 case RETROK_LSHIFT : return Input::Keys::LSHIFT;
322 case RETROK_RSHIFT : return Input::Keys::RSHIFT;
323 case RETROK_LCTRL : return Input::Keys::LCTRL;
324 case RETROK_RCTRL : return Input::Keys::RCTRL;
325 case RETROK_LALT : return Input::Keys::LALT;
326 case RETROK_RALT : return Input::Keys::RALT;
327 case RETROK_0 : return Input::Keys::N0;
328 case RETROK_1 : return Input::Keys::N1;
329 case RETROK_2 : return Input::Keys::N2;
330 case RETROK_3 : return Input::Keys::N3;
331 case RETROK_4 : return Input::Keys::N4;
332 case RETROK_5 : return Input::Keys::N5;
333 case RETROK_6 : return Input::Keys::N6;
334 case RETROK_7 : return Input::Keys::N7;
335 case RETROK_8 : return Input::Keys::N8;
336 case RETROK_9 : return Input::Keys::N9;
337 case RETROK_a : return Input::Keys::A;
338 case RETROK_b : return Input::Keys::B;
339 case RETROK_c : return Input::Keys::C;
340 case RETROK_d : return Input::Keys::D;
341 case RETROK_e : return Input::Keys::E;
342 case RETROK_f : return Input::Keys::F;
343 case RETROK_g : return Input::Keys::G;
344 case RETROK_h : return Input::Keys::H;
345 case RETROK_i : return Input::Keys::I;
346 case RETROK_j : return Input::Keys::J;
347 case RETROK_k : return Input::Keys::K;
348 case RETROK_l : return Input::Keys::L;
349 case RETROK_m : return Input::Keys::M;
350 case RETROK_n : return Input::Keys::N;
351 case RETROK_o : return Input::Keys::O;
352 case RETROK_p : return Input::Keys::P;
353 case RETROK_q : return Input::Keys::Q;
354 case RETROK_r : return Input::Keys::R;
355 case RETROK_s : return Input::Keys::S;
356 case RETROK_t : return Input::Keys::T;
357 case RETROK_u : return Input::Keys::U;
358 case RETROK_v : return Input::Keys::V;
359 case RETROK_w : return Input::Keys::W;
360 case RETROK_x : return Input::Keys::X;
361 case RETROK_y : return Input::Keys::Y;
362 case RETROK_z : return Input::Keys::Z;
363 case RETROK_MENU : return Input::Keys::MENU;
364 case RETROK_KP0 : return Input::Keys::KP0;
365 case RETROK_KP1 : return Input::Keys::KP1;
366 case RETROK_KP2 : return Input::Keys::KP2;
367 case RETROK_KP3 : return Input::Keys::KP3;
368 case RETROK_KP4 : return Input::Keys::KP4;
369 case RETROK_KP5 : return Input::Keys::KP5;
370 case RETROK_KP6 : return Input::Keys::KP6;
371 case RETROK_KP7 : return Input::Keys::KP7;
372 case RETROK_KP8 : return Input::Keys::KP8;
373 case RETROK_KP9 : return Input::Keys::KP9;
374 case RETROK_KP_MULTIPLY : return Input::Keys::KP_MULTIPLY;
375 case RETROK_KP_PLUS : return Input::Keys::KP_ADD;
376 case RETROK_KP_ENTER : return Input::Keys::RETURN;
377 case RETROK_KP_MINUS : return Input::Keys::KP_SUBTRACT;
378 case RETROK_KP_PERIOD : return Input::Keys::KP_PERIOD;
379 case RETROK_KP_DIVIDE : return Input::Keys::KP_DIVIDE;
380 case RETROK_COMMA : return Input::Keys::COMMA;
381 case RETROK_PERIOD : return Input::Keys::PERIOD;
382 case RETROK_SLASH : return Input::Keys::SLASH;
383 case RETROK_F1 : return Input::Keys::F1;
384 case RETROK_F2 : return Input::Keys::F2;
385 case RETROK_F3 : return Input::Keys::F3;
386 case RETROK_F4 : return Input::Keys::F4;
387 case RETROK_F5 : return Input::Keys::F5;
388 case RETROK_F6 : return Input::Keys::F6;
389 case RETROK_F7 : return Input::Keys::F7;
390 case RETROK_F8 : return Input::Keys::F8;
391 case RETROK_F9 : return Input::Keys::F9;
392 case RETROK_F10 : return Input::Keys::F10;
393 case RETROK_F11 : return Input::Keys::F11;
394 case RETROK_F12 : return Input::Keys::F12;
395 case RETROK_CAPSLOCK : return Input::Keys::CAPS_LOCK;
396 case RETROK_NUMLOCK : return Input::Keys::NUM_LOCK;
397 case RETROK_SCROLLOCK : return Input::Keys::SCROLL_LOCK;
398
399 default : return Input::Keys::NONE;
400 }
401 }
402 #endif
403
404 #if defined(USE_JOYSTICK) && defined(SUPPORT_JOYSTICK)
RetroJKey2InputKey(int button_index)405 Input::Keys::InputKey RetroJKey2InputKey(int button_index) {
406 switch (button_index) {
407 case RETRO_DEVICE_ID_JOYPAD_UP : return Input::Keys::JOY_0;
408 case RETRO_DEVICE_ID_JOYPAD_DOWN : return Input::Keys::JOY_1;
409 case RETRO_DEVICE_ID_JOYPAD_LEFT : return Input::Keys::JOY_2;
410 case RETRO_DEVICE_ID_JOYPAD_RIGHT : return Input::Keys::JOY_3;
411 case RETRO_DEVICE_ID_JOYPAD_A : return Input::Keys::JOY_4;
412 case RETRO_DEVICE_ID_JOYPAD_B : return Input::Keys::JOY_5;
413 case RETRO_DEVICE_ID_JOYPAD_Y : return Input::Keys::JOY_6;
414 case RETRO_DEVICE_ID_JOYPAD_START : return Input::Keys::JOY_7;
415 case RETRO_DEVICE_ID_JOYPAD_SELECT : return Input::Keys::JOY_8;
416
417 case RETRO_DEVICE_ID_JOYPAD_X : return (Input::Keys::InputKey)button_remapper[0];
418 case RETRO_DEVICE_ID_JOYPAD_L : return (Input::Keys::InputKey)button_remapper[1];
419 case RETRO_DEVICE_ID_JOYPAD_R : return (Input::Keys::InputKey)button_remapper[2];
420 case RETRO_DEVICE_ID_JOYPAD_L2 : return (Input::Keys::InputKey)button_remapper[3];
421 case RETRO_DEVICE_ID_JOYPAD_R2 : return (Input::Keys::InputKey)button_remapper[4];
422 case RETRO_DEVICE_ID_JOYPAD_L3 : return (Input::Keys::InputKey)button_remapper[5];
423 case RETRO_DEVICE_ID_JOYPAD_R3 : return (Input::Keys::InputKey)button_remapper[6];
424
425 default : return Input::Keys::NONE;
426 }
427 }
428 #endif
429
430 /* libretro api implementation */
431 static const unsigned AUDIO_SAMPLERATE = 48000;
432
retro_time_update(retro_usec_t usec)433 RETRO_CALLCONV void retro_time_update(retro_usec_t usec) {
434 LibretroClock::time_in_microseconds += usec;
435 }
436
retro_write_audio()437 RETRO_CALLCONV void retro_write_audio() {
438 if (DisplayUi) {
439 LibretroAudio::AudioThreadCallback();
440 }
441 }
442
retro_enable_audio(bool enabled)443 RETRO_CALLCONV void retro_enable_audio(bool enabled) {
444 LibretroAudio::EnableAudio(enabled);
445 }
446
retro_keyboard_event(bool down,unsigned keycode,uint32_t,uint16_t)447 RETRO_CALLCONV void retro_keyboard_event(bool down, unsigned keycode, uint32_t, uint16_t) {
448 if (DisplayUi) {
449 static_cast<LibretroUi*>(DisplayUi.get())->UpdateKeyboardCallback(down, keycode);
450 }
451 }
452
fallback_log(enum retro_log_level level,const char * fmt,...)453 static void fallback_log(enum retro_log_level level, const char *fmt, ...) {
454 (void) level;
455 va_list va;
456 va_start(va, fmt);
457 vfprintf(stderr, fmt, va);
458 va_end(va);
459 }
460
461 static retro_log_printf_t log_cb = fallback_log;
462
463 /* Sets callbacks. retro_set_environment() is guaranteed to be called
464 * before retro_init().
465 *
466 * The rest of the set_* functions are guaranteed to have been called
467 * before the first call to retro_run() is made. */
retro_set_environment(retro_environment_t cb)468 RETRO_API void retro_set_environment(retro_environment_t cb) {
469 bool no_content = false;
470
471 static retro_frame_time_callback frame_time_definition = {
472 retro_time_update,
473 1000000 / Game_Clock::GetTargetGameFps()
474 };
475
476 static retro_audio_callback audio_callback_definition = {
477 retro_write_audio,
478 retro_enable_audio
479 };
480
481 static retro_keyboard_callback keyboard_callback_definition = {
482 retro_keyboard_event
483 };
484
485 static struct retro_log_callback logging;
486
487 LibretroUi::environ_cb = cb;
488
489 cb(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME, &no_content);
490 cb(RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK, &audio_callback_definition);
491 cb(RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK, &frame_time_definition);
492 cb(RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK, &keyboard_callback_definition);
493
494 if (cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logging))
495 log_cb = logging.log;
496 else
497 log_cb = fallback_log;
498
499 # define EP_RETRO_OPTIONS "None|0|1|2|3|4|5|6|7|8|9|+|-|*|/|.|Open Debug Menu|Walk through walls"
500
501 struct retro_variable variables[] = {
502 { Options::retropad_x, "Button mapping of RetroPad X; " EP_RETRO_OPTIONS },
503 { Options::retropad_l, "Button mapping of RetroPad L; " EP_RETRO_OPTIONS },
504 { Options::retropad_r, "Button mapping of RetroPad R; " EP_RETRO_OPTIONS },
505 { Options::retropad_l2, "Button mapping of RetroPad L2; " EP_RETRO_OPTIONS },
506 { Options::retropad_r2, "Button mapping of RetroPad R2; " EP_RETRO_OPTIONS },
507 { Options::retropad_l3, "Button mapping of RetroPad L3; " EP_RETRO_OPTIONS },
508 { Options::retropad_r3, "Button mapping of RetroPad R3; " EP_RETRO_OPTIONS },
509 { Options::input, "Keyboard and RetroPad; Use Both|Only Keyboard|Only RetroPad" },
510 { Options::debug_mode, "Debug menu and walk through walls; Disabled|Enabled" },
511 { nullptr, nullptr }
512 };
513 cb(RETRO_ENVIRONMENT_SET_VARIABLES, variables);
514 }
515
retro_set_video_refresh(retro_video_refresh_t cb)516 RETRO_API void retro_set_video_refresh(retro_video_refresh_t cb) {
517 LibretroUi::SetRetroVideoCallback(cb);
518 }
519
retro_set_audio_sample(retro_audio_sample_t cb)520 RETRO_API void retro_set_audio_sample(retro_audio_sample_t cb) {
521 // unused
522 }
523
retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)524 RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) {
525 LibretroAudio::SetRetroAudioCallback(cb);
526 }
527
retro_set_input_poll(retro_input_poll_t cb)528 RETRO_API void retro_set_input_poll(retro_input_poll_t cb) {
529 LibretroUi::input_poll_cb = cb;
530 }
531
retro_set_input_state(retro_input_state_t cb)532 RETRO_API void retro_set_input_state(retro_input_state_t cb) {
533 LibretroUi::SetRetroInputStateCallback(cb);
534 }
535
init_easy_rpg()536 static void init_easy_rpg() {
537 Player::exit_flag = false;
538 LibretroUi::player_exit_called = false;
539
540 Player::Init(0, nullptr);
541
542 auto buttons = Input::GetDefaultButtonMappings();
543 auto directions = Input::GetDefaultDirectionMappings();
544
545 Input::Init(std::move(buttons), std::move(directions), "", "");
546 }
547
548 /* Library global initialization/deinitialization. */
retro_init()549 RETRO_API void retro_init() {
550 // no-op, handled in retro_load_game
551 }
552
retro_deinit()553 RETRO_API void retro_deinit() {
554 // no-op, handled in retro_unload_game
555 }
556
557 /* Must return RETRO_API_VERSION. Used to validate ABI compatibility
558 * when the API is revised. */
retro_api_version()559 RETRO_API unsigned retro_api_version() {
560 return RETRO_API_VERSION;
561 }
562
563 /* Gets statically known system info. Pointers provided in *info
564 * must be statically allocated.
565 * Can be called at any time, even before retro_init(). */
retro_get_system_info(struct retro_system_info * info)566 RETRO_API void retro_get_system_info(struct retro_system_info* info) {
567 memset(info, 0, sizeof(*info));
568 info->library_name = "EasyRPG Player";
569 #ifndef GIT_VERSION
570 #define GIT_VERSION ""
571 #endif
572 info->library_version = PLAYER_VERSION GIT_VERSION;
573 info->need_fullpath = true;
574 info->valid_extensions = "ldb|zip|easyrpg";
575 info->block_extract = true;
576 }
577
578 /* Gets information about system audio/video timings and geometry.
579 * Can be called only after retro_load_game() has successfully completed.
580 * NOTE: The implementation of this function might not initialize every
581 * variable if needed.
582 * E.g. geom.aspect_ratio might not be initialized if core doesn't
583 * desire a particular aspect ratio. */
retro_get_system_av_info(struct retro_system_av_info * info)584 RETRO_API void retro_get_system_av_info(struct retro_system_av_info* info) {
585 info->geometry.base_width = SCREEN_TARGET_WIDTH;
586 info->geometry.base_height = SCREEN_TARGET_HEIGHT;
587 info->geometry.max_width = SCREEN_TARGET_WIDTH;
588 info->geometry.max_height = SCREEN_TARGET_HEIGHT;
589 info->geometry.aspect_ratio = 0.0f;
590 info->timing.fps = Game_Clock::GetTargetGameFps();
591 info->timing.sample_rate = AUDIO_SAMPLERATE;
592 }
593
594 /* Sets device to be used for player 'port'.
595 * By default, RETRO_DEVICE_JOYPAD is assumed to be plugged into all
596 * available ports.
597 * Setting a particular device type is not a guarantee that libretro cores
598 * will only poll input based on that particular device type. It is only a
599 * hint to the libretro core when a core cannot automatically detect the
600 * appropriate input device type on its own. It is also relevant when a
601 * core can change its behavior depending on device type. */
retro_set_controller_port_device(unsigned port,unsigned device)602 RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device) {
603 // Not used
604 }
605
606 /* Resets the current game. */
retro_reset(void)607 RETRO_API void retro_reset(void) {
608 Player::reset_flag = true;
609 }
610
611 /* Runs the game for one video frame.
612 * During retro_run(), input_poll callback must be called at least once.
613 *
614 * If a frame is not rendered for reasons where a game "dropped" a frame,
615 * this still counts as a frame, and retro_run() should explicitly dupe
616 * a frame if GET_CAN_DUPE returns true.
617 * In this case, the video callback can take a NULL argument for data.
618 */
619
retro_run()620 RETRO_API void retro_run() {
621 Player::MainLoop();
622
623 if (!DisplayUi) {
624 // Player::Exit was called, send shutdown request to the frontend
625 LibretroUi::player_exit_called = true;
626 // This closes the whole frontend and not just the core, but there is no API for core unloading...
627 LibretroUi::environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, nullptr);
628 }
629 }
630
extract_directory(char * buf,const char * path,size_t size)631 static void extract_directory(char *buf, const char *path, size_t size) {
632 strncpy(buf, path, size - 1);
633 buf[size - 1] = '\0';
634
635 char *base = strrchr(buf, '/');
636 if (!base)
637 base = strrchr(buf, '\\');
638
639 if (base)
640 *base = '\0';
641 else
642 buf[0] = '\0';
643 }
644
645 /* Loads a game. */
retro_load_game(const struct retro_game_info * game)646 RETRO_API bool retro_load_game(const struct retro_game_info* game) {
647 char parent_dir[1024];
648 enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
649
650 if (!game)
651 return false;
652
653 if (!LibretroUi::environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
654 log_cb(RETRO_LOG_INFO, "XRGB8888 is not supported.\n");
655 return false;
656 }
657
658 Output::IgnorePause(true);
659
660 auto fs = FileFinder::Root().Create(game->path);
661 if (!fs) {
662 extract_directory(parent_dir, game->path, sizeof(parent_dir));
663 fs = FileFinder::Root().Create(parent_dir);
664 if (!fs || !FileFinder::IsValidProject(fs)) {
665 log_cb(RETRO_LOG_ERROR, "Unsupported game %s\n", parent_dir);
666 return false;
667 }
668 }
669 FileFinder::SetGameFilesystem(fs);
670
671 init_easy_rpg();
672
673 Player::Run();
674
675 return true;
676 }
677
678 /* Unloads a currently loaded game. */
retro_unload_game()679 RETRO_API void retro_unload_game() {
680 // Workaround a crash on Windows & Android because the callbacks are invoked after the DLL/SO was unloaded
681 static retro_audio_callback no_audio_callback_definition = {
682 nullptr,
683 nullptr
684 };
685 LibretroUi::environ_cb(RETRO_ENVIRONMENT_SET_AUDIO_CALLBACK, &no_audio_callback_definition);
686
687 if (!LibretroUi::player_exit_called) {
688 // Shutdown requested by the frontend and not via Title scene
689 Player::Exit();
690 }
691 }
692
693 // unused stuff required by libretro api
694 // this looks like features only emulators use but they say that libretro is
695 // not a emulator only API :P
696
697 /* Returns the amount of data the implementation requires to serialize
698 * internal state (save states).
699 * Between calls to retro_load_game() and retro_unload_game(), the
700 * returned size is never allowed to be larger than a previous returned
701 * value, to ensure that the frontend can allocate a save state buffer once.
702 */
retro_serialize_size()703 RETRO_API size_t retro_serialize_size() {
704 // no-op
705 return 0;
706 }
707
708 /* Serializes internal state. If failed, or size is lower than
709 * retro_serialize_size(), it should return false, true otherwise. */
retro_serialize(void * data,size_t size)710 RETRO_API bool retro_serialize(void *data, size_t size) {
711 // no-op
712 return false;
713 }
714
retro_unserialize(const void * data,size_t size)715 RETRO_API bool retro_unserialize(const void *data, size_t size) {
716 // no-op
717 return false;
718 }
719
retro_cheat_reset(void)720 RETRO_API void retro_cheat_reset(void) {
721 // not used
722 }
723
retro_cheat_set(unsigned index,bool enabled,const char * code)724 RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code) {
725 // not used
726 }
727
728 /* Loads a "special" kind of game. Should not be used,
729 * except in extreme cases. */
retro_load_game_special(unsigned game_type,const struct retro_game_info * info,size_t num_info)730 RETRO_API bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info) {
731 // no-op
732 return false;
733 }
734
735 /* Gets region of game. */
retro_get_region()736 RETRO_API unsigned retro_get_region() {
737 // no-op
738 return RETRO_REGION_NTSC;
739 }
740
741 /* Gets region of memory. */
retro_get_memory_data(unsigned id)742 RETRO_API void* retro_get_memory_data(unsigned id) {
743 // no-op
744 return nullptr;
745 }
746
retro_get_memory_size(unsigned id)747 RETRO_API size_t retro_get_memory_size(unsigned id) {
748 // no-op
749 return 0;
750 }
751
752 #endif
753