lutro.c 18.1 KB
Newer Older
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
1
2
#include "lutro.h"
#include "runtime.h"
3
#include "unzip.h"
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
4

Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
5
#include "image.h"
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
6
7
#include "graphics.h"
#include "input.h"
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
8
#include "audio.h"
RobLoach's avatar
RobLoach committed
9
#include "event.h"
RobLoach's avatar
RobLoach committed
10
#include "keyboard.h"
11
#include "sound.h"
12
#include "filesystem.h"
13
#include "system.h"
14
#include "timer.h"
15
#include "lutro_math.h"
16
#include "lutro_window.h"
Higor Eurípedes's avatar
Higor Eurípedes committed
17
#include "live.h"
RobLoach's avatar
RobLoach committed
18
#include "mouse.h"
RobLoach's avatar
RobLoach committed
19
#include "joystick.h"
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
20

21
22
#include <file/file_path.h>
#include <compat/strl.h>
23
#include <ctype.h>
24

Higor Eurípedes's avatar
Higor Eurípedes committed
25
26
27
28
#ifdef HAVE_JIT
#include "luajit.h"
#endif

RobLoach's avatar
RobLoach committed
29
// LuaUTF8
RobLoach's avatar
RobLoach committed
30
31
#include "deps/luautf8/lutf8lib.h"

RobLoach's avatar
RobLoach committed
32
// LuaSocket
33
#ifdef WANT_LUASOCKET
RobLoach's avatar
RobLoach committed
34
#include "deps/luasocket/luasocket.h"
35
#endif
RobLoach's avatar
RobLoach committed
36

Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
37
38
39
40
41
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <assert.h>
42
43

#if !defined(_MSC_VER)
Libretro-Admin's avatar
Libretro-Admin committed
44
#ifndef __CELLOS_LV2__
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
45
#include <libgen.h>
Libretro-Admin's avatar
Libretro-Admin committed
46
#endif
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
47
#include <unistd.h>
48
#endif
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
49
50

static lua_State *L;
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
51
static int16_t input_cache[16];
Higor Eurípedes's avatar
Higor Eurípedes committed
52

Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
53
54
55
56
57
lutro_settings_t settings = {
   .width = 320,
   .height = 240,
   .pitch = 0,
   .framebuffer = NULL,
Higor Eurípedes's avatar
Higor Eurípedes committed
58
59
   .live_enable = 0,
   .live_call_load = 0,
RobLoach's avatar
RobLoach committed
60
   .input_cb = NULL,
RobLoach's avatar
RobLoach committed
61
62
63
64
   .delta = 0,
   .deltaCounter = 0,
   .frameCounter = 0,
   .fps = 0
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
65
66
};

Toad King's avatar
Toad King committed
67
68
struct retro_perf_callback perf_cb;

69
70
71
72
#if 0
static void dumpstack( lua_State* L )
{
  int top = lua_gettop( L );
73

74
75
76
  for ( int i = 1; i <= top; i++ )
  {
    printf( "%2d %3d ", i, i - top - 1 );
77

78
    lua_pushvalue( L, i );
79

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
    switch ( lua_type( L, -1 ) )
    {
    case LUA_TNIL:
      printf( "nil\n" );
      break;
    case LUA_TNUMBER:
      printf( "%e\n", lua_tonumber( L, -1 ) );
      break;
    case LUA_TBOOLEAN:
      printf( "%s\n", lua_toboolean( L, -1 ) ? "true" : "false" );
      break;
    case LUA_TSTRING:
      printf( "\"%s\"\n", lua_tostring( L, -1 ) );
      break;
    case LUA_TTABLE:
      printf( "table\n" );
      break;
    case LUA_TFUNCTION:
      printf( "function\n" );
      break;
    case LUA_TUSERDATA:
      printf( "userdata\n" );
      break;
    case LUA_TTHREAD:
      printf( "thread\n" );
      break;
    case LUA_TLIGHTUSERDATA:
      printf( "light userdata\n" );
      break;
    default:
      printf( "?\n" );
      break;
    }
  }
114

115
116
117
118
  lua_settop( L, top );
}
#endif

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
int _lutro_assertf_internal(int ignorable, const char *fmt, ...)
{
   va_list argptr;
   va_start(argptr, fmt);
   vfprintf(stderr, fmt, argptr);
   va_end(argptr);
   fflush(NULL);

   // tips: the fmt input should always be in the predefined format of:
   //   FILE(LINE): assertion `cond` failed.
   // example:
   //   lutro.cpp(444): assertion `x > 0` failed.
   // 
   // We can use this knowledge to parse the file and line positions and perform additional clever filtering
   // or log prep/routing.

   if (ignorable)
   {
      // TODO : suspend the core, show up some user dialog via libretro api?
      // (it could even have lots of info, like lua stacktrace... )
      // return 0 if ignored.
   }

   return 1;
}

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
static int lutro_lua_panic (lua_State *L) {
   // currently this aborts, but it is also possible to set up a setjmp/longmp handler for
   // these, as the vast majority of them _are_ recoverable, in the sense that we can usually
   // bounce all the way out to retro_run and continue executing some unrelated system.
   // For example, if keyboard or pad or audio APIs panic, this doesn't have to  stop video,
   // or other portions of the engine, fron continuing on (usually).

   // If implementing longjmp, it would be nice to change this into a play_assert (ignorable).

   fprintf(stderr, "lua_panic!\n%s\n", lua_tostring(L, -1));
   abort();
}

