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