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