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