From 86a2e2b6440696380517cc865f7d75476b32145f Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Thu, 28 Mar 2013 12:02:43 +0400 Subject: [PATCH 1/7] Makefile: removed PIE and FPIE compile flags for Linux --- Engine/Makefile-defs.linux | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/Makefile-defs.linux b/Engine/Makefile-defs.linux index 6aa4494a59..9900eb52a0 100644 --- a/Engine/Makefile-defs.linux +++ b/Engine/Makefile-defs.linux @@ -1,6 +1,6 @@ INCDIR = ../Engine ../Common ../Common/libinclude ../Plugins LIBDIR = -override CFLAGS += -O2 -pie -fpie -g -fsigned-char -Wfatal-errors -DNDEBUG -DAGS_RUNTIME_PATCH_ALLEGRO -DAGS_HAS_CD_AUDIO -DAGS_CASE_SENSITIVE_FILESYSTEM -DALLEGRO_STATICLINK -DLINUX_VERSION -DDISABLE_MPEG_AUDIO -DBUILTIN_PLUGINS -DRTLD_NEXT $(shell pkg-config --cflags freetype2) +override CFLAGS += -O2 -g -fsigned-char -Wfatal-errors -DNDEBUG -DAGS_RUNTIME_PATCH_ALLEGRO -DAGS_HAS_CD_AUDIO -DAGS_CASE_SENSITIVE_FILESYSTEM -DALLEGRO_STATICLINK -DLINUX_VERSION -DDISABLE_MPEG_AUDIO -DBUILTIN_PLUGINS -DRTLD_NEXT $(shell pkg-config --cflags freetype2) override CXXFLAGS += -fno-rtti -Wno-write-strings -fpermissive LIBS = -rdynamic $(shell allegro-config --libs) -laldmb -ldumb -Wl,-Bdynamic -ltheora -logg From 0c1614113c3d6edccbedb41b8545105befbeb970 Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Thu, 28 Mar 2013 22:38:24 +0400 Subject: [PATCH 2/7] Engine: corrected Theora playing callback --- Engine/media/video/video.cpp | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Engine/media/video/video.cpp b/Engine/media/video/video.cpp index 7715903e1f..5c6370379b 100644 --- a/Engine/media/video/video.cpp +++ b/Engine/media/video/video.cpp @@ -28,9 +28,10 @@ #include "platform/base/agsplatformdriver.h" #include "gfx/ddb.h" #include "gfx/graphicsdriver.h" -#include "gfx/bitmap.h" +#include "gfx/allegrobitmap.h" using AGS::Common::Bitmap; +using AGS::Common::AllegroBitmap; namespace BitmapHelper = AGS::Common::BitmapHelper; @@ -98,23 +99,21 @@ extern "C" int fli_callback() { // FLIC player end // TODO: find a way to take Bitmap here? -int theora_playing_callback(BITMAP *theoraBuffer_raw) +AllegroBitmap theora_frame_wrapper; +int theora_playing_callback(BITMAP *theoraBuffer) { - // [IKM] CHECKME later (need optimization / reimplementation) - // This is probably not a very good thing to do in a video callback... - // Good thing is that AllegroBitmap does not store much data on its own - Bitmap *theoraBuffer = BitmapHelper::CreateRawObjectWrapper(theoraBuffer_raw); - if (theoraBuffer == NULL) { // No video, only sound return check_if_user_input_should_cancel_video(); } + theora_frame_wrapper.WrapBitmapObject(theoraBuffer); + int drawAtX = 0, drawAtY = 0; if (fli_ddb == NULL) { - fli_ddb = gfxDriver->CreateDDBFromBitmap(theoraBuffer, false, true); + fli_ddb = gfxDriver->CreateDDBFromBitmap(&theora_frame_wrapper, false, true); } if (stretch_flc) { @@ -122,7 +121,7 @@ int theora_playing_callback(BITMAP *theoraBuffer_raw) drawAtY = scrnhit / 2 - fliTargetHeight / 2; if (!gfxDriver->HasAcceleratedStretchAndFlip()) { - fli_target->StretchBlt(theoraBuffer, RectWH(0, 0, theoraBuffer->GetWidth(), theoraBuffer->GetHeight()), + fli_target->StretchBlt(&theora_frame_wrapper, RectWH(0, 0, theora_frame_wrapper.GetWidth(), theora_frame_wrapper.GetHeight()), RectWH(drawAtX, drawAtY, fliTargetWidth, fliTargetHeight)); gfxDriver->UpdateDDBFromBitmap(fli_ddb, fli_target, false); drawAtX = 0; @@ -130,22 +129,21 @@ int theora_playing_callback(BITMAP *theoraBuffer_raw) } else { - gfxDriver->UpdateDDBFromBitmap(fli_ddb, theoraBuffer, false); + gfxDriver->UpdateDDBFromBitmap(fli_ddb, &theora_frame_wrapper, false); fli_ddb->SetStretch(fliTargetWidth, fliTargetHeight); } } else { - gfxDriver->UpdateDDBFromBitmap(fli_ddb, theoraBuffer, false); - drawAtX = scrnwid / 2 - theoraBuffer->GetWidth() / 2; - drawAtY = scrnhit / 2 - theoraBuffer->GetHeight() / 2; + gfxDriver->UpdateDDBFromBitmap(fli_ddb, &theora_frame_wrapper, false); + drawAtX = scrnwid / 2 - theora_frame_wrapper.GetWidth() / 2; + drawAtY = scrnhit / 2 - theora_frame_wrapper.GetHeight() / 2; } gfxDriver->DrawSprite(drawAtX, drawAtY, fli_ddb); render_to_screen(virtual_screen, 0, 0); update_polled_stuff_and_crossfade (); - delete theoraBuffer; return check_if_user_input_should_cancel_video(); } From fafc1d8a15b5cca5c0d19fabf91c0ac32a958c9e Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Fri, 29 Mar 2013 02:40:06 +0400 Subject: [PATCH 3/7] Script: restored compatibility with plugins, creating managed objects I had to add new PluginObject type of script variable, to distinct it from "normal" DynamicObject. First, it should not make use of wrapper helpers for reading/writing values, because plugin interface does not declare such functionality, and read and write directly by memory address, as it was done for all objects in vanilla AGS. Second, member function calls should be handled using vanilla way of calling functions. --- Engine/ac/dynobj/cc_dynamicobject.cpp | 15 +++--- Engine/ac/dynobj/cc_dynamicobject.h | 7 +-- Engine/ac/dynobj/managedobjectpool.cpp | 19 ++++---- Engine/ac/dynobj/managedobjectpool.h | 8 ++-- Engine/plugin/agsplugin.cpp | 20 +++++--- Engine/script/cc_instance.cpp | 66 ++++++++++++++++---------- Engine/script/runtimescriptvalue.h | 11 +++++ Engine/script/script_runtime.cpp | 16 +++++-- Engine/script/script_runtime.h | 2 +- 9 files changed, 107 insertions(+), 57 deletions(-) diff --git a/Engine/ac/dynobj/cc_dynamicobject.cpp b/Engine/ac/dynobj/cc_dynamicobject.cpp index 598975b57d..09fdf9cc1f 100644 --- a/Engine/ac/dynobj/cc_dynamicobject.cpp +++ b/Engine/ac/dynobj/cc_dynamicobject.cpp @@ -45,8 +45,8 @@ void ccSetStringClassImpl(ICCStringClass *theClass) { // register a memory handle for the object and allow script // pointers to point to it -int32_t ccRegisterManagedObject(const void *object, ICCDynamicObject *callback) { - int32_t handl = pool.AddObject((const char*)object, callback); +int32_t ccRegisterManagedObject(const void *object, ICCDynamicObject *callback, bool plugin_object) { + int32_t handl = pool.AddObject((const char*)object, callback, plugin_object); #ifdef DEBUG_MANAGED_OBJECTS char bufff[200]; @@ -58,8 +58,8 @@ int32_t ccRegisterManagedObject(const void *object, ICCDynamicObject *callback) } // register a de-serialized object -int32_t ccRegisterUnserializedObject(int index, const void *object, ICCDynamicObject *callback) { - return pool.AddObject((const char*)object, callback, index); +int32_t ccRegisterUnserializedObject(int index, const void *object, ICCDynamicObject *callback, bool plugin_object) { + return pool.AddObject((const char*)object, callback, plugin_object, index); } // unregister a particular object @@ -130,17 +130,18 @@ const char *ccGetObjectAddressFromHandle(int32_t handle) { return addr; } -void ccGetObjectAddressAndManagerFromHandle(int32_t handle, void *&object, ICCDynamicObject *&manager) +ScriptValueType ccGetObjectAddressAndManagerFromHandle(int32_t handle, void *&object, ICCDynamicObject *&manager) { if (handle == 0) { object = NULL; manager = NULL; - return; + return kScValUndefined; } - pool.HandleToAddressAndManager(handle, object, manager); + ScriptValueType obj_type = pool.HandleToAddressAndManager(handle, object, manager); if (object == NULL) { cc_error("Error retrieving pointer: invalid handle %d", handle); } + return obj_type; } int ccAddObjectReference(int32_t handle) { diff --git a/Engine/ac/dynobj/cc_dynamicobject.h b/Engine/ac/dynobj/cc_dynamicobject.h index c639046f73..94d9345a3c 100644 --- a/Engine/ac/dynobj/cc_dynamicobject.h +++ b/Engine/ac/dynobj/cc_dynamicobject.h @@ -20,6 +20,7 @@ #define __CC_DYNAMICOBJECT_H #include "util/file.h" +#include "script/runtimescriptvalue.h" // Forward declaration namespace AGS { namespace Common { class Stream; } } @@ -63,9 +64,9 @@ struct ICCStringClass { extern void ccSetStringClassImpl(ICCStringClass *theClass); // register a memory handle for the object and allow script // pointers to point to it -extern int32_t ccRegisterManagedObject(const void *object, ICCDynamicObject *); +extern int32_t ccRegisterManagedObject(const void *object, ICCDynamicObject *, bool plugin_object = false); // register a de-serialized object -extern int32_t ccRegisterUnserializedObject(int index, const void *object, ICCDynamicObject *); +extern int32_t ccRegisterUnserializedObject(int index, const void *object, ICCDynamicObject *, bool plugin_object = false); // unregister a particular object extern int ccUnRegisterManagedObject(const void *object); // remove all registered objects @@ -79,7 +80,7 @@ extern void ccAttemptDisposeObject(int32_t handle); // translate between object handles and memory addresses extern int32_t ccGetObjectHandleFromAddress(const char *address); extern const char *ccGetObjectAddressFromHandle(int32_t handle); -extern void ccGetObjectAddressAndManagerFromHandle(int32_t handle, void *&object, ICCDynamicObject *&manager); +extern ScriptValueType ccGetObjectAddressAndManagerFromHandle(int32_t handle, void *&object, ICCDynamicObject *&manager); extern int ccAddObjectReference(int32_t handle); extern int ccReleaseObjectReference(int32_t handle); diff --git a/Engine/ac/dynobj/managedobjectpool.cpp b/Engine/ac/dynobj/managedobjectpool.cpp index 12e3582a12..30cbb83a7d 100644 --- a/Engine/ac/dynobj/managedobjectpool.cpp +++ b/Engine/ac/dynobj/managedobjectpool.cpp @@ -23,7 +23,9 @@ using AGS::Common::Stream; -void ManagedObjectPool::ManagedObject::init(int32_t theHandle, const char *theAddress, ICCDynamicObject *theCallback) { +void ManagedObjectPool::ManagedObject::init(int32_t theHandle, const char *theAddress, + ICCDynamicObject *theCallback, ScriptValueType objType) { + obj_type = objType; handle = theHandle; addr = theAddress; callback = theCallback; @@ -137,16 +139,17 @@ const char* ManagedObjectPool::HandleToAddress(int32_t handle) { return objects[handle].addr; } -void ManagedObjectPool::HandleToAddressAndManager(int32_t handle, void *&object, ICCDynamicObject *&manager) { +ScriptValueType ManagedObjectPool::HandleToAddressAndManager(int32_t handle, void *&object, ICCDynamicObject *&manager) { object = NULL; manager = NULL; // this function is called often (whenever a pointer is used) if ((handle < 1) || (handle >= arrayAllocLimit)) - return; + return kScValUndefined; if (objects[handle].handle == 0) - return; + return kScValUndefined; object = (void*)objects[handle].addr; manager = objects[handle].callback; + return objects[handle].obj_type; } int ManagedObjectPool::RemoveObject(const char *address) { @@ -180,7 +183,7 @@ void ManagedObjectPool::RunGarbageCollection() } } -int ManagedObjectPool::AddObject(const char *address, ICCDynamicObject *callback, int useSlot) { +int ManagedObjectPool::AddObject(const char *address, ICCDynamicObject *callback, bool plugin_object, int useSlot) { if (useSlot == -1) useSlot = numObjects; @@ -188,7 +191,7 @@ int ManagedObjectPool::AddObject(const char *address, ICCDynamicObject *callback if (useSlot < arrayAllocLimit) { // still space in the array, so use it - objects[useSlot].init(useSlot, address, callback); + objects[useSlot].init(useSlot, address, callback, plugin_object ? kScValPluginObject : kScValDynamicObject); if (useSlot == numObjects) numObjects++; return useSlot; @@ -201,7 +204,7 @@ int ManagedObjectPool::AddObject(const char *address, ICCDynamicObject *callback // long for (int i = arrayAllocLimit - 1; i >= 1; i--) { if (objects[i].handle == 0) { - objects[i].init(i, address, callback); + objects[i].init(i, address, callback, plugin_object ? kScValPluginObject : kScValDynamicObject); return i; } } @@ -212,7 +215,7 @@ int ManagedObjectPool::AddObject(const char *address, ICCDynamicObject *callback objects = (ManagedObject*)realloc(objects, sizeof(ManagedObject) * arrayAllocLimit); memset(&objects[useSlot], 0, sizeof(ManagedObject) * ARRAY_INCREMENT_SIZE); - objects[useSlot].init(useSlot, address, callback); + objects[useSlot].init(useSlot, address, callback, plugin_object ? kScValPluginObject : kScValDynamicObject); if (useSlot == numObjects) numObjects++; return useSlot; diff --git a/Engine/ac/dynobj/managedobjectpool.h b/Engine/ac/dynobj/managedobjectpool.h index 5a0e096df5..48fbe00b53 100644 --- a/Engine/ac/dynobj/managedobjectpool.h +++ b/Engine/ac/dynobj/managedobjectpool.h @@ -27,12 +27,14 @@ const int GARBAGE_COLLECTION_INTERVAL = 100; struct ManagedObjectPool { struct ManagedObject { + ScriptValueType obj_type; int32_t handle; const char *addr; ICCDynamicObject * callback; int refCount; - void init(int32_t theHandle, const char *theAddress, ICCDynamicObject *theCallback); + void init(int32_t theHandle, const char *theAddress, + ICCDynamicObject *theCallback, ScriptValueType objType); int remove(bool force); int AddRef(); int CheckDispose(); @@ -53,11 +55,11 @@ struct ManagedObjectPool { int32_t SubRef(int32_t handle); int32_t AddressToHandle(const char *addr); const char* HandleToAddress(int32_t handle); - void HandleToAddressAndManager(int32_t handle, void *&object, ICCDynamicObject *&manager); + ScriptValueType HandleToAddressAndManager(int32_t handle, void *&object, ICCDynamicObject *&manager); int RemoveObject(const char *address); void RunGarbageCollectionIfAppropriate(); void RunGarbageCollection(); - int AddObject(const char *address, ICCDynamicObject *callback, int useSlot = -1); + int AddObject(const char *address, ICCDynamicObject *callback, bool plugin_object, int useSlot = -1); void WriteToDisk(Common::Stream *out); int ReadFromDisk(Common::Stream *in, ICCObjectReader *reader); void reset(); diff --git a/Engine/plugin/agsplugin.cpp b/Engine/plugin/agsplugin.cpp index 19bf0fd8cc..f1b3ce019f 100644 --- a/Engine/plugin/agsplugin.cpp +++ b/Engine/plugin/agsplugin.cpp @@ -680,8 +680,8 @@ void IAGSEngine::QueueGameScriptFunction(const char *name, int32 globalScript, i } int IAGSEngine::RegisterManagedObject(const void *object, IAGSScriptManagedObject *callback) { - GlobalReturnValue.SetDynamicObject((void*)object, (ICCDynamicObject*)callback); - return ccRegisterManagedObject(object, (ICCDynamicObject*)callback); + GlobalReturnValue.SetPluginObject((void*)object, (ICCDynamicObject*)callback); + return ccRegisterManagedObject(object, (ICCDynamicObject*)callback, true); } void IAGSEngine::AddManagedObjectReader(const char *typeName, IAGSManagedObjectReader *reader) { @@ -702,8 +702,8 @@ void IAGSEngine::AddManagedObjectReader(const char *typeName, IAGSManagedObjectR } void IAGSEngine::RegisterUnserializedObject(int key, const void *object, IAGSScriptManagedObject *callback) { - GlobalReturnValue.SetDynamicObject((void*)object, (ICCDynamicObject*)callback); - ccRegisterUnserializedObject(key, object, (ICCDynamicObject*)callback); + GlobalReturnValue.SetPluginObject((void*)object, (ICCDynamicObject*)callback); + ccRegisterUnserializedObject(key, object, (ICCDynamicObject*)callback, true); } int IAGSEngine::GetManagedObjectKeyByAddress(const char *address) { @@ -713,13 +713,21 @@ int IAGSEngine::GetManagedObjectKeyByAddress(const char *address) { void* IAGSEngine::GetManagedObjectAddressByKey(int key) { void *object; ICCDynamicObject *manager; - ccGetObjectAddressAndManagerFromHandle(key, object, manager); - GlobalReturnValue.SetDynamicObject(object, manager); + ScriptValueType obj_type = ccGetObjectAddressAndManagerFromHandle(key, object, manager); + if (obj_type == kScValPluginObject) + { + GlobalReturnValue.SetPluginObject(object, manager); + } + else + { + GlobalReturnValue.SetDynamicObject(object, manager); + } return object; } const char* IAGSEngine::CreateScriptString(const char *fromText) { const char *string = CreateNewScriptString(fromText); + // Should be still standard dynamic object, because not managed by plugin GlobalReturnValue.SetDynamicObject((void*)string, &myScriptStringImpl); return string; } diff --git a/Engine/script/cc_instance.cpp b/Engine/script/cc_instance.cpp index 51a9625d15..863b54b273 100644 --- a/Engine/script/cc_instance.cpp +++ b/Engine/script/cc_instance.cpp @@ -826,8 +826,7 @@ int ccInstance::Run(int32_t curpc) pc += (reg1.IValue - thisbase[curnest]); } - if (next_call_needs_object) // is this right? - next_call_needs_object = 0; + next_call_needs_object = 0; if (loopIterationCheckDisabled) loopIterationCheckDisabled++; @@ -935,8 +934,15 @@ int ccInstance::Run(int32_t curpc) int32_t handle = registers[SREG_MAR].ReadInt32(); void *object; ICCDynamicObject *manager; - ccGetObjectAddressAndManagerFromHandle(handle, object, manager); - reg1.SetDynamicObject( object, manager ); + ScriptValueType obj_type = ccGetObjectAddressAndManagerFromHandle(handle, object, manager); + if (obj_type == kScValPluginObject) + { + reg1.SetPluginObject( object, manager ); + } + else + { + reg1.SetDynamicObject( object, manager ); + } // if error occurred, cc_error will have been set if (ccError) @@ -951,7 +957,8 @@ int ccInstance::Run(int32_t curpc) { address = (char*)reg1.StcArr->GetElementPtr(reg1.Ptr, reg1.IValue); } - else if (reg1.Type == kScValDynamicObject) + else if (reg1.Type == kScValDynamicObject || + reg1.Type == kScValPluginObject) { address = reg1.Ptr; } @@ -980,7 +987,8 @@ int ccInstance::Run(int32_t curpc) { address = (char*)reg1.StcArr->GetElementPtr(reg1.Ptr, reg1.IValue); } - else if (reg1.Type == kScValDynamicObject) + else if (reg1.Type == kScValDynamicObject || + reg1.Type == kScValPluginObject) { address = reg1.Ptr; } @@ -1084,8 +1092,7 @@ int ccInstance::Run(int32_t curpc) return -1; } - if (next_call_needs_object) - next_call_needs_object = 0; + next_call_needs_object = 0; pc = oldpc; was_just_callas = func_callstack.Count; @@ -1110,10 +1117,33 @@ int ccInstance::Run(int32_t curpc) RuntimeScriptValue return_value; - if (next_call_needs_object) + if (reg1.Type == kScValPluginFunction) + { + GlobalReturnValue.Invalidate(); + int32_t int_ret_val; + if (next_call_needs_object) + { + RuntimeScriptValue obj_rval = registers[SREG_OP]; + obj_rval.DirectPtr(); + int_ret_val = call_function((intptr_t)reg1.Ptr, &obj_rval, num_args_to_func, func_callstack.GetHead() + 1); + } + else + { + int_ret_val = call_function((intptr_t)reg1.Ptr, NULL, num_args_to_func, func_callstack.GetHead() + 1); + } + + if (GlobalReturnValue.IsValid()) + { + return_value = GlobalReturnValue; + } + else + { + return_value.SetInt32(int_ret_val); + } + } + else if (next_call_needs_object) { // member function call - next_call_needs_object = 0; if (reg1.Type == kScValObjectFunction) { RuntimeScriptValue obj_rval = registers[SREG_OP]; @@ -1129,19 +1159,6 @@ int ccInstance::Run(int32_t curpc) { return_value = reg1.SPfn(func_callstack.GetHead() + 1, num_args_to_func); } - else if (reg1.Type == kScValPluginFunction) - { - GlobalReturnValue.Invalidate(); - int32_t int_ret_val = call_function((intptr_t)reg1.Ptr, num_args_to_func, func_callstack.GetHead() + 1); - if (GlobalReturnValue.IsValid()) - { - return_value = GlobalReturnValue; - } - else - { - return_value.SetInt32(int_ret_val); - } - } else if (reg1.Type == kScValObjectFunction) { cc_error("unexpected object function pointer on SCMD_CALLEXT"); @@ -1158,6 +1175,7 @@ int ccInstance::Run(int32_t curpc) registers[SREG_AX] = return_value; current_instance = this; + next_call_needs_object = 0; num_args_to_func = -1; break; } @@ -1179,7 +1197,7 @@ int ccInstance::Run(int32_t curpc) cc_error("!Null pointer referenced"); return -1; } - if (reg1.Type == kScValDynamicObject || + if (reg1.Type == kScValDynamicObject || reg1.Type == kScValPluginObject || // This might be an object of USER-DEFINED type, calling its MEMBER-FUNCTION. // Note, that this is the only case known when such object is written into reg[SREG_OP]; // in any other case that would count as error. diff --git a/Engine/script/runtimescriptvalue.h b/Engine/script/runtimescriptvalue.h index 9da904ac4d..7c60b53b94 100644 --- a/Engine/script/runtimescriptvalue.h +++ b/Engine/script/runtimescriptvalue.h @@ -38,6 +38,8 @@ enum ScriptValueType kScValStaticObject, // as a pointer to static global script object kScValStaticArray, // as a pointer to static global array (of static or dynamic objects) kScValDynamicObject,// as a pointer to managed script object + kScValPluginObject, // as a pointer to object managed by plugin (similar to + // kScValDynamicObject, but has backward-compatible limitations) kScValStaticFunction,// as a pointer to static function kScValPluginFunction,// temporary workaround for plugins (unsafe function ptr) kScValObjectFunction,// as a pointer to object member function, gets object pointer as @@ -227,6 +229,15 @@ struct RuntimeScriptValue Size = 4; return *this; } + inline RuntimeScriptValue &SetPluginObject(void *object, ICCDynamicObject *manager) + { + Type = kScValPluginObject; + IValue = 0; + Ptr = (char*)object; + DynMgr = manager; + Size = 4; + return *this; + } inline RuntimeScriptValue &SetStaticFunction(ScriptAPIFunction *pfn) { Type = kScValStaticFunction; diff --git a/Engine/script/script_runtime.cpp b/Engine/script/script_runtime.cpp index 1bd7a747bb..fa539f992f 100644 --- a/Engine/script/script_runtime.cpp +++ b/Engine/script/script_runtime.cpp @@ -141,7 +141,7 @@ void ccSetDebugHook(new_line_hook_type jibble) new_line_hook = jibble; } -int call_function(intptr_t addr, int numparm, const RuntimeScriptValue *parms) +int call_function(intptr_t addr, const RuntimeScriptValue *object, int numparm, const RuntimeScriptValue *parms) { if (!addr) { @@ -155,17 +155,23 @@ int call_function(intptr_t addr, int numparm, const RuntimeScriptValue *parms) } intptr_t parm_value[9]; - for (int i = 0; i < numparm; ++i) + if (object) { - switch (parms[i].Type) + parm_value[0] = (intptr_t)object->GetPtrWithOffset(); + numparm++; + } + + for (int ival = object ? 1 : 0, iparm = 0; ival < numparm; ++ival, ++iparm) + { + switch (parms[iparm].Type) { case kScValInteger: case kScValFloat: // AGS passes floats, copying their values into long variable - parm_value[i] = (intptr_t)parms[i].IValue; + parm_value[ival] = (intptr_t)parms[iparm].IValue; break; break; default: - parm_value[i] = (intptr_t)parms[i].GetPtrWithOffset(); + parm_value[ival] = (intptr_t)parms[iparm].GetPtrWithOffset(); break; } } diff --git a/Engine/script/script_runtime.h b/Engine/script/script_runtime.h index 09b3a9b1a7..f00fb68e36 100644 --- a/Engine/script/script_runtime.h +++ b/Engine/script/script_runtime.h @@ -68,5 +68,5 @@ extern void ccSetScriptAliveTimer (int); // reset the current while loop counter extern void ccNotifyScriptStillAlive (); // for calling exported plugin functions old-style -extern int call_function(intptr_t addr, int numparm, const RuntimeScriptValue *parms); +extern int call_function(intptr_t addr, const RuntimeScriptValue *object, int numparm, const RuntimeScriptValue *parms); extern void nullfree(void *data); // in script/script_runtime From 90310e5fb0f7822a53d792ffea10a155b3bb76da Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Fri, 29 Mar 2013 03:25:39 +0400 Subject: [PATCH 4/7] Script: added PluginArgument script value type When plugin calls a script function, it casts all parameters to 32-bit ints. If an adress of managed object was passed in such a way, script interpreter should know that it is allowed to use this value as one. --- Engine/plugin/agsplugin.cpp | 8 ++++---- Engine/script/cc_instance.cpp | 10 +++++++++- Engine/script/runtimescriptvalue.h | 11 +++++++++++ Engine/script/script_runtime.cpp | 1 + 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Engine/plugin/agsplugin.cpp b/Engine/plugin/agsplugin.cpp index f1b3ce019f..4586e6b89b 100644 --- a/Engine/plugin/agsplugin.cpp +++ b/Engine/plugin/agsplugin.cpp @@ -602,9 +602,9 @@ int IAGSEngine::CallGameScriptFunction(const char *name, int32 globalScript, int toRun = roominst; RuntimeScriptValue params[3]; - params[0].SetInt32(arg1); - params[1].SetInt32(arg2); - params[2].SetInt32(arg3); + params[0].SetPluginArgument(arg1); + params[1].SetPluginArgument(arg2); + params[2].SetPluginArgument(arg3); int toret = toRun->RunScriptFunctionIfExists((char*)name, numArgs, params); return toret; } @@ -676,7 +676,7 @@ void IAGSEngine::QueueGameScriptFunction(const char *name, int32 globalScript, i } strcat(scNameToRun, name); - curscript->run_another(scNameToRun, RuntimeScriptValue().SetInt32(arg1), RuntimeScriptValue().SetInt32(arg2)); + curscript->run_another(scNameToRun, RuntimeScriptValue().SetPluginArgument(arg1), RuntimeScriptValue().SetPluginArgument(arg2)); } int IAGSEngine::RegisterManagedObject(const void *object, IAGSScriptManagedObject *callback) { diff --git a/Engine/script/cc_instance.cpp b/Engine/script/cc_instance.cpp index 863b54b273..3e7d0bed43 100644 --- a/Engine/script/cc_instance.cpp +++ b/Engine/script/cc_instance.cpp @@ -962,6 +962,10 @@ int ccInstance::Run(int32_t curpc) { address = reg1.Ptr; } + else if (reg1.Type == kScValPluginArg) + { + address = (char*)reg1.IValue; + } // There's one possible case when the reg1 is 0, which means writing nullptr else if (!reg1.IsNull()) { @@ -992,6 +996,10 @@ int ccInstance::Run(int32_t curpc) { address = reg1.Ptr; } + else if (reg1.Type == kScValPluginArg) + { + address = (char*)reg1.IValue; + } // There's one possible case when the reg1 is 0, which means writing nullptr else if (!reg1.IsNull()) { @@ -1138,7 +1146,7 @@ int ccInstance::Run(int32_t curpc) } else { - return_value.SetInt32(int_ret_val); + return_value.SetPluginArgument(int_ret_val); } } else if (next_call_needs_object) diff --git a/Engine/script/runtimescriptvalue.h b/Engine/script/runtimescriptvalue.h index 7c60b53b94..86fd64985f 100644 --- a/Engine/script/runtimescriptvalue.h +++ b/Engine/script/runtimescriptvalue.h @@ -29,6 +29,8 @@ enum ScriptValueType kScValUndefined, // to detect errors kScValInteger, // as strictly 32-bit integer (for integer math) kScValFloat, // as float (for floating point math), 32-bit + kScValPluginArg, // an 32-bit value, passed to a script function when called + // directly by plugin; is allowed to represent object pointer kScValStackPtr, // as a pointer to stack entry kScValData, // as a container for randomly sized data (usually array) kScValGlobalVar, // as a pointer to script variable; used only for global vars, @@ -165,6 +167,15 @@ struct RuntimeScriptValue { return SetFloat(val ? 1.0F : 0.0F); } + inline RuntimeScriptValue &SetPluginArgument(int32_t val) + { + Type = kScValPluginArg; + IValue = val; + Ptr = NULL; + MgrPtr = NULL; + Size = 4; + return *this; + } inline RuntimeScriptValue &SetStackPtr(RuntimeScriptValue *stack_entry) { Type = kScValStackPtr; diff --git a/Engine/script/script_runtime.cpp b/Engine/script/script_runtime.cpp index fa539f992f..683236c889 100644 --- a/Engine/script/script_runtime.cpp +++ b/Engine/script/script_runtime.cpp @@ -167,6 +167,7 @@ int call_function(intptr_t addr, const RuntimeScriptValue *object, int numparm, { case kScValInteger: case kScValFloat: // AGS passes floats, copying their values into long variable + case kScValPluginArg: parm_value[ival] = (intptr_t)parms[iparm].IValue; break; break; From 5203a805128226457ab5956cb3ae73d2cf21074f Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Sun, 31 Mar 2013 00:03:14 +0400 Subject: [PATCH 5/7] Common, Engine: fixed few pointer conversion warnings --- Common/ac/gamesetupstructbase.cpp | 19 +++++++++++++------ Common/ac/gamesetupstructbase.h | 7 +++++++ Engine/ac/game.cpp | 6 ++---- Engine/ac/gamestate.cpp | 8 ++++---- Engine/ac/roomstatus.cpp | 4 ++-- Engine/media/audio/queuedaudioitem.cpp | 4 ++-- 6 files changed, 30 insertions(+), 18 deletions(-) diff --git a/Common/ac/gamesetupstructbase.cpp b/Common/ac/gamesetupstructbase.cpp index 4f194b1909..0c7b1982fb 100644 --- a/Common/ac/gamesetupstructbase.cpp +++ b/Common/ac/gamesetupstructbase.cpp @@ -52,9 +52,16 @@ void GameSetupStructBase::ReadFromFile(Stream *in) //for (i = 0; i < MAXGLOBALMES; i++) // messages[i] = (char*)in->ReadInt32(); + // The following pointers are used as flags at one point + // during game loading, therefore they are initialized with + // some values here. These values are never treated as + // actual addresses, only as boolean values. + // See: + // - GameSetupStruct::read_words_dictionary(), dict + // - load_game_file(), compiled_script dict = (WordsDictionary *) in->ReadInt32(); - globalscript = (char *) in->ReadInt32(); - chars = (CharacterInfo *) in->ReadInt32(); + in->ReadInt32(); // globalscript + in->ReadInt32(); // chars compiled_script = (ccScript *) in->ReadInt32(); } @@ -87,8 +94,8 @@ void GameSetupStructBase::WriteToFile(Stream *out) out->WriteArrayOfInt32(reserved, 17); // write the final ptrs so we know to load dictionary, scripts etc out->WriteArrayOfIntPtr32((intptr_t*)messages, MAXGLOBALMES); - out->WriteInt32((int32)dict); - out->WriteInt32((int32)globalscript); - out->WriteInt32((int32)chars); - out->WriteInt32((int32)compiled_script); + out->WriteInt32(dict ? 1 : 0); + out->WriteInt32(0); // globalscript + out->WriteInt32(0); // chars + out->WriteInt32(compiled_script ? 1 : 0); } diff --git a/Common/ac/gamesetupstructbase.h b/Common/ac/gamesetupstructbase.h index 4cd19e49d3..afa4f0b6e8 100644 --- a/Common/ac/gamesetupstructbase.h +++ b/Common/ac/gamesetupstructbase.h @@ -64,6 +64,13 @@ struct GameSetupStructBase { char *globalscript; CharacterInfo *chars; ccScript *compiled_script; + // [IKM] 2013-03-30 + // NOTE: it looks like nor 'globalscript', not 'compiled_script' are used + // to store actual script data anytime; 'ccScript* gamescript' global + // pointer is used for that instead. + // 'compiled_script' member is used once as a flag, to indicate that there's + // a global script data to be loaded; 'globalscript' is not used anywhere + // for anything useful. void ReadFromFile(Common::Stream *in); void WriteToFile(Common::Stream *out); diff --git a/Engine/ac/game.cpp b/Engine/ac/game.cpp index 2674cd7c02..193004687a 100644 --- a/Engine/ac/game.cpp +++ b/Engine/ac/game.cpp @@ -1157,10 +1157,10 @@ void save_game_room_state(Stream *out) out->Write(&roomstat->tsdata[0], roomstat->tsdatasize); } else - out->WriteInt8 (0); // <--- [IKM] added by me, CHECKME if needed / works correctly + out->WriteInt8(0); } else - out->WriteInt8 (0); + out->WriteInt8(0); } } @@ -1308,7 +1308,6 @@ void save_game_displayed_room_status(Stream *out) // save the current troom, in case they save in room 600 or whatever WriteRoomStatus_Aligned(&troom, out); - //out->WriteArray(&troom,sizeof(RoomStatus),1); if (troom.tsdatasize>0) out->Write(&troom.tsdata[0],troom.tsdatasize); @@ -1991,7 +1990,6 @@ void restore_game_displayed_room_status(Stream *in, Bitmap **newbscene) } else troom.tsdata = NULL; - } } diff --git a/Engine/ac/gamestate.cpp b/Engine/ac/gamestate.cpp index ab78d972d4..8007332fff 100644 --- a/Engine/ac/gamestate.cpp +++ b/Engine/ac/gamestate.cpp @@ -200,8 +200,8 @@ void GameState::ReadFromFile_v321(Stream *in) gamma_adjustment = in->ReadInt32(); temporarily_turned_off_character = in->ReadInt16(); inv_backwards_compatibility = in->ReadInt16(); - gui_draw_order = (int*)in->ReadInt32(); - do_once_tokens = (char**)in->ReadInt32(); + in->ReadInt32(); // gui_draw_order + in->ReadInt32(); // do_once_tokens; num_do_once_tokens = in->ReadInt32(); text_min_display_time_ms = in->ReadInt32(); ignore_user_input_after_text_timeout_ms = in->ReadInt32(); @@ -376,8 +376,8 @@ void GameState::WriteToFile_v321(Stream *out) out->WriteInt32( gamma_adjustment); out->WriteInt16(temporarily_turned_off_character); out->WriteInt16(inv_backwards_compatibility); - out->WriteInt32((int32)gui_draw_order); - out->WriteInt32((int32)do_once_tokens); + out->WriteInt32(0); // gui_draw_order + out->WriteInt32(0); // do_once_tokens out->WriteInt32( num_do_once_tokens); out->WriteInt32( text_min_display_time_ms); out->WriteInt32( ignore_user_input_after_text_timeout_ms); diff --git a/Engine/ac/roomstatus.cpp b/Engine/ac/roomstatus.cpp index ec0148d2b7..43e29a7e6a 100644 --- a/Engine/ac/roomstatus.cpp +++ b/Engine/ac/roomstatus.cpp @@ -28,7 +28,7 @@ void RoomStatus::ReadFromFile_v321(Stream *in) ReadRoomObjects_Aligned(in); in->ReadArrayOfInt16(flagstates, MAX_FLAGS); tsdatasize = in->ReadInt32(); - tsdata = (char *) in->ReadInt32(); + in->ReadInt32(); // tsdata for (int i = 0; i < MAX_HOTSPOTS; ++i) { intrHotspot[i].ReadFromFile(in); @@ -55,7 +55,7 @@ void RoomStatus::WriteToFile_v321(Stream *out) WriteRoomObjects_Aligned(out); out->WriteArrayOfInt16(flagstates, MAX_FLAGS); out->WriteInt32(tsdatasize); - out->WriteInt32((int)tsdata); + out->WriteInt32(0); // tsdata for (int i = 0; i < MAX_HOTSPOTS; ++i) { intrHotspot[i].WriteToFile(out); diff --git a/Engine/media/audio/queuedaudioitem.cpp b/Engine/media/audio/queuedaudioitem.cpp index 447ceab603..7e072b9a92 100644 --- a/Engine/media/audio/queuedaudioitem.cpp +++ b/Engine/media/audio/queuedaudioitem.cpp @@ -26,7 +26,7 @@ void QueuedAudioItem::ReadFromFile(Stream *in) audioClipIndex = in->ReadInt16(); priority = in->ReadInt16(); repeat = in->ReadBool(); - cachedClip = (SOUNDCLIP*)in->ReadInt32(); + in->ReadInt32(); // cachedClip } void QueuedAudioItem::WriteToFile(Stream *out) @@ -34,5 +34,5 @@ void QueuedAudioItem::WriteToFile(Stream *out) out->WriteInt16(audioClipIndex); out->WriteInt16(priority); out->WriteBool(repeat); - out->WriteInt32((int32)cachedClip); + out->WriteInt32(0); // cachedClip } From 1780e8441c5538114ea5ece233c1d14f66fce865 Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Mon, 1 Apr 2013 11:49:47 +0400 Subject: [PATCH 6/7] Editor: additions and corrections for the welcome page text --- Editor/AGS.Editor/Panes/WelcomePane.Designer.cs | 7 ++++--- Editor/AGS.Editor/Panes/WelcomePane.resx | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Editor/AGS.Editor/Panes/WelcomePane.Designer.cs b/Editor/AGS.Editor/Panes/WelcomePane.Designer.cs index 28ffa738b4..82d33766cd 100644 --- a/Editor/AGS.Editor/Panes/WelcomePane.Designer.cs +++ b/Editor/AGS.Editor/Panes/WelcomePane.Designer.cs @@ -144,7 +144,7 @@ private void InitializeComponent() this.lnkUpgradingFrom302.TabIndex = 4; this.lnkUpgradingFrom302.TabStop = true; this.lnkUpgradingFrom302.Text = "If you were using AGS 3.1.2, read this page which tells you the main breaking cha" + - "nges in AGS 3.3."; + "nges in AGS 3.2."; this.lnkUpgradingFrom302.UseCompatibleTextRendering = true; this.lnkUpgradingFrom302.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.lnkUpgradingFrom302_LinkClicked); // @@ -166,8 +166,9 @@ private void InitializeComponent() this.lblUpgradingInfo.Name = "lblUpgradingInfo"; this.lblUpgradingInfo.Size = new System.Drawing.Size(292, 28); this.lblUpgradingInfo.TabIndex = 2; - this.lblUpgradingInfo.Text = "As usual, this latest version of AGS brings you more goodies than you can shake a" + - " stick at!"; + // TODO: replace this line with something more generic for the next version; this one is for 3.3 only + this.lblUpgradingInfo.Text = "This is the first version of AGS ever released as the collaborative work of" + + " community members and fellow contributors."; // // lnkUpgrading // diff --git a/Editor/AGS.Editor/Panes/WelcomePane.resx b/Editor/AGS.Editor/Panes/WelcomePane.resx index 8e45bf4338..1fd15ed306 100644 --- a/Editor/AGS.Editor/Panes/WelcomePane.resx +++ b/Editor/AGS.Editor/Panes/WelcomePane.resx @@ -124,6 +124,8 @@ * Find where your characters, dialogs, views, inventory items and global variables are used using a simple menu click * Easy navigation from an open document to its location in the tree using a simple menu click * New shortcuts in the script editor to navigate to a specific line (Ctrl+G) and switch between header and script (Ctrl+M) +* The engine runs games made with previous versions of AGS as old as 2.5 +* Run games with x5, x6 ,x7 or x8 graphics scaling filter * And loads more! \ No newline at end of file From 9fc9417dfe6f453086ca3a67cbebe9bc6f6aaee4 Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Tue, 2 Apr 2013 01:39:12 +0400 Subject: [PATCH 7/7] Editor: updated splash bitmap (version 3.3) --- Editor/AGS.Editor/Resources/splash.bmp | Bin 300056 -> 300054 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Editor/AGS.Editor/Resources/splash.bmp b/Editor/AGS.Editor/Resources/splash.bmp index a044e7ff7e3a52f79a9f1502a6acdf3b2ac14eed..a8b482841d2168665e23c11dc9deb4edd38c289d 100644 GIT binary patch delta 7832 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 u>Nq(u5=MJq{s$uy5Fs_T$ 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 N(lHnQWj1Nj{{i1ax5xkh