1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 /*
24  * This code is based on Broken Sword 2.5 engine
25  *
26  * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
27  *
28  * Licensed under GNU GPL v2
29  *
30  */
31 
32 #include "sword25/kernel/common.h"
33 #include "sword25/kernel/kernel.h"
34 #include "sword25/kernel/filesystemutil.h"
35 #include "sword25/kernel/resmanager.h"
36 #include "sword25/kernel/persistenceservice.h"
37 #include "sword25/script/script.h"
38 #include "sword25/script/luabindhelper.h"
39 
40 namespace Sword25 {
41 
42 // Marks a function that should never be used
dummyFuncError(lua_State * L)43 static int dummyFuncError(lua_State *L) {
44 	error("Dummy function invoked by LUA");
45 	return 1;
46 }
47 
getMilliTicks(lua_State * L)48 static int getMilliTicks(lua_State *L) {
49 	Kernel *pKernel = Kernel::getInstance();
50 	assert(pKernel);
51 
52 	lua_pushnumber(L, pKernel->getMilliTicks());
53 
54 	return 1;
55 }
56 
getTimer(lua_State * L)57 static int getTimer(lua_State *L) {
58 	Kernel *pKernel = Kernel::getInstance();
59 	assert(pKernel);
60 
61 	lua_pushnumber(L, static_cast<lua_Number>(pKernel->getMilliTicks()) / 1000.0);
62 
63 	return 1;
64 }
65 
startService(lua_State * L)66 static int startService(lua_State *L) {
67 	// This function is used by system/boot.lua to init all services.
68 	// However, we do nothing here, as we just hard code the init sequence.
69 	lua_pushbooleancpp(L, true);
70 
71 	return 1;
72 }
73 
sleep(lua_State * L)74 static int sleep(lua_State *L) {
75 	Kernel *pKernel = Kernel::getInstance();
76 	assert(pKernel);
77 	pKernel->sleep(static_cast<uint>(luaL_checknumber(L, 1) * 1000));
78 	return 0;
79 }
80 
crash(lua_State * L)81 static int crash(lua_State *L) {
82 	Kernel *pKernel = Kernel::getInstance();
83 	assert(pKernel);
84 	pKernel->crash();
85 	return 0;
86 }
87 
executeFile(lua_State * L)88 static int executeFile(lua_State *L) {
89 	Kernel *pKernel = Kernel::getInstance();
90 	assert(pKernel);
91 	ScriptEngine *pSE = pKernel->getScript();
92 	assert(pSE);
93 
94 	lua_pushbooleancpp(L, pSE->executeFile(luaL_checkstring(L, 1)));
95 
96 	return 0;
97 }
98 
getUserdataDirectory(lua_State * L)99 static int getUserdataDirectory(lua_State *L) {
100 	lua_pushstring(L, FileSystemUtil::getUserdataDirectory().c_str());
101 	return 1;
102 }
103 
getPathSeparator(lua_State * L)104 static int getPathSeparator(lua_State *L) {
105 	lua_pushstring(L, FileSystemUtil::getPathSeparator().c_str());
106 	return 1;
107 }
108 
fileExists(lua_State * L)109 static int fileExists(lua_State *L) {
110 	lua_pushbooleancpp(L, FileSystemUtil::fileExists(luaL_checkstring(L, 1)));
111 	return 1;
112 }
113 
createDirectory(lua_State * L)114 static int createDirectory(lua_State *L) {
115 	// ScummVM engines cannot create directories, so we do nothing here.
116 	lua_pushbooleancpp(L, false);
117 	return 1;
118 }
119 
getWinCode(lua_State * L)120 static int getWinCode(lua_State *L) {
121 	lua_pushstring(L, "ScummVM");
122 	return 1;
123 }
124 
getSubversionRevision(lua_State * L)125 static int getSubversionRevision(lua_State *L) {
126 	// ScummVM is 1337
127 	lua_pushnumber(L, 1337);
128 	return 1;
129 }
130 
getUsedMemory(lua_State * L)131 static int getUsedMemory(lua_State *L) {
132 	// It doesn't really matter what this call returns,
133 	// as it's used in a debug function.
134 	lua_pushnumber(L, 0);
135 	return 1;
136 }
137 
138 static const char *KERNEL_LIBRARY_NAME = "Kernel";
139 
140 static const luaL_reg KERNEL_FUNCTIONS[] = {
141 	{"DisconnectService", dummyFuncError},
142 	{"GetActiveServiceIdentifier", dummyFuncError},
143 	{"GetSuperclassCount", dummyFuncError},
144 	{"GetSuperclassIdentifier", dummyFuncError},
145 	{"GetServiceCount", dummyFuncError},
146 	{"GetServiceIdentifier", dummyFuncError},
147 	{"GetMilliTicks", getMilliTicks},
148 	{"GetTimer", getTimer},
149 	{"StartService", startService},
150 	{"Sleep", sleep},
151 	{"Crash", crash},
152 	{"ExecuteFile", executeFile},
153 	{"GetUserdataDirectory", getUserdataDirectory},
154 	{"GetPathSeparator", getPathSeparator},
155 	{"FileExists", fileExists},
156 	{"CreateDirectory", createDirectory},
157 	{"GetWinCode", getWinCode},
158 	{"GetSubversionRevision", getSubversionRevision},
159 	{"GetUsedMemory", getUsedMemory},
160 	{0, 0}
161 };
162 
isVisible(lua_State * L)163 static int isVisible(lua_State *L) {
164 	// This function apparently is not used by the game scripts
165 	lua_pushbooleancpp(L, true);
166 
167 	return 1;
168 }
169 
setVisible(lua_State * L)170 static int setVisible(lua_State *L) {
171 	// This function apparently is not used by the game scripts
172 //	pWindow->setVisible(lua_tobooleancpp(L, 1));
173 
174 	return 0;
175 }
176 
getX(lua_State * L)177 static int getX(lua_State *L) {
178 	// This function apparently is not used by the game scripts
179 	lua_pushnumber(L, 0);
180 
181 	return 1;
182 }
183 
getY(lua_State * L)184 static int getY(lua_State *L) {
185 	// This function apparently is not used by the game scripts
186 	lua_pushnumber(L, 0);
187 
188 	return 1;
189 }
190 
setX(lua_State * L)191 static int setX(lua_State *L) {
192 	// This is called by system/boot.lua with -1 as value.
193 //	pWindow->setX(static_cast<int>(luaL_checknumber(L, 1)));
194 
195 	return 0;
196 }
197 
setY(lua_State * L)198 static int setY(lua_State *L) {
199 	// This is called by system/boot.lua with -1 as value.
200 //	pWindow->setY(static_cast<int>(luaL_checknumber(L, 1)));
201 
202 	return 0;
203 }
204 
getWidth(lua_State * L)205 static int getWidth(lua_State *L) {
206 	// This function apparently is not used by the game scripts
207 	lua_pushnumber(L, 800);
208 
209 	return 1;
210 }
211 
getHeight(lua_State * L)212 static int getHeight(lua_State *L) {
213 	// This function apparently is not used by the game scripts
214 	lua_pushnumber(L, 600);
215 
216 	return 1;
217 }
218 
setWidth(lua_State * L)219 static int setWidth(lua_State *L) {
220 	// This is called by system/boot.lua with 800 as value.
221 //	pWindow->setWidth(static_cast<int>(luaL_checknumber(L, 1)));
222 
223 	return 0;
224 }
225 
setHeight(lua_State * L)226 static int setHeight(lua_State *L) {
227 	// This is called by system/boot.lua with 600 as value.
228 //	pWindow->setHeight(static_cast<int>(luaL_checknumber(L, 1)));
229 
230 	return 0;
231 }
232 
getTitle(lua_State * L)233 static int getTitle(lua_State *L) {
234 	// This function apparently is not used by the game scripts
235 	lua_pushstring(L, "");
236 
237 	return 1;
238 }
239 
setTitle(lua_State * L)240 static int setTitle(lua_State *L) {
241 	// This is called by system/boot.lua and system/menu.lua, to
242 	// set the window title to the (localized) game name.
243 	// FIXME: Should we call OSystem::setWindowCaption() here?
244 //	pWindow->setTitle(luaL_checkstring(L, 1));
245 
246 	return 0;
247 }
248 
processMessages(lua_State * L)249 static int processMessages(lua_State *L) {
250 	// This is called by the main loop in system/boot.lua,
251 	// and the game keeps running as true is returned here.
252 	// It terminates if we return false.
253 
254 	// TODO: We could do more stuff here if desired...
255 
256 	// TODO: We could always return true here, and leave quit handling
257 	// to the closeWanted() opcode; see also the TODO comment in there.
258 
259 	lua_pushbooleancpp(L, !Engine::shouldQuit());
260 	g_system->delayMillis(10);
261 
262 	return 1;
263 }
264 
closeWanted(lua_State * L)265 static int closeWanted(lua_State *L) {
266 	// This is called by system/interface.lua to determine whether the
267 	// user requested the game to close (e.g. by clicking the 'close' widget
268 	// of the game window). As a consequence (i.e. this function returns true),
269 	// a quit confirmation dialog is shown.
270 
271 	// TODO: ScummVM currently has a bug / misfeature where some engines provide
272 	// quit confirmation dialogs, some don't; in addition, we have a global confirmation
273 	// dialog (but the user has to explicitly activate that in the config).
274 	// Anyway, this can lead to *two* confirmation dialogs being shown.
275 	// If it wasn't for that, we could simply check for Engine::shouldQuit() here,
276 	// and then invoke EventMan::resetQuit. But currently this would result in
277 	// the user seeing two confirmation dialogs. Bad.
278 	lua_pushbooleancpp(L, false);
279 
280 	return 1;
281 }
282 
283 static const char *WINDOW_LIBRARY_NAME = "Window";
284 
285 static const luaL_reg WINDOW_FUNCTIONS[] = {
286 	{"IsVisible", isVisible},
287 	{"SetVisible", setVisible},
288 	{"GetX", getX},
289 	{"SetX", setX},
290 	{"GetY", getY},
291 	{"SetY", setY},
292 	{"GetClientX", dummyFuncError},
293 	{"GetClientY", dummyFuncError},
294 	{"GetWidth", getWidth},
295 	{"GetHeight", getHeight},
296 	{"SetWidth", setWidth},
297 	{"SetHeight", setHeight},
298 	{"GetTitle", getTitle},
299 	{"SetTitle", setTitle},
300 	{"ProcessMessages", processMessages},
301 	{"CloseWanted", closeWanted},
302 	{"WaitForFocus", dummyFuncError},
303 	{"HasFocus", dummyFuncError},
304 	{0, 0}
305 };
306 
precacheResource(lua_State * L)307 static int precacheResource(lua_State *L) {
308 	Kernel *pKernel = Kernel::getInstance();
309 	assert(pKernel);
310 	ResourceManager *pResource = pKernel->getResourceManager();
311 	assert(pResource);
312 
313 #ifdef PRECACHE_RESOURCES
314 	lua_pushbooleancpp(L, pResource->precacheResource(luaL_checkstring(L, 1)));
315 #else
316 	lua_pushbooleancpp(L, true);
317 #endif
318 
319 	return 1;
320 }
321 
forcePrecacheResource(lua_State * L)322 static int forcePrecacheResource(lua_State *L) {
323 	Kernel *pKernel = Kernel::getInstance();
324 	assert(pKernel);
325 	ResourceManager *pResource = pKernel->getResourceManager();
326 	assert(pResource);
327 
328 #ifdef PRECACHE_RESOURCES
329 	lua_pushbooleancpp(L, pResource->precacheResource(luaL_checkstring(L, 1), true));
330 #else
331 	lua_pushbooleancpp(L, true);
332 #endif
333 
334 	return 1;
335 }
336 
getMaxMemoryUsage(lua_State * L)337 static int getMaxMemoryUsage(lua_State *L) {
338 	Kernel *pKernel = Kernel::getInstance();
339 	assert(pKernel);
340 	ResourceManager *pResource = pKernel->getResourceManager();
341 	assert(pResource);
342 
343 	// This is used for debugging, so it doesn't really matter.
344 	// The default value set by the scripts is 256000000 bytes
345 	lua_pushnumber(L, 256000000);
346 
347 	return 1;
348 }
349 
setMaxMemoryUsage(lua_State * L)350 static int setMaxMemoryUsage(lua_State *L) {
351 	Kernel *pKernel = Kernel::getInstance();
352 	assert(pKernel);
353 	ResourceManager *pResource = pKernel->getResourceManager();
354 	assert(pResource);
355 
356 	// This call is ignored, we set a limit on the number of
357 	// simultaneous resources loaded instead.
358 
359 	return 0;
360 }
361 
emptyCache(lua_State * L)362 static int emptyCache(lua_State *L) {
363 	Kernel *pKernel = Kernel::getInstance();
364 	assert(pKernel);
365 	ResourceManager *pResource = pKernel->getResourceManager();
366 	assert(pResource);
367 
368 	pResource->emptyCache();
369 
370 	return 0;
371 }
372 
dumpLockedResources(lua_State * L)373 static int dumpLockedResources(lua_State *L) {
374 	Kernel *pKernel = Kernel::getInstance();
375 	assert(pKernel);
376 	ResourceManager *pResource = pKernel->getResourceManager();
377 	assert(pResource);
378 
379 	pResource->dumpLockedResources();
380 
381 	return 0;
382 }
383 
384 static const char *RESOURCE_LIBRARY_NAME = "Resource";
385 
386 static const luaL_reg RESOURCE_FUNCTIONS[] = {
387 	{"PrecacheResource", precacheResource},
388 	{"ForcePrecacheResource", forcePrecacheResource},
389 	{"GetMaxMemoryUsage", getMaxMemoryUsage},
390 	{"SetMaxMemoryUsage", setMaxMemoryUsage},
391 	{"EmptyCache", emptyCache},
392 	{"IsLogCacheMiss", dummyFuncError},
393 	{"SetLogCacheMiss", dummyFuncError},
394 	{"DumpLockedResources", dumpLockedResources},
395 	{0, 0}
396 };
397 
reloadSlots(lua_State * L)398 static int reloadSlots(lua_State *L) {
399 	PersistenceService::getInstance().reloadSlots();
400 	lua_pushnil(L);
401 	return 1;
402 }
403 
getSlotCount(lua_State * L)404 static int getSlotCount(lua_State *L) {
405 	lua_pushnumber(L, PersistenceService::getInstance().getSlotCount());
406 	return 1;
407 }
408 
isSlotOccupied(lua_State * L)409 static int isSlotOccupied(lua_State *L) {
410 	lua_pushbooleancpp(L, PersistenceService::getInstance().isSlotOccupied(static_cast<uint>(luaL_checknumber(L, 1)) - 1));
411 	return 1;
412 }
413 
getSavegameDirectory(lua_State * L)414 static int getSavegameDirectory(lua_State *L) {
415 	lua_pushstring(L, PersistenceService::getInstance().getSavegameDirectory().c_str());
416 	return 1;
417 }
418 
isSavegameCompatible(lua_State * L)419 static int isSavegameCompatible(lua_State *L) {
420 	lua_pushbooleancpp(L, PersistenceService::getInstance().isSavegameCompatible(
421 	                       static_cast<uint>(luaL_checknumber(L, 1)) - 1));
422 	return 1;
423 }
424 
getSavegameDescription(lua_State * L)425 static int getSavegameDescription(lua_State *L) {
426 	lua_pushstring(L, PersistenceService::getInstance().getSavegameDescription(
427 	                   static_cast<uint>(luaL_checknumber(L, 1)) - 1).c_str());
428 	return 1;
429 }
430 
getSavegameFilename(lua_State * L)431 static int getSavegameFilename(lua_State *L) {
432 	lua_pushstring(L, PersistenceService::getInstance().getSavegameFilename(static_cast<uint>(luaL_checknumber(L, 1)) - 1).c_str());
433 	return 1;
434 }
435 
loadGame(lua_State * L)436 static int loadGame(lua_State *L) {
437 	lua_pushbooleancpp(L, PersistenceService::getInstance().loadGame(static_cast<uint>(luaL_checknumber(L, 1)) - 1));
438 	return 1;
439 }
440 
saveGame(lua_State * L)441 static int saveGame(lua_State *L) {
442 	lua_pushbooleancpp(L, PersistenceService::getInstance().saveGame(static_cast<uint>(luaL_checknumber(L, 1)) - 1, luaL_checkstring(L, 2)));
443 	return 1;
444 }
445 
446 static const char *PERSISTENCE_LIBRARY_NAME = "Persistence";
447 
448 static const luaL_reg PERSISTENCE_FUNCTIONS[] = {
449 	{"ReloadSlots", reloadSlots},
450 	{"GetSlotCount", getSlotCount},
451 	{"IsSlotOccupied", isSlotOccupied},
452 	{"GetSavegameDirectory", getSavegameDirectory},
453 	{"IsSavegameCompatible", isSavegameCompatible},
454 	{"GetSavegameDescription", getSavegameDescription},
455 	{"GetSavegameFilename", getSavegameFilename},
456 	{"LoadGame", loadGame},
457 	{"SaveGame", saveGame},
458 	{0, 0}
459 };
460 
registerScriptBindings()461 bool Kernel::registerScriptBindings() {
462 	ScriptEngine *pScript = getScript();
463 	assert(pScript);
464 	lua_State *L = static_cast<lua_State *>(pScript->getScriptObject());
465 	assert(L);
466 
467 	if (!LuaBindhelper::addFunctionsToLib(L, KERNEL_LIBRARY_NAME, KERNEL_FUNCTIONS)) return false;
468 	if (!LuaBindhelper::addFunctionsToLib(L, WINDOW_LIBRARY_NAME, WINDOW_FUNCTIONS)) return false;
469 	if (!LuaBindhelper::addFunctionsToLib(L, RESOURCE_LIBRARY_NAME, RESOURCE_FUNCTIONS)) return false;
470 	if (!LuaBindhelper::addFunctionsToLib(L, PERSISTENCE_LIBRARY_NAME, PERSISTENCE_FUNCTIONS)) return false;
471 
472 	return true;
473 }
474 
475 } // End of namespace Sword25
476