1 //=============================================================================
2 //
3 // Adventure Game Studio (AGS)
4 //
5 // Copyright (C) 1999-2011 Chris Jones and 2011-20xx others
6 // The full list of copyright holders can be found in the Copyright.txt
7 // file, which is part of this source code distribution.
8 //
9 // The AGS source code is provided under the Artistic License 2.0.
10 // A copy of this license can be found in the file License.txt and at
11 // http://www.opensource.org/licenses/artistic-license-2.0.php
12 //
13 //=============================================================================
14 
15 #include <vector>
16 
17 #include "util/wgt2allg.h"
18 #include "ac/common.h"
19 #include "ac/roomstruct.h"
20 #include "ac/view.h"
21 #include "ac/charactercache.h"
22 #include "ac/display.h"
23 #include "ac/draw.h"
24 #include "ac/dynamicsprite.h"
25 #include "ac/gamesetup.h"
26 #include "ac/gamesetupstruct.h"
27 #include "ac/global_audio.h"
28 #include "ac/global_plugin.h"
29 #include "ac/global_walkablearea.h"
30 #include "ac/keycode.h"
31 #include "ac/mouse.h"
32 #include "ac/movelist.h"
33 #include "ac/objectcache.h"
34 #include "ac/parser.h"
35 #include "ac/path_helper.h"
36 #include "ac/record.h"
37 #include "ac/roomstatus.h"
38 #include "ac/string.h"
39 #include "font/fonts.h"
40 #include "util/string_utils.h"
41 #include "debug/debug_log.h"
42 #include "debug/debugger.h"
43 #include "device/mousew32.h"
44 #include "gui/guidefines.h"
45 #include "main/game_run.h"
46 #include "main/engine.h"
47 #include "media/audio/audio.h"
48 #include "media/audio/sound.h"
49 #include "plugin/agsplugin.h"
50 #include "plugin/plugin_engine.h"
51 #include "plugin/pluginobjectreader.h"
52 #include "script/script.h"
53 #include "script/script_runtime.h"
54 #include "ac/spritecache.h"
55 #include "util/stream.h"
56 #include "gfx/bitmap.h"
57 #include "gfx/graphicsdriver.h"
58 #include "gfx/gfxfilter.h"
59 #include "script/runtimescriptvalue.h"
60 #include "debug/out.h"
61 #include "ac/dynobj/scriptstring.h"
62 #include "main/graphics_mode.h"
63 #include "gfx/gfx_util.h"
64 
65 using namespace AGS::Common;
66 using namespace AGS::Engine;
67 
68 
69 #if defined(BUILTIN_PLUGINS)
70 #include "../Plugins/AGSflashlight/agsflashlight.h"
71 #include "../Plugins/agsblend/agsblend.h"
72 #include "../Plugins/ags_snowrain/ags_snowrain.h"
73 #include "../Plugins/ags_parallax/ags_parallax.h"
74 #include "../Plugins/agspalrender/agspalrender.h"
75 #if defined(IOS_VERSION)
76 #include "../Plugins/agstouch/agstouch.h"
77 #endif // IOS_VERSION
78 #endif // BUILTIN_PLUGINS
79 
80 #if defined(MAC_VERSION)
81 extern "C"
82 {
83     int osx_sys_question(AL_CONST char *msg, AL_CONST char *but1, AL_CONST char *but2);
84 }
85 #endif
86 
87 #if defined(PSP_VERSION)
88 #include <pspsdk.h>
89 #include <pspkernel.h>
90 extern "C"
91 {
92 #include <systemctrl.h>
93 }
94 #include "../PSP/kernel/kernel.h"
95 #endif
96 
97 
98 
99 extern IGraphicsDriver *gfxDriver;
100 extern int mousex, mousey;
101 extern int displayed_room;
102 extern roomstruct thisroom;
103 extern GameSetupStruct game;
104 extern RoomStatus*croom;
105 extern SpriteCache spriteset;
106 extern ViewStruct*views;
107 extern int game_paused;
108 extern int spritewidth[MAX_SPRITES],spriteheight[MAX_SPRITES];
109 extern GameSetup usetup;
110 extern int inside_script;
111 extern ccInstance *gameinst, *roominst;
112 extern CharacterCache *charcache;
113 extern ObjectCache objcache[MAX_INIT_SPR];
114 extern MoveList *mls;
115 extern int numlines;
116 extern char lines[MAXLINE][200];
117 extern color palette[256];
118 extern int offsetx, offsety;
119 extern PluginObjectReader pluginReaders[MAX_PLUGIN_OBJECT_READERS];
120 extern int numPluginReaders;
121 extern RuntimeScriptValue GlobalReturnValue;
122 extern ScriptString myScriptStringImpl;
123 
124 // **************** PLUGIN IMPLEMENTATION ****************
125 
126 
127 #include "util/library.h"
128 
129 
130 
131 
132 struct EnginePlugin {
133     char        filename[PLUGIN_FILENAME_MAX+1];
134     AGS::Engine::Library   library;
135     bool       available;
136     char       *savedata;
137     int         savedatasize;
138     int         wantHook;
139     int         invalidatedRegion;
140     void      (*engineStartup) (IAGSEngine *);
141     void      (*engineShutdown) ();
142     int       (*onEvent) (int, int);
143     void      (*initGfxHook) (const char *driverName, void *data);
144     int       (*debugHook) (const char * whichscript, int lineNumber, int reserved);
145     IAGSEngine  eiface;
146     bool        builtin;
147 
EnginePluginEnginePlugin148     EnginePlugin() {
149         filename[0] = 0;
150         wantHook = 0;
151         invalidatedRegion = 0;
152         savedata = NULL;
153         builtin = false;
154         available = false;
155     }
156 };
157 #define MAXPLUGINS 20
158 EnginePlugin plugins[MAXPLUGINS];
159 int numPlugins = 0;
160 int pluginsWantingDebugHooks = 0;
161 
162 std::vector<InbuiltPluginDetails> _registered_builtin_plugins;
163 
AbortGame(const char * reason)164 void IAGSEngine::AbortGame (const char *reason) {
165     quit ((char*)reason);
166 }
GetEngineVersion()167 const char* IAGSEngine::GetEngineVersion () {
168     return get_engine_version();
169 }
RegisterScriptFunction(const char * name,void * addy)170 void IAGSEngine::RegisterScriptFunction (const char*name, void*addy) {
171     ccAddExternalPluginFunction (name, addy);
172 }
GetGraphicsDriverID()173 const char* IAGSEngine::GetGraphicsDriverID()
174 {
175     if (gfxDriver == NULL)
176         return NULL;
177 
178     return gfxDriver->GetDriverID();
179 }
180 
GetScreen()181 BITMAP *IAGSEngine::GetScreen ()
182 {
183     if (!gfxDriver->UsesMemoryBackBuffer())
184         quit("!This plugin is not compatible with the Direct3D driver.");
185 
186 	return (BITMAP*)BitmapHelper::GetScreenBitmap()->GetAllegroBitmap();
187 }
GetVirtualScreen()188 BITMAP *IAGSEngine::GetVirtualScreen ()
189 {
190 	// [IKM] Aaahh... this is very dangerous, but what can we do?
191 	return (BITMAP*)gfxDriver->GetMemoryBackBuffer()->GetAllegroBitmap();
192 }
RequestEventHook(int32 event)193 void IAGSEngine::RequestEventHook (int32 event) {
194     if (event >= AGSE_TOOHIGH)
195         quit("!IAGSEngine::RequestEventHook: invalid event requested");
196 
197     if (plugins[this->pluginId].onEvent == NULL)
198         quit("!IAGSEngine::RequestEventHook: no callback AGS_EngineOnEvent function exported from plugin");
199 
200     if ((event & AGSE_SCRIPTDEBUG) &&
201         ((plugins[this->pluginId].wantHook & AGSE_SCRIPTDEBUG) == 0)) {
202             pluginsWantingDebugHooks++;
203             ccSetDebugHook(scriptDebugHook);
204     }
205 
206     if (event & AGSE_AUDIODECODE) {
207         quit("Plugin requested AUDIODECODE, which is no longer supported");
208     }
209 
210 
211     plugins[this->pluginId].wantHook |= event;
212 }
213 
UnrequestEventHook(int32 event)214 void IAGSEngine::UnrequestEventHook(int32 event) {
215     if (event >= AGSE_TOOHIGH)
216         quit("!IAGSEngine::UnrequestEventHook: invalid event requested");
217 
218     if ((event & AGSE_SCRIPTDEBUG) &&
219         (plugins[this->pluginId].wantHook & AGSE_SCRIPTDEBUG)) {
220             pluginsWantingDebugHooks--;
221             if (pluginsWantingDebugHooks < 1)
222                 ccSetDebugHook(NULL);
223     }
224 
225     plugins[this->pluginId].wantHook &= ~event;
226 }
227 
GetSavedData(char * buffer,int32 bufsize)228 int IAGSEngine::GetSavedData (char *buffer, int32 bufsize) {
229     int savedatasize = plugins[this->pluginId].savedatasize;
230 
231     if (bufsize < savedatasize)
232         quit("!IAGSEngine::GetSavedData: buffer too small");
233 
234     if (savedatasize > 0)
235         memcpy (buffer, plugins[this->pluginId].savedata, savedatasize);
236 
237     return savedatasize;
238 }
DrawText(int32 x,int32 y,int32 font,int32 color,char * text)239 void IAGSEngine::DrawText (int32 x, int32 y, int32 font, int32 color, char *text)
240 {
241     Bitmap *ds = gfxDriver->GetMemoryBackBuffer();
242     color_t text_color = ds->GetCompatibleColor(color);
243     draw_and_invalidate_text(ds, x, y, font, text_color, text);
244 }
GetScreenDimensions(int32 * width,int32 * height,int32 * coldepth)245 void IAGSEngine::GetScreenDimensions (int32 *width, int32 *height, int32 *coldepth) {
246     if (width != NULL)
247         width[0] = play.viewport.GetWidth();
248     if (height != NULL)
249         height[0] = play.viewport.GetHeight();
250     if (coldepth != NULL)
251         coldepth[0] = scsystem.coldepth;
252 }
GetRawBitmapSurface(BITMAP * bmp)253 unsigned char ** IAGSEngine::GetRawBitmapSurface (BITMAP *bmp) {
254     if (!is_linear_bitmap (bmp))
255         quit("!IAGSEngine::GetRawBitmapSurface: invalid bitmap for access to surface");
256     acquire_bitmap (bmp);
257 
258     if (bmp == gfxDriver->GetMemoryBackBuffer()->GetAllegroBitmap())
259         plugins[this->pluginId].invalidatedRegion = 0;
260 
261     return bmp->line;
262 }
ReleaseBitmapSurface(BITMAP * bmp)263 void IAGSEngine::ReleaseBitmapSurface (BITMAP *bmp) {
264     release_bitmap (bmp);
265 
266     if (bmp == gfxDriver->GetMemoryBackBuffer()->GetAllegroBitmap()) {
267         // plugin does not manaually invalidate stuff, so
268         // we must invalidate the whole screen to be safe
269         if (!plugins[this->pluginId].invalidatedRegion)
270             invalidate_screen();
271     }
272 }
GetMousePosition(int32 * x,int32 * y)273 void IAGSEngine::GetMousePosition (int32 *x, int32 *y) {
274     if (x) x[0] = mousex;
275     if (y) y[0] = mousey;
276 }
GetCurrentRoom()277 int IAGSEngine::GetCurrentRoom () {
278     return displayed_room;
279 }
GetNumBackgrounds()280 int IAGSEngine::GetNumBackgrounds () {
281     return thisroom.num_bscenes;
282 }
GetCurrentBackground()283 int IAGSEngine::GetCurrentBackground () {
284     return play.bg_frame;
285 }
GetBackgroundScene(int32 index)286 BITMAP *IAGSEngine::GetBackgroundScene (int32 index) {
287     return (BITMAP*)thisroom.ebscene[index]->GetAllegroBitmap();
288 }
GetBitmapDimensions(BITMAP * bmp,int32 * width,int32 * height,int32 * coldepth)289 void IAGSEngine::GetBitmapDimensions (BITMAP *bmp, int32 *width, int32 *height, int32 *coldepth) {
290     if (bmp == NULL)
291         return;
292 
293     if (width != NULL)
294         width[0] = bmp->w;
295     if (height != NULL)
296         height[0] = bmp->h;
297     if (coldepth != NULL)
298         coldepth[0] = bitmap_color_depth(bmp);
299 }
300 
301 // On save/restore, the Engine will provide the plugin with a handle. Because we only ever save to one file at a time,
302 // we can reuse the same handle.
303 
304 static long pl_file_handle = -1;
305 static Stream *pl_file_stream = NULL;
306 
pl_set_file_handle(long data,Stream * stream)307 void pl_set_file_handle(long data, Stream *stream) {
308     pl_file_handle = data;
309     pl_file_stream = stream;
310 }
311 
pl_clear_file_handle()312 void pl_clear_file_handle() {
313     pl_file_handle = -1;
314     pl_file_stream = NULL;
315 }
316 
FRead(void * buffer,int32 len,int32 handle)317 int IAGSEngine::FRead (void *buffer, int32 len, int32 handle) {
318     if (handle != pl_file_handle) {
319         quitprintf("IAGSEngine::FRead: invalid file handle: %d", handle);
320     }
321     if (!pl_file_stream) {
322         quit("IAGSEngine::FRead: file stream not set");
323     }
324     return pl_file_stream->Read(buffer, len);
325 }
326 
FWrite(void * buffer,int32 len,int32 handle)327 int IAGSEngine::FWrite (void *buffer, int32 len, int32 handle) {
328     if (handle != pl_file_handle) {
329         quitprintf("IAGSEngine::FWrite: invalid file handle: %d", handle);
330     }
331     if (!pl_file_stream) {
332         quit("IAGSEngine::FWrite: file stream not set");
333     }
334     return pl_file_stream->Write(buffer, len);
335 }
DrawTextWrapped(int32 xx,int32 yy,int32 wid,int32 font,int32 color,const char * text)336 void IAGSEngine::DrawTextWrapped (int32 xx, int32 yy, int32 wid, int32 font, int32 color, const char*text) {
337     // TODO: use generic function from the engine instead of having copy&pasted code here
338     int linespacing = getfontspacing_outlined(font);
339 
340     break_up_text_into_lines (wid, font, (char*)text);
341 
342     Bitmap *ds = gfxDriver->GetMemoryBackBuffer();
343     color_t text_color = ds->GetCompatibleColor(color);
344     multiply_up_coordinates((int*)&xx, (int*)&yy); // stupid! quick tweak
345     for (int i = 0; i < numlines; i++)
346         draw_and_invalidate_text(ds, xx, yy + linespacing*i, font, text_color, lines[i]);
347 }
SetVirtualScreen(BITMAP * bmp)348 void IAGSEngine::SetVirtualScreen (BITMAP *bmp) {
349 	// [IKM] Very, very dangerous :'(
350     // TODO: this won't work with hardware-accelerated renderers
351     SetVirtualScreenRaw (bmp);
352 }
LookupParserWord(const char * word)353 int IAGSEngine::LookupParserWord (const char *word) {
354     return find_word_in_dictionary ((char*)word);
355 }
BlitBitmap(int32 x,int32 y,BITMAP * bmp,int32 masked)356 void IAGSEngine::BlitBitmap (int32 x, int32 y, BITMAP *bmp, int32 masked) {
357     wputblock_raw (gfxDriver->GetMemoryBackBuffer(), x, y, bmp, masked);
358     invalidate_rect(x, y, x + bmp->w, y + bmp->h);
359 }
BlitSpriteTranslucent(int32 x,int32 y,BITMAP * bmp,int32 trans)360 void IAGSEngine::BlitSpriteTranslucent(int32 x, int32 y, BITMAP *bmp, int32 trans) {
361     Bitmap wrap(bmp, true);
362     if (gfxDriver->UsesMemoryBackBuffer())
363         GfxUtil::DrawSpriteWithTransparency(gfxDriver->GetMemoryBackBuffer(), &wrap, x, y, trans);
364     else
365         GfxUtil::DrawSpriteBlend(gfxDriver->GetMemoryBackBuffer(), Point(x,y), &wrap, kBlendMode_Alpha, true, false, trans);
366 }
BlitSpriteRotated(int32 x,int32 y,BITMAP * bmp,int32 angle)367 void IAGSEngine::BlitSpriteRotated(int32 x, int32 y, BITMAP *bmp, int32 angle) {
368     Common::Bitmap *ds = gfxDriver->GetMemoryBackBuffer();
369     // FIXME: call corresponding Graphics Blit
370     rotate_sprite(ds->GetAllegroBitmap(), bmp, x, y, itofix(angle));
371 }
372 
373 extern void domouse(int);
374 extern int  mgetbutton();
375 
PollSystem()376 void IAGSEngine::PollSystem () {
377 
378     NEXT_ITERATION();
379     domouse(DOMOUSE_NOCURSOR);
380     update_polled_stuff_if_runtime();
381     int mbut = mgetbutton();
382     if (mbut > NONE)
383         pl_run_plugin_hooks (AGSE_MOUSECLICK, mbut);
384 
385     int kp;
386     if (run_service_key_controls(kp)) {
387         pl_run_plugin_hooks (AGSE_KEYPRESS, kp);
388     }
389 
390 }
GetCharacter(int32 charnum)391 AGSCharacter* IAGSEngine::GetCharacter (int32 charnum) {
392     if (charnum >= game.numcharacters)
393         quit("!AGSEngine::GetCharacter: invalid character request");
394 
395     return (AGSCharacter*)&game.chars[charnum];
396 }
GetGameOptions()397 AGSGameOptions* IAGSEngine::GetGameOptions () {
398     return (AGSGameOptions*)&play;
399 }
GetPalette()400 AGSColor* IAGSEngine::GetPalette () {
401     return (AGSColor*)&palette[0];
402 }
SetPalette(int32 start,int32 finish,AGSColor * cpl)403 void IAGSEngine::SetPalette (int32 start, int32 finish, AGSColor *cpl) {
404     set_palette_range((color*)cpl, start, finish, 0);
405 }
GetNumCharacters()406 int IAGSEngine::GetNumCharacters () {
407     return game.numcharacters;
408 }
GetPlayerCharacter()409 int IAGSEngine::GetPlayerCharacter () {
410     return game.playercharacter;
411 }
RoomToViewport(int32 * x,int32 * y)412 void IAGSEngine::RoomToViewport (int32 *x, int32 *y) {
413     if (x)
414         x[0] = multiply_up_coordinate(x[0]) - offsetx;
415     if (y)
416         y[0] = multiply_up_coordinate(y[0]) - offsety;
417 }
ViewportToRoom(int32 * x,int32 * y)418 void IAGSEngine::ViewportToRoom (int32 *x, int32 *y) {
419     if (x)
420         x[0] = divide_down_coordinate(x[0] + offsetx);
421     if (y)
422         y[0] = divide_down_coordinate(y[0] + offsety);
423 }
GetNumObjects()424 int IAGSEngine::GetNumObjects () {
425     return croom->numobj;
426 }
GetObject(int32 num)427 AGSObject *IAGSEngine::GetObject (int32 num) {
428     if (num >= croom->numobj)
429         quit("!IAGSEngine::GetObject: invalid object");
430 
431     return (AGSObject*)&croom->obj[num];
432 }
CreateBlankBitmap(int32 width,int32 height,int32 coldep)433 BITMAP *IAGSEngine::CreateBlankBitmap (int32 width, int32 height, int32 coldep) {
434 	// [IKM] We should not create Bitmap object here, because
435 	// a) we are returning raw allegro bitmap and therefore loosing control over it
436 	// b) plugin won't use Bitmap anyway
437     BITMAP *tempb = create_bitmap_ex(coldep, width, height);
438     clear_to_color(tempb, bitmap_mask_color(tempb));
439     return tempb;
440 }
FreeBitmap(BITMAP * tofree)441 void IAGSEngine::FreeBitmap (BITMAP *tofree) {
442     if (tofree)
443         destroy_bitmap (tofree);
444 }
GetSpriteGraphic(int32 num)445 BITMAP *IAGSEngine::GetSpriteGraphic (int32 num) {
446     return (BITMAP*)spriteset[num]->GetAllegroBitmap();
447 }
GetRoomMask(int32 index)448 BITMAP *IAGSEngine::GetRoomMask (int32 index) {
449     if (index == MASK_WALKABLE)
450         return (BITMAP*)thisroom.walls->GetAllegroBitmap();
451     else if (index == MASK_WALKBEHIND)
452         return (BITMAP*)thisroom.object->GetAllegroBitmap();
453     else if (index == MASK_HOTSPOT)
454         return (BITMAP*)thisroom.lookat->GetAllegroBitmap();
455     else if (index == MASK_REGIONS)
456         return (BITMAP*)thisroom.regions->GetAllegroBitmap();
457     else
458         quit("!IAGSEngine::GetRoomMask: invalid mask requested");
459     return NULL;
460 }
GetViewFrame(int32 view,int32 loop,int32 frame)461 AGSViewFrame *IAGSEngine::GetViewFrame (int32 view, int32 loop, int32 frame) {
462     view--;
463     if ((view < 0) || (view >= game.numviews))
464         quit("!IAGSEngine::GetViewFrame: invalid view");
465     if ((loop < 0) || (loop >= views[view].numLoops))
466         quit("!IAGSEngine::GetViewFrame: invalid loop");
467     if ((frame < 0) || (frame >= views[view].loops[loop].numFrames))
468         return NULL;
469 
470     return (AGSViewFrame*)&views[view].loops[loop].frames[frame];
471 }
GetRawPixelColor(int32 color)472 int IAGSEngine::GetRawPixelColor (int32 color) {
473     // Convert the standardized colour to the local gfx mode color
474     // TODO: this won't work with hardware-accelerated renderers
475     int result;
476     __my_setcolor(&result, color, ::GetVirtualScreen()->GetColorDepth());
477 
478     return result;
479 }
GetWalkbehindBaseline(int32 wa)480 int IAGSEngine::GetWalkbehindBaseline (int32 wa) {
481     if ((wa < 1) || (wa >= MAX_OBJ))
482         quit("!IAGSEngine::GetWalkBehindBase: invalid walk-behind area specified");
483     return croom->walkbehind_base[wa];
484 }
GetScriptFunctionAddress(const char * funcName)485 void* IAGSEngine::GetScriptFunctionAddress (const char *funcName) {
486     return ccGetSymbolAddressForPlugin ((char*)funcName);
487 }
GetBitmapTransparentColor(BITMAP * bmp)488 int IAGSEngine::GetBitmapTransparentColor(BITMAP *bmp) {
489     return bitmap_mask_color (bmp);
490 }
491 // get the character scaling level at a particular point
GetAreaScaling(int32 x,int32 y)492 int IAGSEngine::GetAreaScaling (int32 x, int32 y) {
493     return GetScalingAt(x,y);
494 }
IsGamePaused()495 int IAGSEngine::IsGamePaused () {
496     return game_paused;
497 }
GetSpriteWidth(int32 slot)498 int IAGSEngine::GetSpriteWidth (int32 slot) {
499     return spritewidth[slot];
500 }
GetSpriteHeight(int32 slot)501 int IAGSEngine::GetSpriteHeight (int32 slot) {
502     return spriteheight[slot];
503 }
GetTextExtent(int32 font,const char * text,int32 * width,int32 * height)504 void IAGSEngine::GetTextExtent (int32 font, const char *text, int32 *width, int32 *height) {
505     if ((font < 0) || (font >= game.numfonts)) {
506         if (width != NULL) width[0] = 0;
507         if (height != NULL) height[0] = 0;
508         return;
509     }
510 
511     if (width != NULL)
512         width[0] = wgettextwidth_compensate (text, font);
513     if (height != NULL)
514         height[0] = wgettextheight ((char*)text, font);
515 }
PrintDebugConsole(const char * text)516 void IAGSEngine::PrintDebugConsole (const char *text) {
517     debug_script_log("[PLUGIN] %s", text);
518     platform->WriteStdOut("[PLUGIN] %s", text);
519 }
IsChannelPlaying(int32 channel)520 int IAGSEngine::IsChannelPlaying (int32 channel) {
521     return ::IsChannelPlaying (channel);
522 }
PlaySoundChannel(int32 channel,int32 soundType,int32 volume,int32 loop,const char * filename)523 void IAGSEngine::PlaySoundChannel (int32 channel, int32 soundType, int32 volume, int32 loop, const char *filename) {
524     stop_and_destroy_channel (channel);
525     SOUNDCLIP *newcha = NULL;
526 
527     if (((soundType == PSND_MP3STREAM) || (soundType == PSND_OGGSTREAM))
528         && (loop != 0))
529         quit("IAGSEngine::PlaySoundChannel: streamed samples cannot loop");
530 
531     // TODO: find out how engine was supposed to decide on where to load the sound from
532     AssetPath asset_name("", filename);
533 
534     if (soundType == PSND_WAVE)
535         newcha = my_load_wave (asset_name, volume, loop);
536     else if (soundType == PSND_MP3STREAM)
537         newcha = my_load_mp3 (asset_name, volume);
538     else if (soundType == PSND_OGGSTREAM)
539         newcha = my_load_ogg (asset_name, volume);
540     else if (soundType == PSND_MP3STATIC)
541         newcha = my_load_static_mp3 (asset_name, volume, (loop != 0));
542     else if (soundType == PSND_OGGSTATIC)
543         newcha = my_load_static_ogg (asset_name, volume, (loop != 0));
544     else if (soundType == PSND_MIDI) {
545         if (midi_pos >= 0)
546             quit("!IAGSEngine::PlaySoundChannel: MIDI already in use");
547         newcha = my_load_midi (asset_name, loop);
548         newcha->set_volume (volume);
549     }
550 #ifndef PSP_NO_MOD_PLAYBACK
551     else if (soundType == PSND_MOD) {
552         newcha = my_load_mod (asset_name, loop);
553         newcha->set_volume (volume);
554     }
555 #endif
556     else
557         quit("!IAGSEngine::PlaySoundChannel: unknown sound type");
558 
559     channels[channel] = newcha;
560 }
561 // Engine interface 12 and above are below
MarkRegionDirty(int32 left,int32 top,int32 right,int32 bottom)562 void IAGSEngine::MarkRegionDirty(int32 left, int32 top, int32 right, int32 bottom) {
563     invalidate_rect(left, top, right, bottom);
564     plugins[this->pluginId].invalidatedRegion++;
565 }
GetMouseCursor(int32 cursor)566 AGSMouseCursor * IAGSEngine::GetMouseCursor(int32 cursor) {
567     if ((cursor < 0) || (cursor >= game.numcursors))
568         return NULL;
569 
570     return (AGSMouseCursor*)&game.mcurs[cursor];
571 }
GetRawColorComponents(int32 coldepth,int32 color,int32 * red,int32 * green,int32 * blue,int32 * alpha)572 void IAGSEngine::GetRawColorComponents(int32 coldepth, int32 color, int32 *red, int32 *green, int32 *blue, int32 *alpha) {
573     if (red)
574         *red = getr_depth(coldepth, color);
575     if (green)
576         *green = getg_depth(coldepth, color);
577     if (blue)
578         *blue = getb_depth(coldepth, color);
579     if (alpha)
580         *alpha = geta_depth(coldepth, color);
581 }
MakeRawColorPixel(int32 coldepth,int32 red,int32 green,int32 blue,int32 alpha)582 int IAGSEngine::MakeRawColorPixel(int32 coldepth, int32 red, int32 green, int32 blue, int32 alpha) {
583     return makeacol_depth(coldepth, red, green, blue, alpha);
584 }
GetFontType(int32 fontNum)585 int IAGSEngine::GetFontType(int32 fontNum) {
586     if ((fontNum < 0) || (fontNum >= game.numfonts))
587         return FNT_INVALID;
588 
589     if (font_supports_extended_characters(fontNum))
590         return FNT_TTF;
591 
592     return FNT_SCI;
593 }
CreateDynamicSprite(int32 coldepth,int32 width,int32 height)594 int IAGSEngine::CreateDynamicSprite(int32 coldepth, int32 width, int32 height) {
595 
596     int gotSlot = spriteset.findFreeSlot();
597     if (gotSlot <= 0)
598         return 0;
599 
600     if ((width < 1) || (height < 1))
601         quit("!IAGSEngine::CreateDynamicSprite: invalid width/height requested by plugin");
602 
603     // resize the sprite to the requested size
604     Bitmap *newPic = BitmapHelper::CreateTransparentBitmap(width, height, coldepth);
605     if (newPic == NULL)
606         return 0;
607 
608     // add it into the sprite set
609     add_dynamic_sprite(gotSlot, newPic);
610     return gotSlot;
611 }
DeleteDynamicSprite(int32 slot)612 void IAGSEngine::DeleteDynamicSprite(int32 slot) {
613     free_dynamic_sprite(slot);
614 }
IsSpriteAlphaBlended(int32 slot)615 int IAGSEngine::IsSpriteAlphaBlended(int32 slot) {
616     if (game.spriteflags[slot] & SPF_ALPHACHANNEL)
617         return 1;
618     return 0;
619 }
620 
621 // disable AGS's sound engine
DisableSound()622 void IAGSEngine::DisableSound() {
623     shutdown_sound();
624     usetup.digicard = DIGI_NONE;
625     usetup.midicard = MIDI_NONE;
626     install_sound(usetup.digicard,usetup.midicard,NULL);
627 }
CanRunScriptFunctionNow()628 int IAGSEngine::CanRunScriptFunctionNow() {
629     if (inside_script)
630         return 0;
631     return 1;
632 }
CallGameScriptFunction(const char * name,int32 globalScript,int32 numArgs,long arg1,long arg2,long arg3)633 int IAGSEngine::CallGameScriptFunction(const char *name, int32 globalScript, int32 numArgs, long arg1, long arg2, long arg3) {
634     if (inside_script)
635         return -300;
636 
637     ccInstance *toRun = GetScriptInstanceByType(globalScript ? kScInstGame : kScInstRoom);
638 
639     RuntimeScriptValue params[3];
640     params[0].SetPluginArgument(arg1);
641     params[1].SetPluginArgument(arg2);
642     params[2].SetPluginArgument(arg3);
643     int toret = toRun->RunScriptFunctionIfExists((char*)name, numArgs, params);
644     return toret;
645 }
646 
NotifySpriteUpdated(int32 slot)647 void IAGSEngine::NotifySpriteUpdated(int32 slot) {
648     int ff;
649     // wipe the character cache when we change rooms
650     for (ff = 0; ff < game.numcharacters; ff++) {
651         if ((charcache[ff].inUse) && (charcache[ff].sppic == slot)) {
652             delete charcache[ff].image;
653             charcache[ff].image = NULL;
654             charcache[ff].inUse = 0;
655         }
656     }
657 
658     // clear the object cache
659     for (ff = 0; ff < MAX_INIT_SPR; ff++) {
660         if ((objcache[ff].image != NULL) && (objcache[ff].sppic == slot)) {
661             delete objcache[ff].image;
662             objcache[ff].image = NULL;
663         }
664     }
665 }
666 
SetSpriteAlphaBlended(int32 slot,int32 isAlphaBlended)667 void IAGSEngine::SetSpriteAlphaBlended(int32 slot, int32 isAlphaBlended) {
668 
669     game.spriteflags[slot] &= ~SPF_ALPHACHANNEL;
670 
671     if (isAlphaBlended)
672         game.spriteflags[slot] |= SPF_ALPHACHANNEL;
673 }
674 
QueueGameScriptFunction(const char * name,int32 globalScript,int32 numArgs,long arg1,long arg2)675 void IAGSEngine::QueueGameScriptFunction(const char *name, int32 globalScript, int32 numArgs, long arg1, long arg2) {
676     if (!inside_script) {
677         this->CallGameScriptFunction(name, globalScript, numArgs, arg1, arg2, 0);
678         return;
679     }
680 
681     if (numArgs < 0 || numArgs > 2)
682         quit("IAGSEngine::QueueGameScriptFunction: invalid number of arguments");
683 
684     curscript->run_another(name, globalScript ? kScInstGame : kScInstRoom, numArgs,
685         RuntimeScriptValue().SetPluginArgument(arg1), RuntimeScriptValue().SetPluginArgument(arg2));
686 }
687 
RegisterManagedObject(const void * object,IAGSScriptManagedObject * callback)688 int IAGSEngine::RegisterManagedObject(const void *object, IAGSScriptManagedObject *callback) {
689     GlobalReturnValue.SetPluginObject((void*)object, (ICCDynamicObject*)callback);
690     return ccRegisterManagedObject(object, (ICCDynamicObject*)callback, true);
691 }
692 
AddManagedObjectReader(const char * typeName,IAGSManagedObjectReader * reader)693 void IAGSEngine::AddManagedObjectReader(const char *typeName, IAGSManagedObjectReader *reader) {
694     if (numPluginReaders >= MAX_PLUGIN_OBJECT_READERS)
695         quit("Plugin error: IAGSEngine::AddObjectReader: Too many object readers added");
696 
697     if ((typeName == NULL) || (typeName[0] == 0))
698         quit("Plugin error: IAGSEngine::AddObjectReader: invalid name for type");
699 
700     for (int ii = 0; ii < numPluginReaders; ii++) {
701         if (strcmp(pluginReaders[ii].type, typeName) == 0)
702             quitprintf("Plugin error: IAGSEngine::AddObjectReader: type '%s' has been registered already", typeName);
703     }
704 
705     pluginReaders[numPluginReaders].reader = reader;
706     pluginReaders[numPluginReaders].type = typeName;
707     numPluginReaders++;
708 }
709 
RegisterUnserializedObject(int key,const void * object,IAGSScriptManagedObject * callback)710 void IAGSEngine::RegisterUnserializedObject(int key, const void *object, IAGSScriptManagedObject *callback) {
711     GlobalReturnValue.SetPluginObject((void*)object, (ICCDynamicObject*)callback);
712     ccRegisterUnserializedObject(key, object, (ICCDynamicObject*)callback, true);
713 }
714 
GetManagedObjectKeyByAddress(const char * address)715 int IAGSEngine::GetManagedObjectKeyByAddress(const char *address) {
716     return ccGetObjectHandleFromAddress(address);
717 }
718 
GetManagedObjectAddressByKey(int key)719 void* IAGSEngine::GetManagedObjectAddressByKey(int key) {
720     void *object;
721     ICCDynamicObject *manager;
722     ScriptValueType obj_type = ccGetObjectAddressAndManagerFromHandle(key, object, manager);
723     if (obj_type == kScValPluginObject)
724     {
725         GlobalReturnValue.SetPluginObject(object, manager);
726     }
727     else
728     {
729         GlobalReturnValue.SetDynamicObject(object, manager);
730     }
731     return object;
732 }
733 
CreateScriptString(const char * fromText)734 const char* IAGSEngine::CreateScriptString(const char *fromText) {
735     const char *string = CreateNewScriptString(fromText);
736     // Should be still standard dynamic object, because not managed by plugin
737     GlobalReturnValue.SetDynamicObject((void*)string, &myScriptStringImpl);
738     return string;
739 }
740 
IncrementManagedObjectRefCount(const char * address)741 int IAGSEngine::IncrementManagedObjectRefCount(const char *address) {
742     return ccAddObjectReference(GetManagedObjectKeyByAddress(address));
743 }
744 
DecrementManagedObjectRefCount(const char * address)745 int IAGSEngine::DecrementManagedObjectRefCount(const char *address) {
746     return ccReleaseObjectReference(GetManagedObjectKeyByAddress(address));
747 }
748 
SetMousePosition(int32 x,int32 y)749 void IAGSEngine::SetMousePosition(int32 x, int32 y) {
750     Mouse::SetPosition(Point(x, y));
751     RefreshMouse();
752 }
753 
SimulateMouseClick(int32 button)754 void IAGSEngine::SimulateMouseClick(int32 button) {
755     PluginSimulateMouseClick(button);
756 }
757 
GetMovementPathWaypointCount(int32 pathId)758 int IAGSEngine::GetMovementPathWaypointCount(int32 pathId) {
759     return mls[pathId % TURNING_AROUND].numstage;
760 }
761 
GetMovementPathLastWaypoint(int32 pathId)762 int IAGSEngine::GetMovementPathLastWaypoint(int32 pathId) {
763     return mls[pathId % TURNING_AROUND].onstage;
764 }
765 
GetMovementPathWaypointLocation(int32 pathId,int32 waypoint,int32 * x,int32 * y)766 void IAGSEngine::GetMovementPathWaypointLocation(int32 pathId, int32 waypoint, int32 *x, int32 *y) {
767     *x = (mls[pathId % TURNING_AROUND].pos[waypoint] >> 16) & 0x0000ffff;
768     *y = (mls[pathId % TURNING_AROUND].pos[waypoint] & 0x0000ffff);
769 }
770 
GetMovementPathWaypointSpeed(int32 pathId,int32 waypoint,int32 * xSpeed,int32 * ySpeed)771 void IAGSEngine::GetMovementPathWaypointSpeed(int32 pathId, int32 waypoint, int32 *xSpeed, int32 *ySpeed) {
772     *xSpeed = mls[pathId % TURNING_AROUND].xpermove[waypoint];
773     *ySpeed = mls[pathId % TURNING_AROUND].ypermove[waypoint];
774 }
775 
IsRunningUnderDebugger()776 int IAGSEngine::IsRunningUnderDebugger()
777 {
778     return (editor_debugging_enabled != 0) ? 1 : 0;
779 }
780 
GetPathToFileInCompiledFolder(const char * fileName,char * buffer)781 void IAGSEngine::GetPathToFileInCompiledFolder(const char*fileName, char *buffer)
782 {
783     get_install_dir_path(buffer, fileName);
784 }
785 
BreakIntoDebugger()786 void IAGSEngine::BreakIntoDebugger()
787 {
788     break_on_next_script_step = 1;
789 }
790 
ReplaceFontRenderer(int fontNumber,IAGSFontRenderer * newRenderer)791 IAGSFontRenderer* IAGSEngine::ReplaceFontRenderer(int fontNumber, IAGSFontRenderer *newRenderer)
792 {
793     return font_replace_renderer(fontNumber, newRenderer);
794 }
795 
796 
797 // *********** General plugin implementation **********
798 
pl_stop_plugins()799 void pl_stop_plugins() {
800     int a;
801     ccSetDebugHook(NULL);
802 
803     for (a = 0; a < numPlugins; a++) {
804         if (plugins[a].available) {
805             if (plugins[a].engineShutdown != NULL)
806                 plugins[a].engineShutdown();
807             plugins[a].wantHook = 0;
808             if (plugins[a].savedata) {
809                 free(plugins[a].savedata);
810                 plugins[a].savedata = NULL;
811             }
812             if (!plugins[a].builtin) {
813               plugins[a].library.Unload();
814             }
815         }
816     }
817     numPlugins = 0;
818 }
819 
pl_startup_plugins()820 void pl_startup_plugins() {
821     int i;
822     for (i = 0; i < numPlugins; i++) {
823         if (plugins[i].available)
824             plugins[i].engineStartup (&plugins[i].eiface);
825     }
826 }
827 
pl_run_plugin_hooks(int event,long data)828 int pl_run_plugin_hooks (int event, long data) {
829     int i, retval = 0;
830     for (i = 0; i < numPlugins; i++) {
831         if (plugins[i].wantHook & event) {
832             retval = plugins[i].onEvent (event, data);
833             if (retval)
834                 return retval;
835         }
836     }
837     return 0;
838 }
839 
pl_run_plugin_debug_hooks(const char * scriptfile,int linenum)840 int pl_run_plugin_debug_hooks (const char *scriptfile, int linenum) {
841     int i, retval = 0;
842     for (i = 0; i < numPlugins; i++) {
843         if (plugins[i].wantHook & AGSE_SCRIPTDEBUG) {
844             retval = plugins[i].debugHook(scriptfile, linenum, 0);
845             if (retval)
846                 return retval;
847         }
848     }
849     return 0;
850 }
851 
pl_run_plugin_init_gfx_hooks(const char * driverName,void * data)852 void pl_run_plugin_init_gfx_hooks (const char *driverName, void *data) {
853     for (int i = 0; i < numPlugins; i++)
854     {
855         if (plugins[i].initGfxHook != NULL)
856         {
857             plugins[i].initGfxHook(driverName, data);
858         }
859     }
860 }
861 
pl_register_builtin_plugin(InbuiltPluginDetails const & details)862 int pl_register_builtin_plugin(InbuiltPluginDetails const &details) {
863     _registered_builtin_plugins.push_back(details);
864     return 0;
865 }
866 
pl_use_builtin_plugin(EnginePlugin * apl)867 bool pl_use_builtin_plugin(EnginePlugin* apl)
868 {
869 #if defined(BUILTIN_PLUGINS)
870     if (stricmp(apl->filename, "agsflashlight") == 0)
871     {
872         apl->engineStartup = agsflashlight::AGS_EngineStartup;
873         apl->engineShutdown = agsflashlight::AGS_EngineShutdown;
874         apl->onEvent = agsflashlight::AGS_EngineOnEvent;
875         apl->debugHook = agsflashlight::AGS_EngineDebugHook;
876         apl->initGfxHook = agsflashlight::AGS_EngineInitGfx;
877         apl->available = true;
878         apl->builtin = true;
879         return true;
880     }
881     else if (stricmp(apl->filename, "agsblend") == 0)
882     {
883         apl->engineStartup = agsblend::AGS_EngineStartup;
884         apl->engineShutdown = agsblend::AGS_EngineShutdown;
885         apl->onEvent = agsblend::AGS_EngineOnEvent;
886         apl->debugHook = agsblend::AGS_EngineDebugHook;
887         apl->initGfxHook = agsblend::AGS_EngineInitGfx;
888         apl->available = true;
889         apl->builtin = true;
890         return true;
891     }
892     else if (stricmp(apl->filename, "ags_snowrain") == 0)
893     {
894         apl->engineStartup = ags_snowrain::AGS_EngineStartup;
895         apl->engineShutdown = ags_snowrain::AGS_EngineShutdown;
896         apl->onEvent = ags_snowrain::AGS_EngineOnEvent;
897         apl->debugHook = ags_snowrain::AGS_EngineDebugHook;
898         apl->initGfxHook = ags_snowrain::AGS_EngineInitGfx;
899         apl->available = true;
900         apl->builtin = true;
901         return true;
902     }
903     else if (stricmp(apl->filename, "ags_parallax") == 0)
904     {
905         apl->engineStartup = ags_parallax::AGS_EngineStartup;
906         apl->engineShutdown = ags_parallax::AGS_EngineShutdown;
907         apl->onEvent = ags_parallax::AGS_EngineOnEvent;
908         apl->debugHook = ags_parallax::AGS_EngineDebugHook;
909         apl->initGfxHook = ags_parallax::AGS_EngineInitGfx;
910         apl->available = true;
911         apl->builtin = true;
912         return true;
913     }
914     else if (stricmp(apl->filename, "agspalrender") == 0)
915     {
916         apl->engineStartup = agspalrender::AGS_EngineStartup;
917         apl->engineShutdown = agspalrender::AGS_EngineShutdown;
918         apl->onEvent = agspalrender::AGS_EngineOnEvent;
919         apl->debugHook = agspalrender::AGS_EngineDebugHook;
920         apl->initGfxHook = agspalrender::AGS_EngineInitGfx;
921         apl->available = true;
922         apl->builtin = true;
923         return true;
924     }
925 #if defined(IOS_VERSION)
926     else if (stricmp(apl->filename, "agstouch") == 0)
927     {
928         apl->engineStartup = agstouch::AGS_EngineStartup;
929         apl->engineShutdown = agstouch::AGS_EngineShutdown;
930         apl->onEvent = agstouch::AGS_EngineOnEvent;
931         apl->debugHook = agstouch::AGS_EngineDebugHook;
932         apl->initGfxHook = agstouch::AGS_EngineInitGfx;
933         apl->available = true;
934         apl->builtin = true;
935         return true;
936     }
937 #endif // IOS_VERSION
938 #endif // BUILTIN_PLUGINS
939 
940     for(std::vector<InbuiltPluginDetails>::iterator it = _registered_builtin_plugins.begin(); it != _registered_builtin_plugins.end(); ++it) {
941         if (stricmp(apl->filename, it->filename) == 0) {
942             apl->engineStartup = it->engineStartup;
943             apl->engineShutdown = it->engineShutdown;
944             apl->onEvent = it->onEvent;
945             apl->debugHook = it->debugHook;
946             apl->initGfxHook = it->initGfxHook;
947             apl->available = true;
948             apl->builtin = true;
949             return true;
950         }
951     }
952 
953     AGS::Common::Debug::Printf("No built-in plugin found. Plugin loading failed!");
954     return false;
955 }
956 
pl_register_plugins(const std::vector<Common::PluginInfo> & infos)957 Engine::GameInitError pl_register_plugins(const std::vector<Common::PluginInfo> &infos)
958 {
959     numPlugins = 0;
960     for (size_t inf_index = 0; inf_index < infos.size(); ++inf_index)
961     {
962         const Common::PluginInfo &info = infos[inf_index];
963         String name = info.Name;
964         if (name.GetLast() == '!')
965             continue; // editor-only plugin, ignore it
966         if (numPlugins == MAXPLUGINS)
967             return kGameInitErr_TooManyPlugins;
968         // AGS Editor currently saves plugin names in game data with
969         // ".dll" extension appended; we need to take care of that
970         const String name_ext = ".dll";
971         if (name.GetLength() <= name_ext.GetLength() || name.GetLength() > PLUGIN_FILENAME_MAX + name_ext.GetLength() ||
972                 name.CompareRightNoCase(name_ext, name_ext.GetLength())) {
973             return kGameInitErr_PluginNameInvalid;
974         }
975         // remove ".dll" from plugin's name
976         name.ClipRight(name_ext.GetLength());
977 
978         EnginePlugin *apl = &plugins[numPlugins++];
979         // Copy plugin info
980         snprintf(apl->filename, sizeof(apl->filename), "%s", name.GetCStr());
981         if (info.DataLen)
982         {
983             apl->savedata = (char*)malloc(info.DataLen);
984             memcpy(apl->savedata, info.Data.get(), info.DataLen);
985         }
986         apl->savedatasize = info.DataLen;
987 
988         // Compatibility with the old SnowRain module
989         if (stricmp(apl->filename, "ags_SnowRain20") == 0) {
990             strcpy(apl->filename, "ags_snowrain");
991         }
992 
993         if (apl->library.Load(apl->filename))
994         {
995           AGS::Common::Debug::Printf(kDbgMsg_Init, "Plugin '%s' loading succeeded, resolving imports...", apl->filename);
996 
997           if (apl->library.GetFunctionAddress("AGS_PluginV2") == NULL) {
998               quitprintf("Plugin '%s' is an old incompatible version.", apl->filename);
999           }
1000           apl->engineStartup = (void(*)(IAGSEngine*))apl->library.GetFunctionAddress("AGS_EngineStartup");
1001           apl->engineShutdown = (void(*)())apl->library.GetFunctionAddress("AGS_EngineShutdown");
1002 
1003           if (apl->engineStartup == NULL) {
1004               quitprintf("Plugin '%s' is not a valid AGS plugin (no engine startup entry point)", apl->filename);
1005           }
1006           apl->onEvent = (int(*)(int,int))apl->library.GetFunctionAddress("AGS_EngineOnEvent");
1007           apl->debugHook = (int(*)(const char*,int,int))apl->library.GetFunctionAddress("AGS_EngineDebugHook");
1008           apl->initGfxHook = (void(*)(const char*, void*))apl->library.GetFunctionAddress("AGS_EngineInitGfx");
1009         }
1010         else
1011         {
1012           AGS::Common::Debug::Printf("Plugin loading failed, trying built-in plugins...");
1013           if (!pl_use_builtin_plugin(apl))
1014           {
1015             // Plugin loading has failed at this point, try using built-in plugin function stubs
1016             if (RegisterPluginStubs((const char*)apl->filename))
1017               AGS::Common::Debug::Printf(kDbgMsg_Init, "Placeholder functions for the plugin '%s' found.", apl->filename);
1018             else
1019               AGS::Common::Debug::Printf(kDbgMsg_Init, "No placeholder functions for the plugin '%s' found. The game might fail to load.", apl->filename);
1020 
1021             continue;
1022           }
1023         }
1024 
1025         apl->eiface.pluginId = numPlugins - 1;
1026         apl->eiface.version = 24;
1027         apl->wantHook = 0;
1028         apl->available = true;
1029     }
1030     return kGameInitErr_NoError;
1031 }
1032 
pl_is_plugin_loaded(const char * pl_name)1033 bool pl_is_plugin_loaded(const char *pl_name)
1034 {
1035     if (!pl_name)
1036         return false;
1037 
1038     for (int i = 0; i < numPlugins; ++i)
1039     {
1040         if (stricmp(pl_name, plugins[i].filename) == 0)
1041             return plugins[i].available;
1042     }
1043     return false;
1044 }
1045