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