1 #ifdef _WIN32
2 #include <direct.h>
3 #else
4 #include <unistd.h>
5 #endif
6 #include <stdio.h>
7
8 #include <libretro.h>
9 #include <streams/file_stream.h>
10
11 #include "libretro_shared.h"
12 #include "../common/misc.fdh"
13 #include "../graphics/graphics.h"
14 #include "../input.fdh"
15 #include "../input.h"
16 #include "../nx.h"
17
18 void post_main();
19 bool run_main();
20
21 void *retro_frame_buffer;
22 unsigned retro_frame_buffer_width;
23 unsigned retro_frame_buffer_height;
24 unsigned retro_frame_buffer_pitch;
25
26 retro_log_printf_t log_cb;
27 static retro_video_refresh_t video_cb;
28 static retro_input_poll_t poll_cb;
29 retro_input_state_t input_cb;
30 static retro_audio_sample_batch_t audio_batch_cb;
31 static retro_environment_t environ_cb;
32
33 bool libretro_supports_bitmasks = false;
34
35 static unsigned g_frame_cnt;
36
37 bool retro_60hz = true;
38 unsigned pitch;
39
40 extern bool pre_main(void);
41
retro_get_tick(void)42 unsigned retro_get_tick(void)
43 {
44 return g_frame_cnt;
45 }
46
retro_set_environment(retro_environment_t cb)47 void retro_set_environment(retro_environment_t cb)
48 {
49 struct retro_vfs_interface_info vfs_iface_info;
50 environ_cb = cb;
51
52 vfs_iface_info.required_interface_version = 1;
53 vfs_iface_info.iface = NULL;
54 if (environ_cb(RETRO_ENVIRONMENT_GET_VFS_INTERFACE, &vfs_iface_info))
55 filestream_vfs_init(&vfs_iface_info);
56 }
57
retro_api_version(void)58 unsigned retro_api_version(void)
59 {
60 return RETRO_API_VERSION;
61 }
62
retro_set_video_refresh(retro_video_refresh_t cb)63 void retro_set_video_refresh(retro_video_refresh_t cb)
64 {
65 video_cb = cb;
66 }
67
retro_set_audio_sample(retro_audio_sample_t cb)68 void retro_set_audio_sample(retro_audio_sample_t cb)
69 { }
70
retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)71 void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
72 {
73 audio_batch_cb = cb;
74 }
75
retro_set_input_poll(retro_input_poll_t cb)76 void retro_set_input_poll(retro_input_poll_t cb)
77 {
78 poll_cb = cb;
79 }
80
retro_set_input_state(retro_input_state_t cb)81 void retro_set_input_state(retro_input_state_t cb)
82 {
83 input_cb = cb;
84 }
85
retro_get_system_info(struct retro_system_info * info)86 void retro_get_system_info(struct retro_system_info *info)
87 {
88 info->need_fullpath = true;
89 info->valid_extensions = "exe";
90 info->library_version = "1.0.0.6";
91 info->library_name = "NXEngine";
92 info->block_extract = false;
93 }
94
95 char g_dir[1024];
96
retro_set_controller_port_device(unsigned port,unsigned device)97 void retro_set_controller_port_device(unsigned port, unsigned device)
98 {
99 if (port != 0) return;
100
101 memset(inputs, 0, sizeof(inputs));
102 memset(lastinputs, 0, sizeof(lastinputs));
103 memset(mappings, 0, sizeof(mappings));
104 for (unsigned i = 0; i < INPUT_COUNT; ++i)
105 mappings[i] = RETROK_DUMMY;
106
107 if (device == RETRO_DEVICE_KEYBOARD) {
108 // Use the original keybindings
109 controller_device = RETRO_DEVICE_KEYBOARD;
110 mappings[LEFTKEY] = RETROK_LEFT;
111 mappings[RIGHTKEY] = RETROK_RIGHT;
112 mappings[UPKEY] = RETROK_UP;
113 mappings[DOWNKEY] = RETROK_DOWN;
114
115 mappings[JUMPKEY] = RETROK_z;
116 mappings[FIREKEY] = RETROK_x;
117
118 mappings[PREVWPNKEY] = RETROK_a;
119 mappings[NEXTWPNKEY] = RETROK_s;
120
121 mappings[INVENTORYKEY] = RETROK_q;
122 mappings[MAPSYSTEMKEY] = RETROK_w;
123
124 mappings[ESCKEY] = RETROK_ESCAPE;
125 mappings[F1KEY] = RETROK_F1;
126 mappings[F2KEY] = RETROK_F2;
127 mappings[F3KEY] = RETROK_F3;
128 } else {
129 // Use a joypad model in all other cases
130 controller_device = RETRO_DEVICE_JOYPAD;
131 mappings[LEFTKEY] = RETRO_DEVICE_ID_JOYPAD_LEFT;
132 mappings[RIGHTKEY] = RETRO_DEVICE_ID_JOYPAD_RIGHT;
133 mappings[UPKEY] = RETRO_DEVICE_ID_JOYPAD_UP;
134 mappings[DOWNKEY] = RETRO_DEVICE_ID_JOYPAD_DOWN;
135
136 mappings[JUMPKEY] = RETRO_DEVICE_ID_JOYPAD_B;
137 mappings[FIREKEY] = RETRO_DEVICE_ID_JOYPAD_A;
138
139 mappings[PREVWPNKEY] = RETRO_DEVICE_ID_JOYPAD_L;
140 mappings[NEXTWPNKEY] = RETRO_DEVICE_ID_JOYPAD_R;
141
142 mappings[MAPSYSTEMKEY] = RETRO_DEVICE_ID_JOYPAD_X;
143 mappings[INVENTORYKEY] = RETRO_DEVICE_ID_JOYPAD_START;
144
145 mappings[F3KEY] = RETRO_DEVICE_ID_JOYPAD_SELECT;
146 }
147
148 // Declare the bindings to the frontend
149 struct retro_input_descriptor desc[INPUT_COUNT+1];
150 unsigned j = 0;
151 for (unsigned i = 0; i < INPUT_COUNT; ++i)
152 {
153 if (mappings[i] != RETROK_DUMMY)
154 {
155 desc[j].port = 0;
156 desc[j].device = controller_device;
157 desc[j].index = 0;
158 desc[j].id = mappings[i];
159 desc[j].description = input_get_name(i);
160 j++;
161 }
162 }
163
164 desc[j].port = 0;
165 desc[j].device = 0;
166 desc[j].index = 0;
167 desc[j].id = 0;
168 desc[j].description = NULL;
169
170 environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, desc);
171 }
172
retro_get_system_av_info(struct retro_system_av_info * info)173 void retro_get_system_av_info(struct retro_system_av_info *info)
174 {
175 info->geometry.base_width = SCREEN_WIDTH;
176 info->geometry.base_height = SCREEN_HEIGHT;
177 info->geometry.max_width = SCREEN_WIDTH;
178 info->geometry.max_height = SCREEN_HEIGHT;
179 info->timing.fps = 60.0;
180 info->timing.sample_rate = 22050.0;
181 }
182
check_system_specs(void)183 static void check_system_specs(void)
184 {
185 // TODO - when it starts reliably running at fullspeed on PSP, set to 4
186 unsigned level = 5;
187 environ_cb(RETRO_ENVIRONMENT_SET_PERFORMANCE_LEVEL, &level);
188 }
189
retro_init(void)190 void retro_init(void)
191 {
192 struct retro_log_callback log;
193 enum retro_pixel_format rgb565;
194
195 if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log))
196 log_cb = log.log;
197 else
198 log_cb = NULL;
199
200 // initialize joypad mappings
201 retro_set_controller_port_device(0, 1);
202
203 #if defined(FRONTEND_SUPPORTS_RGB565) || defined(ABGR1555)
204 rgb565 = RETRO_PIXEL_FORMAT_RGB565;
205 if(environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &rgb565) && log_cb)
206 log_cb(RETRO_LOG_INFO, "Frontend supports RGB565 - will use that instead of XRGB1555.\n");
207 #endif
208 check_system_specs();
209
210 if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL))
211 libretro_supports_bitmasks = true;
212 }
213
extract_directory(char * buf,const char * path,size_t size)214 static void extract_directory(char *buf, const char *path, size_t size)
215 {
216 char *base;
217 strncpy(buf, path, size - 1);
218 buf[size - 1] = '\0';
219
220 base = strrchr(buf, '/');
221 if (!base)
222 base = strrchr(buf, '\\');
223
224 if (base)
225 *base = '\0';
226 else
227 {
228 buf[0] = '.';
229 buf[1] = '\0';
230 }
231 }
232
retro_load_game(const struct retro_game_info * game)233 bool retro_load_game(const struct retro_game_info *game)
234 {
235 if (!game)
236 return false;
237
238 extract_directory(g_dir, game->path, sizeof(g_dir));
239 NX_LOG("g_dir: %s\n", g_dir);
240
241 retro_init_saves();
242
243 if (pre_main())
244 return false;
245 return true;
246 }
247
retro_deinit(void)248 void retro_deinit(void)
249 {
250 libretro_supports_bitmasks = false;
251 }
252
retro_reset(void)253 void retro_reset(void)
254 {
255 lastinputs[F2KEY] = true;
256 game.reset();
257 }
258
259 void mixaudio(int16_t *stream, size_t len_samples);
260
261 #if 0
262 #include <time.h>
263 static int64_t get_usec(void)
264 {
265 struct timespec tv;
266 clock_gettime(CLOCK_MONOTONIC, &tv);
267 return (int64_t)tv.tv_sec * 1000000 + (int64_t)tv.tv_nsec / 1000;
268 }
269 #endif
270
retro_run(void)271 void retro_run(void)
272 {
273 poll_cb();
274 static unsigned frame_cnt = 0;
275
276 #if 0
277 if (log_cb)
278 log_cb(RETRO_LOG_INFO, "[NX]: Start frame.\n");
279 int64_t start_time = get_usec();
280
281 platform_sync_to_vblank();
282 #endif
283 screen->Flip();
284
285 if (retro_60hz)
286 {
287 //int64_t start_time_frame = get_usec();
288 while (!run_main());
289 #if 0
290 int64_t total_time_frame = get_usec() - start_time_frame;
291 if (log_cb)
292 log_cb(RETRO_LOG_INFO, "[NX]: total_time_frame took %lld usec.\n", (long long)total_time_frame);
293 #endif
294
295 //int64_t start_time_frame_cb = get_usec();
296 video_cb(retro_frame_buffer, retro_frame_buffer_width, retro_frame_buffer_height, retro_frame_buffer_pitch);
297 #if 0
298 int64_t total_time_frame_cb = get_usec() - start_time_frame_cb;
299 if (log_cb)
300 log_cb(RETRO_LOG_INFO, "[NX]: total_time_frame_cb took %lld usec.\n", (long long)total_time_frame_cb);
301 #endif
302
303 frame_cnt++;
304 }
305 else
306 {
307 if (frame_cnt % 6)
308 {
309 while (!run_main());
310 video_cb(retro_frame_buffer, retro_frame_buffer_width, retro_frame_buffer_height, retro_frame_buffer_pitch);
311 }
312 else
313 video_cb(NULL, SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH * sizeof(uint16_t)); // Dupe every 6th frame.
314
315 frame_cnt++;
316 }
317
318 int16_t samples[(2 * 22050) / 60 + 1] = {0};
319
320 // Average audio frames / video frame: 367.5.
321 unsigned frames = (22050 + (frame_cnt & 1 ? 30 : -30)) / 60;
322
323 mixaudio(samples, frames * 2);
324 audio_batch_cb(samples, frames);
325
326 g_frame_cnt++;
327
328 #if 0
329 int64_t total_time = get_usec() - start_time;
330 if (log_cb)
331 log_cb(RETRO_LOG_INFO, "[NX]: Frame took %lld usec.\n", (long long)total_time);
332 #endif
333
334 if (!game.running) environ_cb(RETRO_ENVIRONMENT_SHUTDOWN, NULL);
335 }
336
retro_serialize_size(void)337 size_t retro_serialize_size(void)
338 {
339 return 0;
340 }
341
retro_serialize(void * data,size_t size)342 bool retro_serialize(void *data, size_t size)
343 {
344 return false;
345 }
346
retro_unserialize(const void * data,size_t size)347 bool retro_unserialize(const void *data, size_t size)
348 {
349 return false;
350 }
351
retro_cheat_reset(void)352 void retro_cheat_reset(void) {}
retro_cheat_set(unsigned index,bool enabled,const char * code)353 void retro_cheat_set(unsigned index, bool enabled, const char *code) {}
354
retro_load_game_special(unsigned game_type,const struct retro_game_info * info,size_t num_info)355 bool retro_load_game_special(
356 unsigned game_type,
357 const struct retro_game_info *info, size_t num_info
358 )
359 {
360 return false;
361 }
362
retro_unload_game(void)363 void retro_unload_game (void)
364 {
365 post_main();
366 }
367
retro_get_region(void)368 unsigned retro_get_region(void)
369 {
370 return RETRO_REGION_NTSC;
371 }
372
retro_get_memory_data(unsigned id)373 void * retro_get_memory_data(unsigned id) { return 0; }
retro_get_memory_size(unsigned id)374 size_t retro_get_memory_size(unsigned id) { return 0; }
375
retro_create_subpath_string(char * fname,size_t fname_size,const char * dir,const char * subdir,const char * filename)376 void retro_create_subpath_string(char *fname, size_t fname_size, const char * dir, const char * subdir, const char * filename)
377 {
378 #ifdef _WIN32
379 char slash = '\\';
380 #else
381 char slash = '/';
382 #endif
383 snprintf(fname, fname_size, "%s%c%s%c%s", dir, slash, subdir, slash, filename);
384 }
385
retro_create_path_string(char * fname,size_t fname_size,const char * dir,const char * filename)386 void retro_create_path_string(char *fname, size_t fname_size, const char * dir, const char * filename)
387 {
388 #ifdef _WIN32
389 char slash = '\\';
390 #else
391 char slash = '/';
392 #endif
393 snprintf(fname, fname_size, "%s%c%s", dir, slash, filename);
394 }
395
396 /**
397 * Retrieve the desired save directory.
398 */
retro_get_save_dir()399 const char* retro_get_save_dir()
400 {
401 const char* dir = NULL;
402
403 // Attempt to get the save directory from the frontend.
404 if (environ_cb(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &dir) && dir && *dir) {
405 return dir;
406 }
407
408 // If the save directory isn't available, use the game path.
409 return g_dir;
410 }
411
412 /**
413 * Copy any missing profiles from the content directory to the save directory.
414 */
retro_init_saves()415 void retro_init_saves()
416 {
417 // Copy any profiles into the save directory.
418 const char* save_dir = retro_get_save_dir();
419 char gamedirProfile[1024];
420 char savedirProfile[1024];
421 char profile_name[1024];
422
423 // Copy profiles only if te folders are different.
424 if (strcmp(save_dir, g_dir) != 0) {
425 // Parse through all the different profiles.
426 for (int i = 0; i < 5; i++) {
427 // Create the profile filename.
428 if (i == 0) {
429 snprintf(profile_name, sizeof(profile_name), "profile.dat");
430 }
431 else {
432 snprintf(profile_name, sizeof(profile_name), "profile%d.dat", i + 1);
433 }
434
435 // Get the profile's file path in the game directory.
436 retro_create_path_string(gamedirProfile, sizeof(gamedirProfile), g_dir, profile_name);
437
438 // Make sure the profile exists.
439 if (file_exists(gamedirProfile)) {
440 // Create the profile's file path in the save directory.
441 retro_create_path_string(savedirProfile, sizeof(savedirProfile), save_dir, profile_name);
442
443 // Copy the file to the save directory only if it doesn't exist.
444 if (!file_exists(savedirProfile)) {
445 if (retro_copy_file(gamedirProfile, savedirProfile)) {
446 NX_LOG("Copied profile %s to save directory at %s\n", gamedirProfile, savedirProfile);
447 }
448 else {
449 NX_ERR("Failed to copy profile %s to %s\n", gamedirProfile, savedirProfile);
450 }
451 }
452 }
453 }
454 }
455 }
456
457 /**
458 * Copy a file to the given destination.
459 */
retro_copy_file(const char * from,const char * to)460 bool retro_copy_file(const char* from, const char* to)
461 {
462 // Open the file for reading.
463 FILE *fd1 = fopen(from, "r");
464 if (!fd1) {
465 return false;
466 }
467
468 // Prepare the destination.
469 FILE *fd2 = fopen(to, "w");
470 if(!fd2) {
471 fclose(fd1);
472 return false;
473 }
474
475 // Prepare the buffer.
476 size_t l1;
477 unsigned char buffer[8192];
478
479 // Loop through the from file through the buffer.
480 while((l1 = fread(buffer, 1, sizeof buffer, fd1)) > 0) {
481 // Write the data to the destination file.
482 size_t l2 = fwrite(buffer, 1, l1, fd2);
483
484 // Check if there was an error writing.
485 if (l2 < l1) {
486 // Display an error message.
487 if (ferror(fd2)) {
488 NX_ERR("Error copying profile from %s to %s\n", from, to);
489 }
490 else {
491 NX_ERR("Error copying profile, media full from %s to %s\n", from, to);
492 }
493 return false;
494 }
495 }
496 fclose(fd1);
497 fclose(fd2);
498 return true;
499 }
500