int traceback(lua_State *L) {
   // use lua's provided debug.traceback to get a contextual error.
   lua_getglobal(L, "debug");
   lua_getfield(L, -1, "traceback");
   lua_pushvalue(L, 1);
   lua_pushinteger(L, 2);
   lua_call(L, 2, 1);

   // generally we don't want to stop/assert on lua errors. The majority are ignorable/recoverable
   // and the better strategy is to surface the info the the content creator via some interface
   // that's slightly more friendly than a console window. (eg, something that captures all the spam
   // and produces a summary report of unique errors)
   //tool_errorf("%s\n", lua_tostring(L, -1));

   fprintf(stderr, "%s\n", lua_tostring(L, -1));

Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
174
   return 1;
175
176
}

177
178
179
180
181
182
183
184
int lutro_pcall(lua_State *L, int narg, int nret)
{
   int handler = lua_gettop(L) - narg - 1;
   while (handler && (lua_tocfunction(L, handler) != traceback)) --handler;
   dbg_assert(lua_tocfunction(L, handler) == traceback);
   return lua_pcall(L, narg, nret, handler);
}

185
186
187
static int dofile(lua_State *L, const char *path)
{
   int res;
188

189
   lua_pushcfunction(L, traceback);
190
   res = luaL_loadfile(L, path);
191
   if (res)
192
193
      return res;

194
   res = lutro_pcall(L, 0, LUA_MULTRET);
195
196
197
   return res;
}

Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
static int lutro_core_preload(lua_State *L)
{
   lutro_ensure_global_table(L, "lutro");

   return 1;
}

static void init_settings(lua_State *L)
{
   lutro_ensure_global_table(L, "lutro");

   lua_newtable(L);

   lua_pushnumber(L, settings.width);
   lua_setfield(L, -2, "width");

   lua_pushnumber(L, settings.height);
   lua_setfield(L, -2, "height");

   lua_setfield(L, -2, "settings");

   lua_pop(L, 1);
}

void lutro_init()
{
   L = luaL_newstate();
225
226
227
228

   // handler for errors thatr occur outside pcall() scope
   lua_atpanic(L, &lutro_lua_panic);

Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
229
230
   luaL_openlibs(L);

Higor Eurípedes's avatar
Higor Eurípedes committed
231
232
233
234
#ifdef HAVE_JIT
   luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC|LUAJIT_MODE_ON);
#endif

235
236
   lutro_checked_stack_begin();

Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
237
238
239
   init_settings(L);

   lutro_preload(L, lutro_core_preload, "lutro");
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
240
   lutro_preload(L, lutro_image_preload, "lutro.image");
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
241
   lutro_preload(L, lutro_graphics_preload, "lutro.graphics");
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
242
   lutro_preload(L, lutro_audio_preload, "lutro.audio");
RobLoach's avatar
RobLoach committed
243
   lutro_preload(L, lutro_event_preload, "lutro.event");
