1 #include <libretro.h>
2 
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdarg.h>
6 
7 #include <gwlua.h>
8 
9 /*---------------------------------------------------------------------------*/
10 
dummy_log(enum retro_log_level level,const char * fmt,...)11 static void dummy_log( enum retro_log_level level, const char* fmt, ... )
12 {
13   (void)level;
14   (void)fmt;
15 }
16 
17 #define SRAM_MAX 8
18 
19 typedef struct
20 {
21   char types[ SRAM_MAX ];
22   char keys[ SRAM_MAX ][ 32 ];
23   char values[ SRAM_MAX ][ 64 ];
24   char count;
25 }
26 sram_t;
27 
28 retro_log_printf_t log_cb = dummy_log;
29 retro_environment_t env_cb;
30 static retro_video_refresh_t video_cb;
31 static retro_audio_sample_batch_t audio_cb;
32 static retro_input_poll_t input_poll_cb;
33 static retro_input_state_t input_state_cb;
34 
35 static int     init;
36 static gwrom_t rom;
37 static gwlua_t state;
38 static sram_t  sram;
39 static int     offset;
40 static int     soft_width;
41 static int     soft_height;
42 
43 static struct retro_input_descriptor input_descriptors[] =
44 {
45   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" },
46   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" },
47   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
48   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" },
49   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
50   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
51   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "X" },
52   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "Y" },
53   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "L1" },
54   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "R1" },
55   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "L2" },
56   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "R2" },
57   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L3, "L3" },
58   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R3, "R3" },
59   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
60   { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
61   // TODO: Is this needed?
62   { 255, 255, 255, 255, NULL }
63 };
64 
65 /*---------------------------------------------------------------------------*/
66 /* gwlua user-defined functions */
67 
find_key(const char * key)68 static int find_key( const char* key )
69 {
70   for ( int i = 0; i < sram.count; i++ )
71   {
72     if ( !strcmp( sram.keys[ i ], key ) )
73     {
74       return i;
75     }
76   }
77 
78   return -1;
79 }
80 
gwlua_load_value(gwlua_t * state,const char * key,int * type)81 const char* gwlua_load_value( gwlua_t* state, const char* key, int* type )
82 {
83   int i = find_key( key );
84 
85   if ( i != -1 )
86   {
87     *type = sram.types[ i ];
88     return sram.values[ i ];
89   }
90 
91   return NULL;
92 }
93 
gwlua_save_value(gwlua_t * state,const char * key,const char * value,int type)94 void gwlua_save_value( gwlua_t* state, const char* key, const char* value, int type )
95 {
96   int i = find_key( key );
97 
98   if ( i == -1 )
99   {
100     if ( sram.count == SRAM_MAX )
101     {
102       /* TODO: return an error when SRAM is full */
103       log_cb( RETRO_LOG_ERROR, "Out of space writing <%s, %s> to SRAM\n", key, value );
104       return;
105     }
106 
107     i = sram.count++;
108   }
109 
110   sram.types[ i ] = type;
111 
112   strncpy( sram.keys[ i ], key, sizeof( sram.keys[ i ] ) );
113   sram.keys[ i ][ sizeof( sram.keys[ i ] ) - 1 ] = 0;
114 
115   strncpy( sram.values[ i ], value, sizeof( sram.values[ i ] ) );
116   sram.values[ i ][ sizeof( sram.values[ i ] ) - 1 ] = 0;
117 }
118 
gwlua_set_fb(unsigned width,unsigned height)119 int gwlua_set_fb( unsigned width, unsigned height )
120 {
121   struct retro_game_geometry geometry;
122 
123   geometry.base_width = width;
124   geometry.base_height = height;
125   geometry.max_width = width;
126   geometry.max_height = height;
127   geometry.aspect_ratio = 0.0f;
128 
129   env_cb( RETRO_ENVIRONMENT_SET_GEOMETRY, &geometry );
130 
131   offset = 0;
132   soft_width = width;
133   soft_height = height;
134 
135   return 0;
136 }
137 
gwlua_zoom(gwlua_t * state,int x0,int y0,int width,int height)138 void gwlua_zoom( gwlua_t* state, int x0, int y0, int width, int height )
139 {
140   struct retro_game_geometry geometry;
141 
142   if ( x0 >= 0 )
143   {
144     geometry.base_width = width;
145     geometry.base_height = height;
146     soft_width = width;
147     soft_height = height;
148     offset = y0 * state->width + x0;
149   }
150   else
151   {
152     geometry.base_width = state->width;
153     geometry.base_height = state->height;
154     soft_width = state->width;
155     soft_height = state->height;
156     offset = 0;
157   }
158 
159   geometry.max_width = state->width;
160   geometry.max_height = state->height;
161   geometry.aspect_ratio = 0.0f;
162 
163   env_cb( RETRO_ENVIRONMENT_SET_GEOMETRY, &geometry );
164 }
165 
gwlua_vlog(const char * format,va_list args)166 void gwlua_vlog( const char* format, va_list args )
167 {
168   char buffer[ 8192 ]; /* should be enough */
169 
170   vsnprintf( buffer, sizeof( buffer ), format, args );
171   buffer[ sizeof( buffer ) - 1 ] = 0;
172   log_cb( RETRO_LOG_ERROR, "%s", buffer );
173 }
174 
175 /*---------------------------------------------------------------------------*/
176 /* compatibility functions */
177 
178 #ifdef __CELLOS_LV2__
179 
getenv(const char * name)180 char* getenv( const char* name )
181 {
182   (void)name;
183   return NULL;
184 }
185 
186 #endif
187 
188 /*---------------------------------------------------------------------------*/
189 
190 extern const char* gw_version;
191 extern const char* gw_hithash;
192 
retro_get_system_info(struct retro_system_info * info)193 void retro_get_system_info( struct retro_system_info* info )
194 {
195   info->library_name = "Game & Watch";
196   info->library_version = gw_version;
197   info->need_fullpath = false;
198   info->block_extract = false;
199   info->valid_extensions = "mgw";
200 }
201 
retro_set_environment(retro_environment_t cb)202 void retro_set_environment( retro_environment_t cb )
203 {
204   env_cb = cb;
205 
206   static const struct retro_variable vars[] = {
207     { NULL, NULL },
208   };
209 
210   static const struct retro_controller_description controllers[] = {
211     { "Controller", RETRO_DEVICE_JOYPAD },
212     // TODO: Is this needed?
213     { NULL, 0 }
214   };
215 
216   static const struct retro_controller_description pointers[] = {
217     { "Pointer", RETRO_DEVICE_POINTER },
218     // TODO: Is this needed?
219     { NULL, 0 }
220   };
221 
222   static const struct retro_controller_info ports[] = {
223     { controllers, 1 },
224     { controllers, 1 },
225     { pointers, 1 },
226     { NULL, 0 }
227   };
228 
229   cb( RETRO_ENVIRONMENT_SET_VARIABLES, (void*)vars );
230   cb( RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports );
231 }
232 
retro_api_version()233 unsigned retro_api_version()
234 {
235   return RETRO_API_VERSION;
236 }
237 
retro_init()238 void retro_init()
239 {
240   struct retro_log_callback log;
241 
242   if ( env_cb( RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &log ) )
243     log_cb = log.log;
244 }
245 
246 extern const char* gw_gitstamp;
247 extern const char* rl_gitstamp;
248 
retro_load_game(const struct retro_game_info * info)249 bool retro_load_game( const struct retro_game_info* info )
250 {
251   enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_RGB565;
252 
253   if (!info)
254      return false;
255 
256   if ( !env_cb( RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt ) )
257   {
258     log_cb( RETRO_LOG_ERROR, "RGB565 is not supported\n" );
259     return false;
260   }
261 
262 
263   log_cb( RETRO_LOG_INFO, "\n%s\n%s", gw_gitstamp, rl_gitstamp );
264 
265   int res = gwrom_init( &rom, (void*)info->data, info->size, GWROM_COPY_ALWAYS );
266 
267   if ( res != GWROM_OK )
268   {
269     log_cb( RETRO_LOG_ERROR, "Error initializing the rom: ", gwrom_error_message( res ) );
270     init = -1;
271     return false;
272   }
273 
274   env_cb( RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, input_descriptors );
275   memset( (void*)&state, 0, sizeof( state ) );
276   state.width = state.height = 128;
277   init = 0;
278   return true;
279 }
280 
retro_get_memory_size(unsigned id)281 size_t retro_get_memory_size( unsigned id )
282 {
283   return id == RETRO_MEMORY_SAVE_RAM ? sizeof( sram ) : 0;
284 }
285 
retro_get_memory_data(unsigned id)286 void* retro_get_memory_data( unsigned id )
287 {
288   return id == RETRO_MEMORY_SAVE_RAM ? (void*)&sram : NULL;
289 }
290 
retro_set_video_refresh(retro_video_refresh_t cb)291 void retro_set_video_refresh( retro_video_refresh_t cb )
292 {
293   video_cb = cb;
294 }
295 
retro_set_audio_sample(retro_audio_sample_t cb)296 void retro_set_audio_sample( retro_audio_sample_t cb )
297 {
298   (void)cb;
299 }
300 
retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)301 void retro_set_audio_sample_batch( retro_audio_sample_batch_t cb )
302 {
303   audio_cb = cb;
304 }
305 
retro_set_input_state(retro_input_state_t cb)306 void retro_set_input_state( retro_input_state_t cb )
307 {
308   input_state_cb = cb;
309 }
310 
retro_set_input_poll(retro_input_poll_t cb)311 void retro_set_input_poll( retro_input_poll_t cb )
312 {
313   input_poll_cb = cb;
314 }
315 
retro_get_system_av_info(struct retro_system_av_info * info)316 void retro_get_system_av_info( struct retro_system_av_info* info )
317 {
318   info->geometry.base_width = state.width;
319   info->geometry.base_height = state.height;
320   info->geometry.max_width = state.width;
321   info->geometry.max_height = state.height;
322   info->geometry.aspect_ratio = 0.0f;
323   info->timing.fps = 60.0;
324   info->timing.sample_rate = 44100.0;
325 }
326 
retro_run()327 void retro_run()
328 {
329   static const struct { unsigned retro; int gw; } map[] =
330   {
331     { RETRO_DEVICE_ID_JOYPAD_UP,     GWLUA_UP },
332     { RETRO_DEVICE_ID_JOYPAD_DOWN,   GWLUA_DOWN },
333     { RETRO_DEVICE_ID_JOYPAD_LEFT,   GWLUA_LEFT },
334     { RETRO_DEVICE_ID_JOYPAD_RIGHT,  GWLUA_RIGHT },
335     { RETRO_DEVICE_ID_JOYPAD_A,      GWLUA_A },
336     { RETRO_DEVICE_ID_JOYPAD_B,      GWLUA_B },
337     { RETRO_DEVICE_ID_JOYPAD_X,      GWLUA_X },
338     { RETRO_DEVICE_ID_JOYPAD_Y,      GWLUA_Y },
339     { RETRO_DEVICE_ID_JOYPAD_L,      GWLUA_L1 },
340     { RETRO_DEVICE_ID_JOYPAD_R,      GWLUA_R1 },
341     { RETRO_DEVICE_ID_JOYPAD_L2,     GWLUA_L2 },
342     { RETRO_DEVICE_ID_JOYPAD_R2,     GWLUA_R2 },
343     { RETRO_DEVICE_ID_JOYPAD_L3,     GWLUA_L3 },
344     { RETRO_DEVICE_ID_JOYPAD_R3,     GWLUA_R3 },
345     { RETRO_DEVICE_ID_JOYPAD_SELECT, GWLUA_SELECT },
346     { RETRO_DEVICE_ID_JOYPAD_START,  GWLUA_START },
347   };
348 
349   input_poll_cb();
350 
351   if ( init == 0 )
352   {
353     /* Initialize game */
354     if ( gwlua_create( &state, &rom ) )
355     {
356       log_cb( RETRO_LOG_ERROR, "Error inializing gwlua" );
357       init = -1;
358       return;
359     }
360 
361     struct retro_system_av_info info;
362     retro_get_system_av_info( &info );
363     env_cb( RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &info );
364 
365     init = 1;
366   }
367   else if ( init == -1 )
368   {
369     /* Error, return */
370     return;
371   }
372   else
373   {
374     /* erase sprites here to avoid blank screenshots */
375     rl_sprites_unblit();
376   }
377 
378   /* Run game */
379   unsigned id;
380   int16_t x, y, pressed;
381 
382   for ( id = 0; id < sizeof( map ) / sizeof( map [ 0 ] ); id++ )
383   {
384     pressed = input_state_cb( 0, RETRO_DEVICE_JOYPAD, 0, map[ id ].retro );
385     gwlua_set_button( &state, 0, map[ id ].gw, pressed != 0 );
386 
387     pressed = input_state_cb( 1, RETRO_DEVICE_JOYPAD, 0, map[ id ].retro );
388     gwlua_set_button( &state, 1, map[ id ].gw, pressed != 0 );
389   }
390 
391   x = input_state_cb( 2, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_X );
392   y = input_state_cb( 2, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_Y );
393   pressed = input_state_cb( 2, RETRO_DEVICE_POINTER, 0, RETRO_DEVICE_ID_POINTER_PRESSED );
394   gwlua_set_pointer( &state, x, y, pressed != 0 );
395 
396   gwlua_tick( &state );
397   rl_sprites_blit();
398 
399   video_cb( state.screen + offset, soft_width, soft_height, state.width * sizeof( uint16_t ) );
400   audio_cb( rl_sound_mix(), RL_SAMPLES_PER_FRAME );
401 }
402 
retro_deinit()403 void retro_deinit()
404 {
405 }
406 
retro_set_controller_port_device(unsigned port,unsigned device)407 void retro_set_controller_port_device( unsigned port, unsigned device )
408 {
409   (void)port;
410   (void)device;
411 }
412 
retro_reset()413 void retro_reset()
414 {
415   gwlua_reset( &state );
416 }
417 
retro_serialize_size()418 size_t retro_serialize_size()
419 {
420   return 0;
421 }
422 
retro_serialize(void * data,size_t size)423 bool retro_serialize( void* data, size_t size )
424 {
425   (void)data;
426   (void)size;
427   return false;
428 }
429 
retro_unserialize(const void * data,size_t size)430 bool retro_unserialize( const void* data, size_t size )
431 {
432   (void)data;
433   (void)size;
434   return false;
435 }
436 
retro_cheat_reset()437 void retro_cheat_reset()
438 {
439 }
440 
retro_cheat_set(unsigned a,bool b,const char * c)441 void retro_cheat_set( unsigned a, bool b, const char* c )
442 {
443   (void)a;
444   (void)b;
445   (void)c;
446 }
447 
retro_load_game_special(unsigned a,const struct retro_game_info * b,size_t c)448 bool retro_load_game_special(unsigned a, const struct retro_game_info* b, size_t c)
449 {
450   (void)a;
451   (void)b;
452   (void)c;
453   return false;
454 }
455 
retro_unload_game()456 void retro_unload_game()
457 {
458   gwlua_destroy( &state );
459   gwrom_destroy( &rom );
460 }
461 
retro_get_region()462 unsigned retro_get_region()
463 {
464   return RETRO_REGION_NTSC;
465 }
466