244
   lutro_preload(L, lutro_sound_preload, "lutro.sound");
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
245
   lutro_preload(L, lutro_input_preload, "lutro.input");
246
   lutro_preload(L, lutro_filesystem_preload, "lutro.filesystem");
RobLoach's avatar
RobLoach committed
247
   lutro_preload(L, lutro_keyboard_preload, "lutro.keyboard");
248
   lutro_preload(L, lutro_system_preload, "lutro.system");
249
   lutro_preload(L, lutro_timer_preload, "lutro.timer");
RobLoach's avatar
RobLoach committed
250
   lutro_preload(L, lutro_math_preload, "lutro.math");
251
   lutro_preload(L, lutro_window_preload, "lutro.window");
RobLoach's avatar
RobLoach committed
252
   lutro_preload(L, lutro_mouse_preload, "lutro.mouse");
RobLoach's avatar
RobLoach committed
253
   lutro_preload(L, lutro_joystick_preload, "lutro.joystick");
RobLoach's avatar
RobLoach committed
254
255

   // UTF8
RobLoach's avatar
RobLoach committed
256
257
   lutro_preload(L, luaopen_luautf8, "utf8");

RobLoach's avatar
RobLoach committed
258
   // LuaSocket
259
#ifdef WANT_LUASOCKET
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
260
   _o_open(L);
261
#endif
RobLoach's avatar
RobLoach committed
262

263
#ifdef HAVE_INOTIFY
Higor Eurípedes's avatar
Higor Eurípedes committed
264
   lutro_preload(L, lutro_live_preload, "lutro.live");
265
#endif
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
266

267
268
269
   // if any of these requires fail, the checked stack assertion at the end will
   // be triggered. remember that assertions are only avaialable in debug mode.
   lutro_require(L, "lutro", 1);
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
270
   lutro_require(L, "lutro.image", 1);
271
272
   lutro_require(L, "lutro.graphics", 1);
   lutro_require(L, "lutro.audio", 1);
RobLoach's avatar
RobLoach committed
273
   lutro_require(L, "lutro.event", 1);
274
   lutro_require(L, "lutro.sound", 1);
RobLoach's avatar
RobLoach committed
275
   lutro_require(L, "lutro.keyboard", 1);
276
277
   lutro_require(L, "lutro.input", 1);
   lutro_require(L, "lutro.filesystem", 1);
278
   lutro_require(L, "lutro.system", 1);
279
   lutro_require(L, "lutro.timer", 1);
RobLoach's avatar
RobLoach committed
280
   lutro_require(L, "lutro.math", 1);
281
   lutro_require(L, "lutro.window", 1);
RobLoach's avatar
RobLoach committed
282
   lutro_require(L, "lutro.mouse", 1);
RobLoach's avatar
RobLoach committed
283
   lutro_require(L, "lutro.joystick", 1);
284
#ifdef HAVE_INOTIFY
285
   lutro_require(L, "lutro.live", 1);
286
#endif
Higor Eurípedes's avatar
Higor Eurípedes committed
287

RobLoach's avatar
RobLoach committed
288
289
290
291
   // Mirror the lutro namespace to "love".
   luaL_dostring(L, "love = lutro");

   // Initialize the filesystem.
orbea's avatar
orbea committed
292
293
   lutro_filesystem_init();

294
   lutro_checked_stack_assert(0);
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
295
296
297
298
}

void lutro_deinit()
{
299
#ifdef HAVE_INOTIFY
Higor Eurípedes's avatar
Higor Eurípedes committed
300
   if (settings.live_enable)
Higor Eurípedes's avatar
Higor Eurípedes committed
301
      lutro_live_deinit();
302
#endif
Higor Eurípedes's avatar
Higor Eurípedes committed
303

304
   lutro_audio_stop_all(L);
305
306
307
   lua_gc(L, LUA_GCSTEP, 0);
   lua_close(L);

308
   lutro_audio_deinit();
RobLoach's avatar
RobLoach committed
309
   lutro_filesystem_deinit();
310
}
311

312
313
314
315
void lutro_mixer_render(int16_t* buffer)
{
   if (!L) return;
   mixer_render(L, buffer);
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
316
317
}

318
int lutro_set_package_path(lua_State* L, const char* path)
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
319
{
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
320
   const char *cur_path;
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
321
322
323
324
325
326
327
328
329
330
331
332
333
   char new_path[PATH_MAX_LENGTH];
   lua_getglobal(L, "package");
   lua_getfield(L, -1, "path");
   cur_path = lua_tostring( L, -1);
   strlcpy(new_path, cur_path, sizeof(new_path));
   strlcat(new_path, path, sizeof(new_path));
   lua_pop(L, 1);
   lua_pushstring(L, new_path) ;
   lua_setfield(L, -2, "path");
   lua_pop(L, 1);
   return 1;
}

334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
int lutro_unzip(const char *path, const char *extraction_directory)
{
   path_mkdir(extraction_directory);

   unzFile *zipfile = unzOpen(path);
   if ( zipfile == NULL )
   {
      printf("%s: not found\n", path);
      return -1;
   }

   unz_global_info global_info;
   if (unzGetGlobalInfo(zipfile, &global_info) != UNZ_OK)
   {
      printf("could not read file global info\n");
      unzClose(zipfile);
      return -1;
   }

   char read_buffer[8192];

   uLong i;
   for (i = 0; i < global_info.number_entry; ++i)
   {
      unz_file_info file_info;
      char filename[PATH_MAX_LENGTH];
      if (unzGetCurrentFileInfo(zipfile, &file_info, filename, PATH_MAX_LENGTH,
         NULL, 0, NULL, 0 ) != UNZ_OK)
      {
         printf( "could not read file info\n" );
         unzClose( zipfile );
         return -1;
      }

      const size_t filename_length = strlen(filename);
      if (filename[filename_length-1] == '/')
      {
         printf("dir:%s\n", filename);
         char abs_path[PATH_MAX_LENGTH];
         fill_pathname_join(abs_path,
               extraction_directory, filename, sizeof(abs_path));
         path_mkdir(abs_path);
      }
      else
      {
         printf("file:%s\n", filename);
         if (unzOpenCurrentFile(zipfile) != UNZ_OK)
         {
            printf("could not open file\n");
            unzClose(zipfile);
            return -1;
         }

         char abs_path[PATH_MAX_LENGTH];
         fill_pathname_join(abs_path,
               extraction_directory, filename, sizeof(abs_path));
         FILE *out = fopen(abs_path, "wb");
         if (out == NULL)
         {
            printf("could not open destination file\n");
            unzCloseCurrentFile(zipfile);
            unzClose(zipfile);
            return -1;
         }

         int error = UNZ_OK;
         do
         {
            error = unzReadCurrentFile(zipfile, read_buffer, 8192);
            if (error < 0)
            {
               printf("error %d\n", error);
               unzCloseCurrentFile(zipfile);
               unzClose(zipfile);
               return -1;
            }

            if (error > 0)
               fwrite(read_buffer, error, 1, out);

         } while (error > 0);

         fclose(out);
      }

      unzCloseCurrentFile(zipfile);

      if (i + 1  < global_info.number_entry)
      {
         if (unzGoToNextFile(zipfile) != UNZ_OK)
         {
            printf("cound not read next file\n");
            unzClose(zipfile);
            return -1;
         }
      }
   }

   unzClose(zipfile);
   return 0;
}

Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
436
437
int lutro_load(const char *path)
{
Higor Eurípedes's avatar
Higor Eurípedes committed
438
   char mainfile[PATH_MAX_LENGTH];
RobLoach's avatar
RobLoach committed
439
440
   // conf.lua https://love2d.org/wiki/Config_Files
   char conffile[PATH_MAX_LENGTH];
441
   char gamedir[PATH_MAX_LENGTH];
Higor Eurípedes's avatar
Higor Eurípedes committed
442

Higor Eurípedes's avatar
Higor Eurípedes committed
443
   strlcpy(mainfile, path, PATH_MAX_LENGTH);
RobLoach's avatar
RobLoach committed
444
   strlcpy(conffile, path, PATH_MAX_LENGTH);
445
   strlcpy(gamedir, path, PATH_MAX_LENGTH);
Higor Eurípedes's avatar
Higor Eurípedes committed
446

RobLoach's avatar
RobLoach committed
447
   if (path_is_directory(mainfile)) {
Higor Eurípedes's avatar
Higor Eurípedes committed
448
      fill_pathname_join(mainfile, gamedir, "main.lua", sizeof(mainfile));
RobLoach's avatar
RobLoach committed
449
450
451
      fill_pathname_join(conffile, gamedir, "conf.lua", sizeof(conffile));
   }
   else {
Higor Eurípedes's avatar
Higor Eurípedes committed
452
      path_basedir(gamedir);
RobLoach's avatar
RobLoach committed
453
   }
Higor Eurípedes's avatar
Higor Eurípedes committed
454

RobLoach's avatar
RobLoach committed
455
   // Loading a .lutro file.
Higor Eurípedes's avatar
Higor Eurípedes committed
456
   if (!strcmp(path_get_extension(mainfile), "lutro"))
457
   {
458
      fill_pathname(gamedir, mainfile, "/", sizeof(gamedir));
RobLoach's avatar
RobLoach committed
459
      fill_pathname(gamedir, conffile, "/", sizeof(gamedir));
460
      lutro_unzip(mainfile, gamedir);
Higor Eurípedes's avatar
Higor Eurípedes committed
461
      fill_pathname_join(mainfile, gamedir, "main.lua", sizeof(mainfile));
RobLoach's avatar
RobLoach committed
462
      fill_pathname_join(conffile, gamedir, "conf.lua", sizeof(conffile));
463
   }
RobLoach's avatar
RobLoach committed
464
465
466
467
   else {
      // Loading a main.lua file, so construct the config file.
      fill_pathname_join(conffile, gamedir, "conf.lua", sizeof(conffile));
   }
468

469
470
   fill_pathname_slash(gamedir, sizeof(gamedir));

Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
471
   char package_path[PATH_MAX_LENGTH];
472
   snprintf(package_path, PATH_MAX_LENGTH, ";%s?.lua;%s?/init.lua", gamedir, gamedir);
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
473
474
   lutro_set_package_path(L, package_path);

RobLoach's avatar
RobLoach committed
475
476
477
478
   // Load the configuration file, ignoring any errors.
   dofile(L, conffile);

   // Now that configuration is in place, load main.lua.
RobLoach's avatar
RobLoach committed
479
   if (dofile(L, mainfile))
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
480
481
482
483
484
485
486
   {
       fprintf(stderr, "%s\n", lua_tostring(L, -1));
       lua_pop(L, 1);

       return 0;
   }

487
488
   int oldtop = lua_gettop(L);
   lua_pushcfunction(L, traceback);
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
489
490
   lua_getglobal(L, "lutro");

491
492
   int tbl_top_lutro = lua_gettop(L);

493
   strlcpy(settings.gamedir, gamedir, PATH_MAX_LENGTH);
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
494
495
496

   lua_getfield(L, -1, "conf");

497
498
   // Process the custom configuration, if it exists.
   if (lua_isfunction(L, -1))
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
499
500
501
   {
      lua_getfield(L, -2, "settings");

502
      if(lutro_pcall(L, 1, 0))
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
503
504
505
506
507
508
509
      {
         fprintf(stderr, "%s\n", lua_tostring(L, -1));
         lua_pop(L, 1);

         return 0;
      }

510
511
      // no stack cleanup necessary inside oldtop scope.

Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
512
513
514
      lua_getfield(L, -1, "settings");

      lua_getfield(L, -1, "width");
515
516
517
      lua_getfield(L, -2, "height");
      lua_getfield(L, -3, "live_enable");
      lua_getfield(L, -4, "live_call_load");
Higor Eurípedes's avatar
Higor Eurípedes committed
518

519
520
521
      settings.width          = lua_tointeger(L, -4);
      settings.height         = lua_tointeger(L, -3);
      settings.live_enable    = lua_toboolean(L, -2);
Higor Eurípedes's avatar
Higor Eurípedes committed
522
      settings.live_call_load = lua_toboolean(L, -1);
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
523
524
   }

525
   lutro_graphics_init(L);
526
   lutro_audio_init(L);
RobLoach's avatar
RobLoach committed
527
   lutro_event_init();
RobLoach's avatar
RobLoach committed
528
   lutro_math_init();
RobLoach's avatar
RobLoach committed
529
   lutro_joystick_init();
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
530

531
#ifdef HAVE_INOTIFY
Higor Eurípedes's avatar
Higor Eurípedes committed
532
   if (settings.live_enable)
Higor Eurípedes's avatar
Higor Eurípedes committed
533
      lutro_live_init();
534
#endif
535
   lua_settop(L, tbl_top_lutro);
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
536
537
   lua_getfield(L, -1, "load");

538
   int result = 1;
RobLoach's avatar
RobLoach committed
539
   // Check if lutro.load() exists.
540
   if (lua_isfunction(L, -1))
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
541
   {
RobLoach's avatar
RobLoach committed
542
      // It exists, so call lutro.load().
543
      if(lutro_pcall(L, 0, 0))
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
544
545
546
      {
         fprintf(stderr, "%s\n", lua_tostring(L, -1));
         lua_pop(L, 1);
547
         result = 0;
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
548
549
550
      }
   }

551
   lua_settop(L, oldtop);
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
552
553
554
   return 1;
}

555
556
void lutro_gamepadevent(lua_State* L)
{
557
   int oldtop = lua_gettop(L);
558
   unsigned i;
559

560
561
562
563
564
565
566
567
   for (i = 0; i < 16; i++)
   {
      int16_t is_down = settings.input_cb(0, RETRO_DEVICE_JOYPAD, 0, i);
      if (is_down != input_cache[i])
      {
         lua_getfield(L, -1, is_down ? "gamepadpressed" : "gamepadreleased");
         if (lua_isfunction(L, -1))
         {
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
568
            lua_pushnumber(L, i);
569
            lua_pushstring(L, input_find_name(joystick_enum, i));
570
            if (lutro_pcall(L, 2, 0))
571
572
573
574
575
576
            {
               fprintf(stderr, "%s\n", lua_tostring(L, -1));
               lua_pop(L, 1);
            }
            input_cache[i] = is_down;
         }
577
         lua_pop(L, 1);
578
579
      }
   }
580
   lua_settop(L, oldtop);
581
582
}

Higor Eurípedes's avatar
Higor Eurípedes committed
583
void lutro_run(double delta)
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
584
{
RobLoach's avatar
RobLoach committed
585
   // Update the Delta and FPS.
RobLoach's avatar
RobLoach committed
586
   settings.delta = delta;
RobLoach's avatar
RobLoach committed
587
588
   settings.deltaCounter += delta;
   settings.frameCounter += 1;
RobLoach's avatar
RobLoach committed
589
   if (settings.deltaCounter >= 1.0) {
RobLoach's avatar
RobLoach committed
590
591
592
593
594
      settings.fps = settings.frameCounter;
      settings.frameCounter = 0;
      settings.deltaCounter = 0;
   }

595
#ifdef HAVE_INOTIFY
Higor Eurípedes's avatar
Higor Eurípedes committed
596
597
   if (settings.live_enable)
      lutro_live_update(L);
598
#endif
Higor Eurípedes's avatar
Higor Eurípedes committed
599

600
601
   int oldtop = lua_gettop(L);
   lua_pushcfunction(L, traceback);
602

Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
603
604
605
606
607
   lua_getglobal(L, "lutro");
   lua_getfield(L, -1, "update");
   if (lua_isfunction(L, -1))
   {
      lua_pushnumber(L, delta);
608

609
      if(lutro_pcall(L, 1, 0))
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
610
611
612
613
614
615
616
617
618
      {
         fprintf(stderr, "%s\n", lua_tostring(L, -1));
         lua_pop(L, 1);
      }
   }

   lua_getfield(L, -1, "draw");
   if (lua_isfunction(L, -1))
   {
619
      lutro_graphics_begin_frame(L);
620

621
      if(lutro_pcall(L, 0, 0))
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
622
623
624
625
      {
         fprintf(stderr, "%s\n", lua_tostring(L, -1));
         lua_pop(L, 1);
      }
626
      lutro_graphics_end_frame(L);
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
627
628
   }

RobLoach's avatar
RobLoach committed
629
   lutro_keyboardevent(L);
630
   lutro_gamepadevent(L);
RobLoach's avatar
RobLoach committed
631
   lutro_mouseevent(L);
RobLoach's avatar
RobLoach committed
632
   lutro_joystickevent(L);
633

634
   lua_settop(L, oldtop);
635

636
   mixer_unref_stopped_sounds(L);
637
   lua_gc(L, LUA_GCSTEP, 0);
Jean-Andre Santoni's avatar
Jean-Andre Santoni committed
638
}
639
640
641

void lutro_reset()
{
642
643
644
   int oldtop = lua_gettop(L);

   lua_pushcfunction(L, traceback);
645
646
647
648
649
650

   lua_getglobal(L, "lutro");
   lua_getfield(L, -1, "reset");

   if (lua_isfunction(L, -1))
   {
651
      lutro_audio_stop_all(L);
652
      if(lutro_pcall(L, 0, 0))
653
654
655
656
657
658
      {
         fprintf(stderr, "%s\n", lua_tostring(L, -1));
         lua_pop(L, 1);
      }
   }

659
   lua_settop(L, oldtop);
660
661
662
663
664
665
   lua_gc(L, LUA_GCSTEP, 0);
}

size_t lutro_serialize_size()
{
   size_t size = 0;
666
   int oldtop = lua_gettop(L);
667

668
   lua_pushcfunction(L, traceback);
669
670
671
672
673
674

   lua_getglobal(L, "lutro");
   lua_getfield(L, -1, "serializeSize");

   if (lua_isfunction(L, -1))
   {
675
      if (lutro_pcall(L, 0, 1))
676
677
678
679
680
      {
         fprintf(stderr, "%s\n", lua_tostring(L, -1));
         lua_pop(L, 1);
      }

681
682
683
684
      if (lua_isnumber(L, -1))
         size = lua_tonumber(L, -1);
      else
         tool_assertf(false, "Invalid type returned from lutro.serializeSize. An integer result is expected.\n");
685
   }
686

687
   lua_settop(L, oldtop);
688
   lua_gc(L, LUA_GCSTEP, 0);
689
690
691
692
693
694

   return size;
}

bool lutro_serialize(void *data_, size_t size)
{
695
696
   int oldtop = lua_gettop(L);
   lua_pushcfunction(L, traceback);
697
698
699
700
701
702
703

   lua_getglobal(L, "lutro");
   lua_getfield(L, -1, "serialize");

   if (lua_isfunction(L, -1))
   {
      lua_pushnumber(L, size);
704
      if (lutro_pcall(L, 1, 1))
705
706
707
708
      {
         fprintf(stderr, "%s\n", lua_tostring(L, -1));
         lua_pop(L, 1);
      }
709
710
711
712
      else
      {
         const char* data = lua_tostring(L, -1);
         lua_pop(L, 1);
713

714
715
716
         memset(data_, 0, size);
         memcpy(data_, data, strlen(data));
      }
717
718
   }

719
   lua_settop(L, oldtop);
720
721
722
723
724
725
726
   lua_gc(L, LUA_GCSTEP, 0);

   return true;
}

bool lutro_unserialize(const void *data_, size_t size)
{
727
728
   int oldtop = lua_gettop(L);
   lua_pushcfunction(L, traceback);
729
730
731
732
733
734
735
736

   lua_getglobal(L, "lutro");
   lua_getfield(L, -1, "unserialize");

   if (lua_isfunction(L, -1))
   {
      lua_pushstring(L, data_);
      lua_pushnumber(L, size);
737
      if (lutro_pcall(L, 2, 0))
738
739
740
741
742
743
      {
         fprintf(stderr, "%s\n", lua_tostring(L, -1));
         lua_pop(L, 1);
      }
   }

744
   lua_settop(L, oldtop);
745
746
747
   lua_gc(L, LUA_GCSTEP, 0);

   return true;
748
}
749
750
751
752
753
754
755
756
757
758
759
760
761

void lutro_assetPath_init(AssetPathInfo* dest, const char* path)
{
   assert (dest);

   strlcpy(dest->fullpath, settings.gamedir, sizeof(dest->fullpath));
   strlcat(dest->fullpath, path, sizeof(dest->fullpath));

   //get file extension
   strcpy(dest->ext, path_get_extension(path));
   for(int i = 0; dest->ext[i]; i++)
      dest->ext[i] = tolower((uint8_t)dest->ext[i]);
}