diff --git a/source/common/engine/namedef.h b/source/common/engine/namedef.h index ef33f4522..0f4fb6340 100644 --- a/source/common/engine/namedef.h +++ b/source/common/engine/namedef.h @@ -117,6 +117,7 @@ xx(Double) xx(String) xx(Vector) xx(Map) +xx(MapIterator) xx(Array) xx(Include) xx(Sound) @@ -162,6 +163,14 @@ xx(ToVector) xx(Size) xx(Push) xx(Insert) +xx(InsertNew) +xx(Remove) +xx(Get) +xx(GetValue) +xx(GetKey) +xx(SetValue) +xx(CheckKey) +xx(Value) xx(Copy) xx(Move) xx(Voidptr) @@ -174,6 +183,7 @@ xx(IsNull) xx(Exists) xx(SetInvalid) xx(SetNull) +xx(Key) // color channels xx(a) diff --git a/source/common/engine/printf.h b/source/common/engine/printf.h index 964d7dc85..98af91ff8 100644 --- a/source/common/engine/printf.h +++ b/source/common/engine/printf.h @@ -88,7 +88,7 @@ int Printf (const char *format, ...) ATTRIBUTE((format(printf,1,2))); int DPrintf (int level, const char *format, ...) ATTRIBUTE((format(printf,2,3))); void I_DebugPrint(const char* cp); -void debugprintf(const char* f, ...); // Prints to the debugger's log. +void I_DebugPrintf(const char* fmt, ...); // Prints to the debugger's log. // flag to silence non-error output extern bool batchrun; diff --git a/source/common/engine/sc_man_scanner.re b/source/common/engine/sc_man_scanner.re index 1d18be71d..59c3c7712 100644 --- a/source/common/engine/sc_man_scanner.re +++ b/source/common/engine/sc_man_scanner.re @@ -167,6 +167,7 @@ std2: 'vector2' { RET(TK_Vector2); } 'vector3' { RET(TK_Vector3); } 'map' { RET(TK_Map); } + 'mapiterator' { RET(TK_MapIterator); } 'array' { RET(TK_Array); } 'in' { RET(TK_In); } 'sizeof' { RET(TK_SizeOf); } diff --git a/source/common/engine/sc_man_tokens.h b/source/common/engine/sc_man_tokens.h index 5aa6360fe..d93e5be62 100644 --- a/source/common/engine/sc_man_tokens.h +++ b/source/common/engine/sc_man_tokens.h @@ -128,6 +128,7 @@ xx(TK_Replaces, "'replaces'") xx(TK_Vector2, "'vector2'") xx(TK_Vector3, "'vector3'") xx(TK_Map, "'map'") +xx(TK_MapIterator, "'mapiterator'") xx(TK_Array, "'array'") xx(TK_In, "'in'") xx(TK_SizeOf, "'sizeof'") diff --git a/source/common/fonts/hexfont.cpp b/source/common/fonts/hexfont.cpp index 8b50427f4..d86f70f41 100644 --- a/source/common/fonts/hexfont.cpp +++ b/source/common/fonts/hexfont.cpp @@ -443,4 +443,5 @@ void LoadHexFont(const char* filename) auto hexfont = resf->FindLump("newconsolefont.hex"); if (hexfont == nullptr) I_FatalError("Unable to find newconsolefont.hex in %s", filename); hexdata.ParseDefinition(hexfont); + delete resf; } diff --git a/source/common/models/models_obj.cpp b/source/common/models/models_obj.cpp index a79caa9e4..e1d777c81 100644 --- a/source/common/models/models_obj.cpp +++ b/source/common/models/models_obj.cpp @@ -544,7 +544,7 @@ inline FVector3 FOBJModel::RealignVector(FVector3 vecToRealign) */ inline FVector2 FOBJModel::FixUV(FVector2 vecToRealign) { - vecToRealign.Y *= -1; + vecToRealign.Y = 1-vecToRealign.Y; return vecToRealign; } diff --git a/source/common/objects/dobject.cpp b/source/common/objects/dobject.cpp index f82f50580..ba4f0a0f1 100644 --- a/source/common/objects/dobject.cpp +++ b/source/common/objects/dobject.cpp @@ -42,6 +42,7 @@ #include "types.h" #include "i_time.h" #include "printf.h" +#include "maps.h" //========================================================================== // @@ -342,6 +343,17 @@ DEFINE_ACTION_FUNCTION(DObject, Destroy) // //========================================================================== +template +static void PropagateMarkMap(M *map) +{ + TMapIterator it(*map); + typename M::Pair * p; + while(it.NextPair(p)) + { + GC::Mark(&p->Value); + } +} + size_t DObject::PropagateMark() { const PClass *info = GetClass(); @@ -375,6 +387,27 @@ size_t DObject::PropagateMark() offsets++; } + { + const std::pair *maps = info->MapPointers; + if (maps == NULL) + { + const_cast(info)->BuildMapPointers(); + maps = info->MapPointers; + } + while (maps->first != ~(size_t)0) + { + if(maps->second->RegType == REGT_STRING) + { // FString,DObject* + PropagateMarkMap((ZSMap*)((uint8_t *)this + maps->first)); + } + else + { // uint32_t,DObject* + PropagateMarkMap((ZSMap*)((uint8_t *)this + maps->first)); + } + maps++; + } + + } return info->Size; } return 0; diff --git a/source/common/objects/dobjtype.cpp b/source/common/objects/dobjtype.cpp index 250dccbdd..fde9ea0e3 100644 --- a/source/common/objects/dobjtype.cpp +++ b/source/common/objects/dobjtype.cpp @@ -82,6 +82,8 @@ DEFINE_GLOBAL(WP_NOCHANGE); // A harmless non-nullptr FlatPointer for classes without pointers. static const size_t TheEnd = ~(size_t)0; +static const std::pair TheMapEnd = {~(size_t)0 , nullptr}; + //========================================================================== // // PClass :: WriteValue @@ -877,6 +879,68 @@ void PClass::BuildArrayPointers() } } +//========================================================================== +// +// PClass :: BuildMapPointers +// +// same as above, but creates a list to dynamic object arrays +// +//========================================================================== + +void PClass::BuildMapPointers() +{ + if (MapPointers != nullptr) + { // Already built: Do nothing. + return; + } + else if (ParentClass == nullptr) + { // No parent (i.e. DObject: FlatPointers is the same as Pointers. + MapPointers = &TheMapEnd; + } + else + { + ParentClass->BuildMapPointers(); + + TArray> ScriptPointers; + + // Collect all arrays to pointers in scripted fields. + for (auto field : Fields) + { + if (!(field->Flags & VARF_Native)) + { + field->Type->SetPointerMap(Defaults, unsigned(field->Offset), &ScriptPointers); + } + } + + if (ScriptPointers.Size() == 0) + { // No new pointers: Just use the same ArrayPointers as the parent. + MapPointers = ParentClass->MapPointers; + } + else + { // New pointers: Create a new FlatPointers array and add them. + int numSuperPointers; + + // Count pointers defined by superclasses. + for (numSuperPointers = 0; ParentClass->MapPointers[numSuperPointers].first != ~(size_t)0; numSuperPointers++) + { + } + + // Concatenate them into a new array + std::pair *flat = (std::pair*)ClassDataAllocator.Alloc(sizeof(std::pair) * (numSuperPointers + ScriptPointers.Size() + 1)); + if (numSuperPointers > 0) + { + memcpy(flat, ParentClass->MapPointers, sizeof(std::pair)*numSuperPointers); + } + if (ScriptPointers.Size() > 0) + { + memcpy(flat + numSuperPointers, &ScriptPointers[0], sizeof(std::pair) * ScriptPointers.Size()); + } + flat[numSuperPointers + ScriptPointers.Size()] = TheMapEnd; + MapPointers = flat; + } + } +} + //========================================================================== // // PClass :: NativeClass diff --git a/source/common/objects/dobjtype.h b/source/common/objects/dobjtype.h index f7996d89d..a21ab8c79 100644 --- a/source/common/objects/dobjtype.h +++ b/source/common/objects/dobjtype.h @@ -56,6 +56,7 @@ public: const size_t *Pointers = nullptr; // object pointers defined by this class *only* const size_t *FlatPointers = nullptr; // object pointers defined by this class and all its superclasses; not initialized by default const size_t *ArrayPointers = nullptr; // dynamic arrays containing object pointers. + const std::pair *MapPointers = nullptr; // maps containing object pointers. uint8_t *Defaults = nullptr; uint8_t *Meta = nullptr; // Per-class static script data unsigned Size = sizeof(DObject); @@ -84,6 +85,7 @@ public: void InitializeActorInfo(); void BuildFlatPointers(); void BuildArrayPointers(); + void BuildMapPointers(); void DestroySpecials(void *addr); void DestroyMeta(void *addr); const PClass *NativeClass() const; diff --git a/source/common/scripting/backend/codegen.cpp b/source/common/scripting/backend/codegen.cpp index 2bc262c96..6cf593fa2 100644 --- a/source/common/scripting/backend/codegen.cpp +++ b/source/common/scripting/backend/codegen.cpp @@ -8205,6 +8205,8 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) PContainerType *ccls = nullptr; + PFunction * afd_override = nullptr; + if (ctx.Class == nullptr) { // There's no way that a member function call can resolve to a constant so abort right away. @@ -8278,7 +8280,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) if (Self->ValueType->isRealPointer()) { auto pointedType = Self->ValueType->toPointer()->PointedType; - if (pointedType && pointedType->isDynArray()) + if (pointedType && (pointedType->isDynArray() || pointedType->isMap() || pointedType->isMapIterator())) { Self = new FxOutVarDereference(Self, Self->ScriptPosition); SAFE_RESOLVE(Self, ctx); @@ -8454,7 +8456,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) if (a->ValueType->isRealPointer()) { auto pointedType = a->ValueType->toPointer()->PointedType; - if (pointedType && pointedType->isDynArray()) + if (pointedType && (pointedType->isDynArray() || pointedType->isMap() || pointedType->isMapIterator())) { a = new FxOutVarDereference(a, a->ScriptPosition); SAFE_RESOLVE(a, ctx); @@ -8561,6 +8563,193 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) } } } + else if(Self->IsMap()) + { + PMap * m = static_cast(Self->ValueType); + Self->ValueType = m->BackingType; + + auto mapKeyType = m->KeyType; + auto mapValueType = m->ValueType; + + bool isObjMap = mapValueType->isObjectPointer(); + + if (PFunction **Override; (Override = m->FnOverrides.CheckKey(MethodName))) + { + afd_override = *Override; + } + + // Adapted from DynArray codegen + + int idx = 0; + for (auto &a : ArgList) + { + a = a->Resolve(ctx); + if (a == nullptr) + { + delete this; + return nullptr; + } + if (a->ValueType->isRealPointer()) + { + auto pointedType = a->ValueType->toPointer()->PointedType; + if (pointedType && (pointedType->isDynArray() || pointedType->isMap() || pointedType->isMapIterator())) + { + a = new FxOutVarDereference(a, a->ScriptPosition); + SAFE_RESOLVE(a, ctx); + } + } + if (isObjMap && (MethodName == NAME_Insert && idx == 1)) + { + // Null pointers are always valid. + if (!a->isConstant() || static_cast(a)->GetValue().GetPointer() != nullptr) + { + if (!a->ValueType->isObjectPointer() || + !static_cast(mapValueType)->PointedClass()->IsAncestorOf(static_cast(a->ValueType)->PointedClass())) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument"); + delete this; + return nullptr; + } + } + } + + if (a->IsMap()) + { + // Copy and Move must turn their parameter into a pointer to the backing struct type. + auto o = static_cast(a->ValueType); + auto backingtype = o->BackingType; + if (mapKeyType != o->KeyType || mapValueType != o->ValueType) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument"); + delete this; + return nullptr; + } + bool writable; + if (!a->RequestAddress(ctx, &writable)) + { + ScriptPosition.Message(MSG_ERROR, "Unable to dereference map variable"); + delete this; + return nullptr; + } + a->ValueType = NewPointer(backingtype); + + // Also change the field's type so the code generator can work with this (actually this requires swapping out the entire field.) + if (Self->ExprType == EFX_StructMember || Self->ExprType == EFX_ClassMember || Self->ExprType == EFX_StackVariable) + { + auto member = static_cast(Self); + auto newfield = Create(NAME_None, backingtype, 0, member->membervar->Offset); + member->membervar = newfield; + } + } + else if (a->IsPointer() && Self->ValueType->isPointer()) + { + // the only case which must be checked up front is for pointer arrays receiving a new element. + // Since there is only one native backing class it uses a neutral void pointer as its argument, + // meaning that FxMemberFunctionCall is unable to do a proper check. So we have to do it here. + if (a->ValueType != mapValueType) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument. Got %s, expected %s", a->ValueType->DescriptiveName(), mapValueType->DescriptiveName()); + delete this; + return nullptr; + } + } + idx++; + + } + } + else if(Self->IsMapIterator()) + { + PMapIterator * mi = static_cast(Self->ValueType); + Self->ValueType = mi->BackingType; + + auto mapKeyType = mi->KeyType; + auto mapValueType = mi->ValueType; + + bool isObjMap = mapValueType->isObjectPointer(); + + if (PFunction **Override; (Override = mi->FnOverrides.CheckKey(MethodName))) + { + afd_override = *Override; + } + + // Adapted from DynArray codegen + + int idx = 0; + for (auto &a : ArgList) + { + a = a->Resolve(ctx); + if (a == nullptr) + { + delete this; + return nullptr; + } + if (a->ValueType->isRealPointer()) + { + auto pointedType = a->ValueType->toPointer()->PointedType; + if (pointedType && (pointedType->isDynArray() || pointedType->isMap() || pointedType->isMapIterator())) + { + a = new FxOutVarDereference(a, a->ScriptPosition); + SAFE_RESOLVE(a, ctx); + } + } + if (isObjMap && (MethodName == NAME_SetValue && idx == 0)) + { + // Null pointers are always valid. + if (!a->isConstant() || static_cast(a)->GetValue().GetPointer() != nullptr) + { + if (!a->ValueType->isObjectPointer() || + !static_cast(mapValueType)->PointedClass()->IsAncestorOf(static_cast(a->ValueType)->PointedClass())) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument"); + delete this; + return nullptr; + } + } + } + + if (a->IsMap()) + { + // Copy and Move must turn their parameter into a pointer to the backing struct type. + auto o = static_cast(a->ValueType); + auto backingtype = o->BackingType; + if (mapKeyType != o->KeyType || mapValueType != o->ValueType) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument"); + delete this; + return nullptr; + } + bool writable; + if (!a->RequestAddress(ctx, &writable)) + { + ScriptPosition.Message(MSG_ERROR, "Unable to dereference map variable"); + delete this; + return nullptr; + } + a->ValueType = NewPointer(backingtype); + + // Also change the field's type so the code generator can work with this (actually this requires swapping out the entire field.) + if (Self->ExprType == EFX_StructMember || Self->ExprType == EFX_ClassMember || Self->ExprType == EFX_StackVariable) + { + auto member = static_cast(Self); + auto newfield = Create(NAME_None, backingtype, 0, member->membervar->Offset); + member->membervar = newfield; + } + } + else if (a->IsPointer() && Self->ValueType->isPointer()) + { + // the only case which must be checked up front is for pointer arrays receiving a new element. + // Since there is only one native backing class it uses a neutral void pointer as its argument, + // meaning that FxMemberFunctionCall is unable to do a proper check. So we have to do it here. + if (a->ValueType != mapValueType) + { + ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument. Got %s, expected %s", a->ValueType->DescriptiveName(), mapValueType->DescriptiveName()); + delete this; + return nullptr; + } + } + idx++; + } + } if (MethodName == NAME_GetParentClass && @@ -8635,7 +8824,7 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx) isresolved: bool error = false; - PFunction *afd = FindClassMemberFunction(cls, ctx.Class, MethodName, ScriptPosition, &error, ctx.Version, !ctx.FromDecorate); + PFunction *afd = afd_override ? afd_override : FindClassMemberFunction(cls, ctx.Class, MethodName, ScriptPosition, &error, ctx.Version, !ctx.FromDecorate); if (error) { delete this; @@ -9034,7 +9223,7 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) if (ArgList[i] && ArgList[i]->ValueType->isRealPointer()) { auto pointedType = ArgList[i]->ValueType->toPointer()->PointedType; - if (pointedType && pointedType->isDynArray()) + if (pointedType && (pointedType->isDynArray() || pointedType->isMap() || pointedType->isMapIterator())) { ArgList[i] = new FxOutVarDereference(ArgList[i], ArgList[i]->ScriptPosition); SAFE_RESOLVE(ArgList[i], ctx); @@ -11806,7 +11995,7 @@ FxExpression *FxOutVarDereference::Resolve(FCompileContext &ctx) SelfType = Self->ValueType->toPointer()->PointedType; ValueType = SelfType; - if (SelfType->GetRegType() == REGT_NIL && !SelfType->isRealPointer() && !SelfType->isDynArray()) + if (SelfType->GetRegType() == REGT_NIL && !SelfType->isRealPointer() && !SelfType->isDynArray() && !SelfType->isMap() && !SelfType->isMapIterator()) { ScriptPosition.Message(MSG_ERROR, "Cannot dereference pointer"); delete this; @@ -11835,7 +12024,7 @@ ExpEmit FxOutVarDereference::Emit(VMFunctionBuilder *build) regType = REGT_POINTER; loadOp = OP_LP; } - else if (SelfType->isDynArray()) + else if (SelfType->isDynArray() || SelfType->isMap() || SelfType->isMapIterator()) { regType = REGT_POINTER; loadOp = OP_MOVEA; diff --git a/source/common/scripting/backend/codegen.h b/source/common/scripting/backend/codegen.h index f31b4ea1e..e26cea84d 100644 --- a/source/common/scripting/backend/codegen.h +++ b/source/common/scripting/backend/codegen.h @@ -350,6 +350,8 @@ public: bool IsArray() const { return ValueType->isArray() || (ValueType->isPointer() && ValueType->toPointer()->PointedType->isArray()); } bool isStaticArray() const { return (ValueType->isPointer() && ValueType->toPointer()->PointedType->isStaticArray()); } // can only exist in pointer form. bool IsDynamicArray() const { return (ValueType->isDynArray()); } + bool IsMap() const { return ValueType->isMap(); } + bool IsMapIterator() const { return ValueType->isMapIterator(); } bool IsStruct() const { return ValueType->isStruct(); } bool IsNativeStruct() const { return (ValueType->isStruct() && static_cast(ValueType)->isNative); } diff --git a/source/common/scripting/core/maps.cpp b/source/common/scripting/core/maps.cpp new file mode 100644 index 000000000..bb7cd369b --- /dev/null +++ b/source/common/scripting/core/maps.cpp @@ -0,0 +1,483 @@ +#include "tarray.h" +#include "dobject.h" +#include "zstring.h" +#include "vm.h" +#include "types.h" +#include "v_draw.h" +#include "maps.h" + + +//========================================================================== +// +// MAP_GC_WRITE_BARRIER +// +//========================================================================== + + +#define MAP_GC_WRITE_BARRIER(x) { \ + TMapIterator it(*x);\ + typename M::Pair * p;\ + while(it.NextPair(p)){\ + GC::WriteBarrier(p->Value);\ + }\ +} + + +//========================================================================== +// +// MapCopy +// +//========================================================================== + + +template void MapCopy(M * self, M * other) +{ + if constexpr(std::is_same_v) + { + MAP_GC_WRITE_BARRIER(other); + } + *self = *other; // how does this handle self->info? TODO double check. + self->info->rev++; +} + + +//========================================================================== +// +// MapMove +// +//========================================================================== + + +template void MapMove(M * self, M * other) +{ + if constexpr(std::is_same_v) + { + MAP_GC_WRITE_BARRIER(other); + } + self->TransferFrom(*other); + self->info->rev++; + other->info->rev++; +} + + +//========================================================================== +// +// MapSwap +// +//========================================================================== + + +template void MapSwap(M * self, M * other) +{ + if constexpr(std::is_same_v) + { + MAP_GC_WRITE_BARRIER(other); + } + self->Swap(*other); + self->info->rev++; + other->info->rev++; +} + + +//========================================================================== +// +// MapClear +// +//========================================================================== + + +template void MapClear(M * self) +{ + if constexpr(std::is_same_v) + { + MAP_GC_WRITE_BARRIER(self); + } + self->Clear(); + self->info->rev++; +} + + +//========================================================================== +// +// +// +//========================================================================== + + +template +using expand_types_vm = +std::conditional_t || std::is_same_v, uint32_t , /* expand 8/16-bit to 32-bit */ + std::conditional_t , double , /* expand float to double */ + std::conditional_t , const FString & , T>>>; /* change String to String ref */ + +template unsigned int MapCountUsed(M * self) +{ + return self->CountUsed(); +} + +template expand_types_vm MapGet(M * self,expand_types_vm key) +{ + typename M::ValueType * v = self->CheckKey(key); + if (v) { + return *v; + } + else + { + typename M::ValueType n {}; + self->Insert(key,n); + return n; + } +} + +template void MapGetString(M * self,expand_types_vm key, FString &out) +{ + FString * v = self->CheckKey(key); + if (v) { + out = *v; + } + else + { + out = FString(); + self->Insert(key,out); + } +} + +template int MapCheckKey(M * self, expand_types_vm key) +{ + return self->CheckKey(key) != nullptr; +} + + +//========================================================================== +// +// MapInsert +// +//========================================================================== + + +template void MapInsert(M * self, expand_types_vm key, expand_types_vm value) +{ + if constexpr(std::is_same_v) + { + MAP_GC_WRITE_BARRIER(self); + GC::WriteBarrier(value); + } + self->Insert(key, value); + self->info->rev++; // invalidate iterators +} + + +//========================================================================== +// +// +// +//========================================================================== + + +template void MapInsertNew(M * self, expand_types_vm key) +{ + if constexpr(std::is_same_v) + { + MAP_GC_WRITE_BARRIER(self); + } + self->Insert(key,{}); + self->info->rev++; // invalidate iterators +} + +template void MapRemove(M * self, expand_types_vm key) +{ + self->Remove(key); + self->info->rev++; // invalidate iterators +} + + +//========================================================================== +// +// +// +//========================================================================== + + +template int MapIteratorInit(I * self, M * other) +{ + return self->Init(*other); +} + +template int MapIteratorReInit(I * self) +{ + return self->ReInit(); +} + +template int MapIteratorValid(I * self) +{ + return self->Valid(); +} + +template int MapIteratorNext(I * self) +{ + return self->Next(); +} + +template expand_types_vm MapIteratorGetKey(I * self) +{ + return self->GetKey(); +} + +template void MapIteratorGetKeyString(I * self, FString &out) +{ + out = self->GetKey(); +} + +template expand_types_vm MapIteratorGetValue(I * self) +{ + return self->GetValue(); +} + +template void MapIteratorGetValueString(I * self, FString &out) +{ + out = self->GetValue(); +} + +template void MapIteratorSetValue(I * self, expand_types_vm value) +{ + auto & val = self->GetValue(); + if constexpr(std::is_same_v) + { + GC::WriteBarrier(val); + GC::WriteBarrier(value); + } + val = value; +} + + +//========================================================================== +// +// +// +//========================================================================== + +#define PARAM_VOIDPOINTER(X) PARAM_POINTER( X , void ) +#define PARAM_OBJPOINTER(X) PARAM_OBJECT( X , DObject ) + +#define _DEF_MAP( name, key_type, value_type, PARAM_KEY, PARAM_VALUE ) \ + typedef ZSMap name;\ + DEFINE_ACTION_FUNCTION_NATIVE( name , Copy , MapCopy< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_POINTER( other, name ); \ + MapCopy(self , other); \ + return 0; \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , Move , MapMove< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_POINTER( other, name ); \ + MapMove(self , other); \ + return 0; \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , Swap , MapSwap< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_POINTER( other, name ); \ + MapSwap(self , other); \ + return 0; \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , Clear , MapClear< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + MapClear(self); \ + return 0; \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name, CountUsed, MapCountUsed< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_INT( MapCountUsed(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name, CheckKey, MapCheckKey< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_KEY( key ); \ + ACTION_RETURN_BOOL( MapCheckKey(self, key) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name, Insert, MapInsert< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_KEY( key ); \ + PARAM_VALUE( value ); \ + MapInsert(self, key, value); \ + return 0; \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name, InsertNew, MapInsertNew< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_KEY( key ); \ + MapInsertNew(self, key); \ + return 0; \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name, Remove, MapRemove< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_KEY( key ); \ + MapRemove(self, key); \ + return 0; \ + } + +#define DEF_MAP_X_X( name, key_type, value_type, PARAM_KEY, PARAM_VALUE , ACTION_RETURN_VALUE ) \ + _DEF_MAP( name , key_type , value_type , PARAM_KEY , PARAM_VALUE ) \ + DEFINE_ACTION_FUNCTION_NATIVE( name, Get, MapGet< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_KEY( key ); \ + ACTION_RETURN_VALUE( MapGet(self, key) ); \ + } + +#define DEF_MAP_X_S( name, key_type, PARAM_KEY ) \ + _DEF_MAP( name , key_type , FString , PARAM_KEY , PARAM_STRING ) \ + DEFINE_ACTION_FUNCTION_NATIVE( name, Get, MapGetString< name >) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_KEY( key ); \ + FString out; \ + MapGetString(self, key, out); \ + ACTION_RETURN_STRING( out ); \ + } + +#define COMMA , + +#define _DEF_MAP_IT( map_name , name, key_type, value_type, PARAM_VALUE ) \ + typedef ZSMapIterator name; \ + DEFINE_ACTION_FUNCTION_NATIVE( name , Init , MapIteratorInit< name COMMA map_name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_POINTER( other, map_name ); \ + ACTION_RETURN_BOOL( MapIteratorInit(self , other) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , ReInit , MapIteratorReInit< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_BOOL( MapIteratorReInit(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , Valid , MapIteratorValid< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_BOOL( MapIteratorValid(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , Next , MapIteratorNext< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_BOOL( MapIteratorNext(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , SetValue , MapIteratorSetValue< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + PARAM_VALUE( value ); \ + MapIteratorSetValue(self, value); \ + return 0; \ + } +/* + DEFINE_ACTION_FUNCTION_NATIVE( name , GetKey , MapIteratorGetKey< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_KEY( MapIteratorGetKey(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , GetValue , MapIteratorGetValue< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_VALUE( MapIteratorGetValue(self) ); \ + } \ +*/ + +#define DEF_MAP_IT_I_X( map_name , name , value_type , PARAM_VALUE , ACTION_RETURN_VALUE ) \ + _DEF_MAP_IT( map_name , name , uint32_t , value_type , PARAM_VALUE ) \ + DEFINE_ACTION_FUNCTION_NATIVE( name , GetKey , MapIteratorGetKey< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_INT( MapIteratorGetKey(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , GetValue , MapIteratorGetValue< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_VALUE( MapIteratorGetValue(self) ); \ + } + +#define DEF_MAP_IT_S_X( map_name , name , value_type , PARAM_VALUE , ACTION_RETURN_VALUE ) \ + _DEF_MAP_IT( map_name , name , FString , value_type , PARAM_VALUE ) \ + DEFINE_ACTION_FUNCTION_NATIVE( name , GetKey , MapIteratorGetKeyString< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + FString out; \ + MapIteratorGetKeyString(self , out); \ + ACTION_RETURN_STRING( out ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( name , GetValue , MapIteratorGetValue< name > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( name ); \ + ACTION_RETURN_VALUE( MapIteratorGetValue(self) ); \ + } + +#define DEF_MAP_IT_I_S() \ + _DEF_MAP_IT( FMap_I32_Str , FMapIterator_I32_Str , uint32_t , FString , PARAM_STRING ) \ + DEFINE_ACTION_FUNCTION_NATIVE( FMapIterator_I32_Str , GetKey , MapIteratorGetKey< FMapIterator_I32_Str > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( FMapIterator_I32_Str ); \ + ACTION_RETURN_INT( MapIteratorGetKey(self) ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( FMapIterator_I32_Str , GetValue , MapIteratorGetValue< FMapIterator_I32_Str > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( FMapIterator_I32_Str ); \ + ACTION_RETURN_STRING( MapIteratorGetValue(self) ); \ + } + +#define DEF_MAP_IT_S_S() \ + _DEF_MAP_IT( FMap_Str_Str , FMapIterator_Str_Str , FString , FString , PARAM_STRING ) \ + DEFINE_ACTION_FUNCTION_NATIVE( FMapIterator_Str_Str , GetKey , MapIteratorGetKeyString< FMapIterator_Str_Str > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( FMapIterator_Str_Str ); \ + FString out; \ + MapIteratorGetKeyString(self , out); \ + ACTION_RETURN_STRING( out ); \ + } \ + DEFINE_ACTION_FUNCTION_NATIVE( FMapIterator_Str_Str , GetValue , MapIteratorGetValueString< FMapIterator_Str_Str > ) \ + { \ + PARAM_SELF_STRUCT_PROLOGUE( FMapIterator_Str_Str ); \ + FString out; \ + MapIteratorGetValueString(self , out); \ + ACTION_RETURN_STRING( out ); \ + } + + + +#define DEFINE_MAP_AND_IT_I_X( name , value_type , PARAM_VALUE , ACTION_RETURN_VALUE ) \ + DEF_MAP_X_X( FMap_ ## name , uint32_t , value_type , PARAM_INT , PARAM_VALUE , ACTION_RETURN_VALUE ) \ + DEF_MAP_IT_I_X( FMap_ ## name , FMapIterator_ ## name , value_type , PARAM_VALUE , ACTION_RETURN_VALUE ) + + +#define DEFINE_MAP_AND_IT_S_X( name , value_type , PARAM_VALUE , ACTION_RETURN_VALUE ) \ + DEF_MAP_X_X( FMap_ ## name , FString , value_type , PARAM_STRING , PARAM_VALUE , ACTION_RETURN_VALUE ) \ + DEF_MAP_IT_S_X( FMap_ ## name , FMapIterator_ ## name , value_type , PARAM_VALUE , ACTION_RETURN_VALUE ) + +#define DEFINE_MAP_AND_IT_I_S() \ + DEF_MAP_X_S( FMap_I32_Str , uint32_t , PARAM_INT ) \ + DEF_MAP_IT_I_S() + +#define DEFINE_MAP_AND_IT_S_S() \ + DEF_MAP_X_S( FMap_Str_Str , FString , PARAM_STRING ) \ + DEF_MAP_IT_S_S() + +DEFINE_MAP_AND_IT_I_X(I32_I8 , uint8_t , PARAM_INT , ACTION_RETURN_INT); +DEFINE_MAP_AND_IT_I_X(I32_I16 , uint16_t , PARAM_INT , ACTION_RETURN_INT); +DEFINE_MAP_AND_IT_I_X(I32_I32 , uint32_t , PARAM_INT , ACTION_RETURN_INT); +DEFINE_MAP_AND_IT_I_X(I32_F32 , float , PARAM_FLOAT , ACTION_RETURN_FLOAT); +DEFINE_MAP_AND_IT_I_X(I32_F64 , double , PARAM_FLOAT , ACTION_RETURN_FLOAT); +DEFINE_MAP_AND_IT_I_X(I32_Obj , DObject* , PARAM_OBJPOINTER , ACTION_RETURN_OBJECT); +DEFINE_MAP_AND_IT_I_X(I32_Ptr , void* , PARAM_VOIDPOINTER , ACTION_RETURN_POINTER); +DEFINE_MAP_AND_IT_I_S(); + +DEFINE_MAP_AND_IT_S_X(Str_I8 , uint8_t , PARAM_INT , ACTION_RETURN_INT); +DEFINE_MAP_AND_IT_S_X(Str_I16 , uint16_t , PARAM_INT , ACTION_RETURN_INT); +DEFINE_MAP_AND_IT_S_X(Str_I32 , uint32_t , PARAM_INT , ACTION_RETURN_INT); +DEFINE_MAP_AND_IT_S_X(Str_F32 , float , PARAM_FLOAT , ACTION_RETURN_FLOAT); +DEFINE_MAP_AND_IT_S_X(Str_F64 , double , PARAM_FLOAT , ACTION_RETURN_FLOAT); +DEFINE_MAP_AND_IT_S_X(Str_Obj , DObject* , PARAM_OBJPOINTER , ACTION_RETURN_OBJECT); +DEFINE_MAP_AND_IT_S_X(Str_Ptr , void* , PARAM_VOIDPOINTER , ACTION_RETURN_POINTER); +DEFINE_MAP_AND_IT_S_S(); \ No newline at end of file diff --git a/source/common/scripting/core/maps.h b/source/common/scripting/core/maps.h new file mode 100644 index 000000000..ed303537b --- /dev/null +++ b/source/common/scripting/core/maps.h @@ -0,0 +1,110 @@ +#pragma once + +#include +#include "tarray.h" +#include "refcounted.h" + +class ZSMapInfo : public RefCountedBase +{ +public: + void * self = nullptr; + int rev = 0; +}; + +struct ZSFMap : FMap { + RefCountedPtr info; +}; + +template +class ZSMap : public TMap +{ +public: + RefCountedPtr info; + ZSMap() : + TMap(), info(new ZSMapInfo) + { + info->self = this; + } + ~ZSMap() + { + info->self = nullptr; + } +}; + +template +struct ZSMapIterator +{ + RefCountedPtr info; + TMapIterator *it = nullptr; + typename ZSMap::Pair *p = nullptr; + + typedef KT KeyType; + typedef VT ValueType; + + int rev = 0; + + ~ZSMapIterator() + { + if(it) delete it; + } + + bool Valid() + { + return it && p && info.get() && info->self && info->rev == rev; + } + + bool ReInit() + { + if(info.get() && info->self) { + if(it) delete it; + it = new TMapIterator(*static_cast*>(info->self)); + rev = info->rev; + p = nullptr; + return true; + } + return false; + } + + bool Init(ZSMap &m) + { + info = m.info; + return ReInit(); + } + + bool Next() + { + if(it && info.get() && info->self && info->rev == rev) + { + p = nullptr; + return it->NextPair(p); + } + else + { + ThrowAbortException(X_FORMAT_ERROR,"MapIterator::Next called from invalid iterator"); + } + } + + VT& GetValue() + { + if(p && info.get() && info->self && info->rev == rev) + { + return p->Value; + } + else + { + ThrowAbortException(X_FORMAT_ERROR,p ? "MapIterator::GetValue called from invalid iterator" : "MapIterator::GetValue called from invalid position"); + } + } + + const KT& GetKey() + { + if(p && info.get() && info->self && info->rev == rev) + { + return p->Key; + } + else + { + ThrowAbortException(X_FORMAT_ERROR,p ? "MapIterator::GetKey called from invalid iterator" : "MapIterator::GetKey called from invalid position"); + } + } +}; \ No newline at end of file diff --git a/source/common/scripting/core/types.cpp b/source/common/scripting/core/types.cpp index 376587d15..702277e89 100644 --- a/source/common/scripting/core/types.cpp +++ b/source/common/scripting/core/types.cpp @@ -33,11 +33,14 @@ ** */ +#include + #include "vmintern.h" #include "s_soundinternal.h" #include "types.h" #include "printf.h" #include "textureid.h" +#include "maps.h" FTypeTable TypeTable; @@ -183,7 +186,7 @@ void PType::SetDefaultValue(void *base, unsigned offset, TArray //========================================================================== // -// PType :: SetDefaultValue +// PType :: SetPointer* // //========================================================================== @@ -195,6 +198,10 @@ void PType::SetPointerArray(void *base, unsigned offset, TArray *stroffs { } +void PType::SetPointerMap(void *base, unsigned offset, TArray> *ptrofs) +{ +} + //========================================================================== // // PType :: InitializeValue @@ -1862,7 +1869,7 @@ void PArray::SetPointer(void *base, unsigned offset, TArray *special) void PArray::SetPointerArray(void *base, unsigned offset, TArray *special) { - if (ElementType->isStruct()) + if (ElementType->isStruct() || ElementType->isDynArray()) { for (unsigned int i = 0; i < ElementCount; ++i) { @@ -1871,6 +1878,23 @@ void PArray::SetPointerArray(void *base, unsigned offset, TArray *specia } } +//========================================================================== +// +// PArray :: SetPointerMap +// +//========================================================================== + +void PArray::SetPointerMap(void *base, unsigned offset, TArray> *special) +{ + if(ElementType->isStruct() || ElementType->isMap()) + { + for (unsigned int i = 0; i < ElementCount; ++i) + { + ElementType->SetPointerMap(base, offset + ElementSize * i, special); + } + } +} + //========================================================================== // // NewArray @@ -2202,12 +2226,89 @@ PDynArray *NewDynArray(PType *type) // //========================================================================== -PMap::PMap(PType *keytype, PType *valtype) -: KeyType(keytype), ValueType(valtype) +enum OverrideFunctionRetType { + OFN_RET_VOID, + OFN_RET_VAL, + OFN_RET_KEY, + OFN_RET_BOOL, +}; +enum OverrideFunctionArgType { + OFN_ARG_VOID, + OFN_ARG_KEY, + OFN_ARG_VAL, + OFN_ARG_KEY_VAL, +}; + +template +void CreateOverrideFunction(MT *self, FName name) +{ + auto Fn = Create(self->BackingType, name); + auto NativeFn = FindFunction(self->BackingType, name.GetChars()); + + assert(NativeFn); + assert(NativeFn->VMPointer); + + + TArray ret; + TArray args; + TArray argflags; + TArray argnames; + + if constexpr(RetType == OFN_RET_VAL) + { + ret.Push(self->ValueType); + } + else if constexpr(RetType == OFN_RET_KEY) + { + ret.Push(self->KeyType); + } + else if constexpr(RetType == OFN_RET_BOOL) + { + ret.Push(TypeBool); + } + + args.Push(NewPointer(self->BackingType)); + argnames.Push(NAME_self); + argflags.Push(VARF_Implicit | VARF_ReadOnly); + + if constexpr(ArgType == OFN_ARG_KEY) + { + args.Push(self->KeyType); + argflags.Push(0); + argnames.Push(NAME_Key); + } + else if constexpr(ArgType == OFN_ARG_VAL) + { + + args.Push(self->ValueType); + argflags.Push(0); + argnames.Push(NAME_Value); + } + else if constexpr(ArgType == OFN_ARG_KEY_VAL) + { + args.Push(self->KeyType); + args.Push(self->ValueType); + argflags.Push(0); + argflags.Push(0); + argnames.Push(NAME_Key); + argnames.Push(NAME_Value); + } + + Fn->AddVariant(NewPrototype(ret, args), argflags, argnames, *NativeFn->VMPointer, VARF_Method | VARF_Native,SUF_ACTOR | SUF_OVERLAY | SUF_WEAPON | SUF_ITEM); + self->FnOverrides.Insert(name, Fn); +} + +PMap::PMap(PType *keytype, PType *valtype, PStruct *backing, int backing_class) +: KeyType(keytype), ValueType(valtype), BackingType(backing), BackingClass((decltype(BackingClass)) backing_class) { mDescriptiveName.Format("Map<%s, %s>", keytype->DescriptiveName(), valtype->DescriptiveName()); - Size = sizeof(FMap); - Align = alignof(FMap); + Size = sizeof(ZSFMap); + Align = alignof(ZSFMap); + CreateOverrideFunction(this, NAME_Get); + CreateOverrideFunction(this, NAME_CheckKey); + CreateOverrideFunction(this, NAME_Insert); + CreateOverrideFunction(this, NAME_InsertNew); + CreateOverrideFunction(this, NAME_Remove); } //========================================================================== @@ -2236,6 +2337,345 @@ void PMap::GetTypeIDs(intptr_t &id1, intptr_t &id2) const id2 = (intptr_t)ValueType; } +//========================================================================== +// +// PMap :: InitializeValue +// +//========================================================================== + +void PMap::Construct(void * addr) const { + switch(BackingClass) + { + case MAP_I32_I8: + new(addr) ZSMap(); + break; + case MAP_I32_I16: + new(addr) ZSMap(); + break; + case MAP_I32_I32: + new(addr) ZSMap(); + break; + case MAP_I32_F32: + new(addr) ZSMap(); + break; + case MAP_I32_F64: + new(addr) ZSMap(); + break; + case MAP_I32_OBJ: + new(addr) ZSMap(); + break; + case MAP_I32_PTR: + new(addr) ZSMap(); + break; + case MAP_I32_STR: + new(addr) ZSMap(); + break; + case MAP_STR_I8: + new(addr) ZSMap(); + break; + case MAP_STR_I16: + new(addr) ZSMap(); + break; + case MAP_STR_I32: + new(addr) ZSMap(); + break; + case MAP_STR_F32: + new(addr) ZSMap(); + break; + case MAP_STR_F64: + new(addr) ZSMap(); + break; + case MAP_STR_OBJ: + new(addr) ZSMap(); + break; + case MAP_STR_PTR: + new(addr) ZSMap(); + break; + case MAP_STR_STR: + new(addr) ZSMap(); + break; + }; +} + +void PMap::InitializeValue(void *addr, const void *def) const +{ + if (def != nullptr) + { + I_Error("Map cannot have default values"); + } + Construct(addr); +} + +//========================================================================== +// +// PMap :: DestroyValue +// +//========================================================================== + +void PMap::DestroyValue(void *addr) const +{ + switch(BackingClass) + { + case MAP_I32_I8: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_I16: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_I32: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_F32: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_F64: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_OBJ: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_PTR: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_I32_STR: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_I8: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_I16: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_I32: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_F32: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_F64: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_OBJ: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_PTR: + static_cast*>(addr)->~ZSMap(); + break; + case MAP_STR_STR: + static_cast*>(addr)->~ZSMap(); + break; + } +} + +//========================================================================== +// +// PMap :: SetDefaultValue +// +//========================================================================== + +void PMap::SetDefaultValue(void *base, unsigned offset, TArray *special) +{ + assert(!(base && special)); + if (base != nullptr) + { + Construct(((uint8_t*)base)+offset); // is this needed? string/dynarray do this initialization if base != nullptr, but their initialization doesn't need to allocate + // it might lead to double allocations (and memory leakage) for Map if both base and special != nullptr + } + if (special != nullptr) + { + special->Push(std::make_pair(this, offset)); + } + else + { + I_Error("null special"); + } +} + +//========================================================================== +// +// PMap :: SetPointer +// +//========================================================================== + +void PMap::SetPointerMap(void *base, unsigned offset, TArray> *special) +{ + if (ValueType->isObjectPointer()) + { + // Add to the list of pointer arrays for this class. + special->Push(std::make_pair(offset,KeyType)); + } +} + +//========================================================================== +// +// PMap :: WriteValue +// +//========================================================================== + +template +static void PMapValueWriter(FSerializer &ar, const M *map, const PMap *m) +{ + TMapConstIterator it(*map); + const typename M::Pair * p; + while(it.NextPair(p)) + { + if constexpr(std::is_same_v) + { + m->ValueType->WriteValue(ar,p->Key.GetChars(),static_cast(&p->Value)); + } + else if constexpr(std::is_same_v) + { + FString key; + key.Format("%u",p->Key); + m->ValueType->WriteValue(ar,key.GetChars(),static_cast(&p->Value)); + } + //else unknown key type + } +} + +void PMap::WriteValue(FSerializer &ar, const char *key, const void *addr) const +{ + if(ar.BeginObject(key)) + { + switch(BackingClass) + { + case MAP_I32_I8: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_I16: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_I32: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_F32: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_F64: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_OBJ: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_PTR: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_I32_STR: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_I8: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_I16: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_I32: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_F32: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_F64: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_OBJ: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_PTR: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + case MAP_STR_STR: + PMapValueWriter(ar, static_cast*>(addr), this); + break; + } + ar.EndObject(); + } +} + +//========================================================================== +// +// PMap :: ReadValue +// +//========================================================================== + + +template +static bool PMapValueReader(FSerializer &ar, M *map, const PMap *m) +{ + const char * k; + while(k = ar.GetKey()) + { + typename M::ValueType * val; + if constexpr(std::is_same_v) + { + val = &map->InsertNew(k); + } + else if constexpr(std::is_same_v) + { + FString s(k); + if(!s.IsInt()) + { + ar.EndObject(); + return false; + } + val = &map->InsertNew(static_cast(s.ToULong())); + } + if (!m->ValueType->ReadValue(ar,nullptr,static_cast(val))) + { + ar.EndObject(); + return false; + } + } + ar.EndObject(); + return true; +} + + +bool PMap::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + DestroyValue(addr); + InitializeValue(addr, nullptr); + if(ar.BeginObject(key)) + { + switch(BackingClass) + { + case MAP_I32_I8: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_I16: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_I32: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_F32: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_F64: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_OBJ: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_PTR: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_I32_STR: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_I8: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_I16: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_I32: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_F32: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_F64: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_OBJ: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_PTR: + return PMapValueReader(ar, static_cast*>(addr), this); + case MAP_STR_STR: + return PMapValueReader(ar, static_cast*>(addr), this); + } + } + return false; +} + //========================================================================== // // NewMap @@ -2245,16 +2685,330 @@ void PMap::GetTypeIDs(intptr_t &id1, intptr_t &id2) const // //========================================================================== -PMap *NewMap(PType *keytype, PType *valuetype) +int PMapBackingClass(PType *keytype, PType *valuetype, FString &backingName) { + int backingClass; + auto key_rtype = keytype->GetRegType(); + auto value_rtype = valuetype->GetRegType(); + if (key_rtype == REGT_INT) + { + if(keytype->Size != 4) + { + I_Error("Unsupported map requested"); + } + else + { + backingName += "I32_"; + backingClass = PMap::MAP_I32_I8; + } + } + else if (key_rtype == REGT_STRING) + { + backingName += "Str_"; + backingClass = PMap::MAP_STR_I8; + } + else + { + I_Error("Unsupported map requested"); + } + switch (valuetype->GetRegType()) + { + case REGT_INT: + backingName.AppendFormat("I%d", valuetype->Size * 8); + backingClass += (valuetype->Size >> 1); + break; + case REGT_FLOAT: + backingName.AppendFormat("F%d", valuetype->Size * 8); + backingClass += PMap::MAP_I32_F32 + (valuetype->Size == 8); + break; + case REGT_STRING: + backingName += "Str"; + backingClass += PMap::MAP_I32_STR; + break; + + case REGT_POINTER: + if (valuetype->isObjectPointer()) + { + backingName += "Obj"; + backingClass += PMap::MAP_I32_OBJ; + } + else + { + backingName += "Ptr"; + backingClass += PMap::MAP_I32_PTR; + } + break; + default: + I_Error("Unsupported map requested"); + break; + } + return backingClass; +} + +PMap *NewMap(PType *keyType, PType *valueType) { size_t bucket; - PType *maptype = TypeTable.FindType(NAME_Map, (intptr_t)keytype, (intptr_t)valuetype, &bucket); - if (maptype == nullptr) + PType *mapType = TypeTable.FindType(NAME_Map, (intptr_t)keyType, (intptr_t)valueType, &bucket); + if (mapType == nullptr) { - maptype = new PMap(keytype, valuetype); - TypeTable.AddType(maptype, NAME_Map, (intptr_t)keytype, (intptr_t)valuetype, bucket); + FString backingName = "Map_"; + int backingClass = PMapBackingClass(keyType, valueType, backingName); + + auto backing = NewStruct(backingName, nullptr, true); + mapType = new PMap(keyType, valueType, backing, backingClass); + TypeTable.AddType(mapType, NAME_Map, (intptr_t)keyType, (intptr_t)valueType, bucket); + } - return (PMap *)maptype; + return (PMap *)mapType; +} + +/* PMap *******************************************************************/ + +//========================================================================== +// +// PMap - Parameterized Constructor +// +//========================================================================== + +PMapIterator::PMapIterator(PType *keytype, PType *valtype, PStruct *backing, int backing_class) + : KeyType(keytype), ValueType(valtype), BackingType(backing), BackingClass((decltype(BackingClass)) backing_class) +{ + mDescriptiveName.Format("MapIterator<%s, %s>", keytype->DescriptiveName(), valtype->DescriptiveName()); + Size = sizeof(ZSFMap); + Align = alignof(ZSFMap); + CreateOverrideFunction(this, NAME_GetKey); + CreateOverrideFunction(this, NAME_GetValue); + CreateOverrideFunction(this, NAME_SetValue); +} + +//========================================================================== +// +// PMapIterator :: IsMatch +// +//========================================================================== + +bool PMapIterator::IsMatch(intptr_t id1, intptr_t id2) const +{ + const PType *keyty = (const PType *)id1; + const PType *valty = (const PType *)id2; + + return keyty == KeyType && valty == ValueType; +} + +//========================================================================== +// +// PMapIterator :: GetTypeIDs +// +//========================================================================== + +void PMapIterator::GetTypeIDs(intptr_t &id1, intptr_t &id2) const +{ + id1 = (intptr_t)KeyType; + id2 = (intptr_t)ValueType; +} + +//========================================================================== +// +// PMapIterator :: InitializeValue +// +//========================================================================== + +void PMapIterator::Construct(void * addr) const { + switch(BackingClass) + { + case PMap::MAP_I32_I8: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_I16: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_I32: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_F32: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_F64: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_OBJ: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_PTR: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_I32_STR: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_I8: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_I16: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_I32: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_F32: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_F64: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_OBJ: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_PTR: + new(addr) ZSMapIterator(); + break; + case PMap::MAP_STR_STR: + new(addr) ZSMapIterator(); + break; + }; +} + +void PMapIterator::InitializeValue(void *addr, const void *def) const +{ + if (def != nullptr) + { + I_Error("Map cannot have default values"); + } + Construct(addr); +} + +//========================================================================== +// +// PMapIterator :: DestroyValue +// +//========================================================================== + +void PMapIterator::DestroyValue(void *addr) const +{ + switch(BackingClass) + { + case PMap::MAP_I32_I8: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_I16: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_I32: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_F32: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_F64: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_OBJ: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_PTR: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_I32_STR: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_I8: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_I16: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_I32: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_F32: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_F64: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_OBJ: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_PTR: + static_cast*>(addr)->~ZSMapIterator(); + break; + case PMap::MAP_STR_STR: + static_cast*>(addr)->~ZSMapIterator(); + break; + } +} + +//========================================================================== +// +// PMapIterator :: SetDefaultValue +// +//========================================================================== + +void PMapIterator::SetDefaultValue(void *base, unsigned offset, TArray *special) +{ + assert(!(base && special)); + if (base != nullptr) + { + Construct(((uint8_t*)base)+offset); + } + if (special != nullptr) + { + special->Push(std::make_pair(this, offset)); + } + else + { + I_Error("null special"); + } +} + +//========================================================================== +// +// PMapIterator :: WriteValue +// +//========================================================================== + +void PMapIterator::WriteValue(FSerializer &ar, const char *key, const void *addr) const +{ + ar.BeginObject(key); + ar.EndObject(); +} + +//========================================================================== +// +// PMapIterator :: ReadValue +// +//========================================================================== + +bool PMapIterator::ReadValue(FSerializer &ar, const char *key, void *addr) const +{ + DestroyValue(addr); + InitializeValue(addr, nullptr); + ar.BeginObject(key); + ar.EndObject(); + return true; +} + +//========================================================================== +// +// NewMapIterator +// +// Returns a PMapIterator for the given key and value types, ensuring not to create +// duplicates. +// +//========================================================================== + +PMapIterator *NewMapIterator(PType *keyType, PType *valueType) +{ + size_t bucket; + PType *mapIteratorType = TypeTable.FindType(NAME_MapIterator, (intptr_t)keyType, (intptr_t)valueType, &bucket); + if (mapIteratorType == nullptr) + { + FString backingName = "MapIterator_"; + int backingClass = PMapBackingClass(keyType, valueType, backingName); + + auto backing = NewStruct(backingName, nullptr, true); + mapIteratorType = new PMapIterator(keyType, valueType, backing, backingClass); + TypeTable.AddType(mapIteratorType, NAME_MapIterator, (intptr_t)keyType, (intptr_t)valueType, bucket); + } + return (PMapIterator *)mapIteratorType; } /* PStruct ****************************************************************/ @@ -2333,6 +3087,26 @@ void PStruct::SetPointerArray(void *base, unsigned offset, TArray *speci } } +//========================================================================== +// +// PStruct :: SetPointerMap +// +//========================================================================== + +void PStruct::SetPointerMap(void *base, unsigned offset, TArray> *special) +{ + auto it = Symbols.GetIterator(); + PSymbolTable::MapType::Pair *pair; + while (it.NextPair(pair)) + { + auto field = dyn_cast(pair->Value); + if (field && !(field->Flags & VARF_Transient)) + { + field->Type->SetPointerMap(base, unsigned(offset + field->Offset), special); + } + } +} + //========================================================================== // // PStruct :: WriteValue diff --git a/source/common/scripting/core/types.h b/source/common/scripting/core/types.h index 14597ddfd..03ac8d89e 100644 --- a/source/common/scripting/core/types.h +++ b/source/common/scripting/core/types.h @@ -129,6 +129,7 @@ public: virtual void SetDefaultValue(void *base, unsigned offset, TArray *special=NULL); virtual void SetPointer(void *base, unsigned offset, TArray *ptrofs = NULL); virtual void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL); + virtual void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs = NULL); // Initialize the value, if needed (e.g. strings) virtual void InitializeValue(void *addr, const void *def) const; @@ -200,6 +201,8 @@ public: bool isArray() const { return !!(Flags & TYPE_Array); } bool isStaticArray() const { return TypeTableType == NAME_StaticArray; } bool isDynArray() const { return TypeTableType == NAME_DynArray; } + bool isMap() const { return TypeTableType == NAME_Map; } + bool isMapIterator() const { return TypeTableType == NAME_MapIterator; } bool isStruct() const { return TypeTableType == NAME_Struct; } bool isClass() const { return TypeTableType == NAME_Object; } bool isPrototype() const { return TypeTableType == NAME_Prototype; } @@ -489,6 +492,7 @@ public: void SetDefaultValue(void *base, unsigned offset, TArray *special) override; void SetPointer(void *base, unsigned offset, TArray *special) override; void SetPointerArray(void *base, unsigned offset, TArray *ptrofs = NULL) override; + void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs = NULL) override; }; class PStaticArray : public PArray @@ -521,14 +525,72 @@ public: class PMap : public PCompoundType { + void Construct(void * addr) const; public: - PMap(PType *keytype, PType *valtype); + PMap(PType *keytype, PType *valtype, PStruct *backing, int backing_class); PType *KeyType; PType *ValueType; + TMap FnOverrides; + + PStruct *BackingType; + + enum EBackingClass { + MAP_I32_I8, + MAP_I32_I16, + MAP_I32_I32, + MAP_I32_F32, + MAP_I32_F64, + MAP_I32_OBJ, + MAP_I32_PTR, + MAP_I32_STR, + + MAP_STR_I8, + MAP_STR_I16, + MAP_STR_I32, + MAP_STR_F32, + MAP_STR_F64, + MAP_STR_OBJ, + MAP_STR_PTR, + MAP_STR_STR, + } BackingClass; + virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; + + void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; + void SetDefaultValue(void *base, unsigned offset, TArray *specials) override; + void InitializeValue(void *addr, const void *def) const override; + void DestroyValue(void *addr) const override; + void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs) override; +}; + + +class PMapIterator : public PCompoundType +{ + void Construct(void * addr) const; +public: + PMapIterator(PType *keytype, PType *valtype, PStruct *backing, int backing_class); + + PType *KeyType; + PType *ValueType; + + TMap FnOverrides; + + PStruct *BackingType; + + PMap::EBackingClass BackingClass; + + virtual bool IsMatch(intptr_t id1, intptr_t id2) const; + virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; + + void WriteValue(FSerializer &ar, const char *key, const void *addr) const override; + bool ReadValue(FSerializer &ar, const char *key, void *addr) const override; + void SetDefaultValue(void *base, unsigned offset, TArray *specials) override; + void InitializeValue(void *addr, const void *def) const override; + void DestroyValue(void *addr) const override; }; class PStruct : public PContainerType @@ -550,6 +612,7 @@ public: void SetDefaultValue(void *base, unsigned offset, TArray *specials) override; void SetPointer(void *base, unsigned offset, TArray *specials) override; void SetPointerArray(void *base, unsigned offset, TArray *special) override; + void SetPointerMap(void *base, unsigned offset, TArray> *ptrofs) override; }; class PPrototype : public PCompoundType @@ -586,6 +649,7 @@ inline PClass *PObjectPointer::PointedClass() const // Returns a type from the TypeTable. Will create one if it isn't present. PMap *NewMap(PType *keytype, PType *valuetype); +PMapIterator *NewMapIterator(PType *keytype, PType *valuetype); PArray *NewArray(PType *type, unsigned int count); PStaticArray *NewStaticArray(PType *type); PDynArray *NewDynArray(PType *type); diff --git a/source/common/scripting/frontend/ast.cpp b/source/common/scripting/frontend/ast.cpp index 929352db8..44cc0f516 100644 --- a/source/common/scripting/frontend/ast.cpp +++ b/source/common/scripting/frontend/ast.cpp @@ -39,7 +39,7 @@ #include "printf.h" class FLispString; -extern void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode *); +using NodePrinterFunc = void (*)(FLispString &, const ZCC_TreeNode *); static const char *BuiltInTypeNames[] = { @@ -221,24 +221,11 @@ private: bool NeedSpace; }; -static void PrintNode(FLispString &out, ZCC_TreeNode *node) -{ - assert(TreeNodePrinter[NUM_AST_NODE_TYPES-1] != NULL); - if (node->NodeType >= 0 && node->NodeType < NUM_AST_NODE_TYPES) - { - TreeNodePrinter[node->NodeType](out, node); - } - else - { - out.Open("unknown-node-type"); - out.AddInt(node->NodeType); - out.Close(); - } -} +static void PrintNode(FLispString &out, const ZCC_TreeNode *node); -static void PrintNodes(FLispString &out, ZCC_TreeNode *node, bool newlist=true, bool addbreaks=false) +static void PrintNodes(FLispString &out, const ZCC_TreeNode *node, bool newlist=true, bool addbreaks=false) { - ZCC_TreeNode *p; + const ZCC_TreeNode *p; if (node == NULL) { @@ -269,7 +256,7 @@ static void PrintNodes(FLispString &out, ZCC_TreeNode *node, bool newlist=true, static void PrintBuiltInType(FLispString &out, EZCCBuiltinType type) { - assert(ZCC_NUM_BUILT_IN_TYPES == countof(BuiltInTypeNames)); + static_assert(ZCC_NUM_BUILT_IN_TYPES == countof(BuiltInTypeNames)); if (unsigned(type) >= unsigned(ZCC_NUM_BUILT_IN_TYPES)) { char buf[30]; @@ -282,7 +269,7 @@ static void PrintBuiltInType(FLispString &out, EZCCBuiltinType type) } } -static void PrintIdentifier(FLispString &out, ZCC_TreeNode *node) +static void PrintIdentifier(FLispString &out, const ZCC_TreeNode *node) { ZCC_Identifier *inode = (ZCC_Identifier *)node; out.Open("identifier"); @@ -317,7 +304,7 @@ static void PrintStringConst(FLispString &out, FString str) out.Add(outstr); } -static void PrintClass(FLispString &out, ZCC_TreeNode *node) +static void PrintClass(FLispString &out, const ZCC_TreeNode *node) { ZCC_Class *cnode = (ZCC_Class *)node; out.Break(); @@ -330,7 +317,7 @@ static void PrintClass(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintStruct(FLispString &out, ZCC_TreeNode *node) +static void PrintStruct(FLispString &out, const ZCC_TreeNode *node) { ZCC_Struct *snode = (ZCC_Struct *)node; out.Break(); @@ -340,7 +327,7 @@ static void PrintStruct(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintProperty(FLispString &out, ZCC_TreeNode *node) +static void PrintProperty(FLispString &out, const ZCC_TreeNode *node) { ZCC_Property *snode = (ZCC_Property *)node; out.Break(); @@ -350,7 +337,7 @@ static void PrintProperty(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintFlagDef(FLispString &out, ZCC_TreeNode *node) +static void PrintFlagDef(FLispString &out, const ZCC_TreeNode *node) { ZCC_FlagDef *snode = (ZCC_FlagDef *)node; out.Break(); @@ -361,7 +348,7 @@ static void PrintFlagDef(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintStaticArrayState(FLispString &out, ZCC_TreeNode *node) +static void PrintStaticArrayState(FLispString &out, const ZCC_TreeNode *node) { auto *snode = (ZCC_StaticArrayStatement *)node; out.Break(); @@ -371,7 +358,7 @@ static void PrintStaticArrayState(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintEnum(FLispString &out, ZCC_TreeNode *node) +static void PrintEnum(FLispString &out, const ZCC_TreeNode *node) { ZCC_Enum *enode = (ZCC_Enum *)node; out.Break(); @@ -382,13 +369,13 @@ static void PrintEnum(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintEnumTerminator(FLispString &out, ZCC_TreeNode *node) +static void PrintEnumTerminator(FLispString &out, const ZCC_TreeNode *node) { out.Open("enum-term"); out.Close(); } -static void PrintStates(FLispString &out, ZCC_TreeNode *node) +static void PrintStates(FLispString &out, const ZCC_TreeNode *node) { ZCC_States *snode = (ZCC_States *)node; out.Break(); @@ -398,13 +385,13 @@ static void PrintStates(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintStatePart(FLispString &out, ZCC_TreeNode *node) +static void PrintStatePart(FLispString &out, const ZCC_TreeNode *node) { out.Open("state-part"); out.Close(); } -static void PrintStateLabel(FLispString &out, ZCC_TreeNode *node) +static void PrintStateLabel(FLispString &out, const ZCC_TreeNode *node) { ZCC_StateLabel *snode = (ZCC_StateLabel *)node; out.Open("state-label"); @@ -412,31 +399,31 @@ static void PrintStateLabel(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintStateStop(FLispString &out, ZCC_TreeNode *node) +static void PrintStateStop(FLispString &out, const ZCC_TreeNode *node) { out.Open("state-stop"); out.Close(); } -static void PrintStateWait(FLispString &out, ZCC_TreeNode *node) +static void PrintStateWait(FLispString &out, const ZCC_TreeNode *node) { out.Open("state-wait"); out.Close(); } -static void PrintStateFail(FLispString &out, ZCC_TreeNode *node) +static void PrintStateFail(FLispString &out, const ZCC_TreeNode *node) { out.Open("state-fail"); out.Close(); } -static void PrintStateLoop(FLispString &out, ZCC_TreeNode *node) +static void PrintStateLoop(FLispString &out, const ZCC_TreeNode *node) { out.Open("state-loop"); out.Close(); } -static void PrintStateGoto(FLispString &out, ZCC_TreeNode *node) +static void PrintStateGoto(FLispString &out, const ZCC_TreeNode *node) { ZCC_StateGoto *snode = (ZCC_StateGoto *)node; out.Open("state-goto"); @@ -446,7 +433,7 @@ static void PrintStateGoto(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintStateLine(FLispString &out, ZCC_TreeNode *node) +static void PrintStateLine(FLispString &out, const ZCC_TreeNode *node) { ZCC_StateLine *snode = (ZCC_StateLine *)node; out.Open("state-line"); @@ -463,7 +450,7 @@ static void PrintStateLine(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintVarName(FLispString &out, ZCC_TreeNode *node) +static void PrintVarName(FLispString &out, const ZCC_TreeNode *node) { ZCC_VarName *vnode = (ZCC_VarName *)node; out.Open("var-name"); @@ -472,7 +459,7 @@ static void PrintVarName(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintVarInit(FLispString &out, ZCC_TreeNode *node) +static void PrintVarInit(FLispString &out, const ZCC_TreeNode *node) { ZCC_VarInit *vnode = (ZCC_VarInit *)node; out.Open("var-init"); @@ -483,7 +470,7 @@ static void PrintVarInit(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintType(FLispString &out, ZCC_TreeNode *node) +static void PrintType(FLispString &out, const ZCC_TreeNode *node) { ZCC_Type *tnode = (ZCC_Type *)node; out.Open("bad-type"); @@ -491,7 +478,7 @@ static void PrintType(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintBasicType(FLispString &out, ZCC_TreeNode *node) +static void PrintBasicType(FLispString &out, const ZCC_TreeNode *node) { ZCC_BasicType *tnode = (ZCC_BasicType *)node; out.Open("basic-type"); @@ -505,7 +492,7 @@ static void PrintBasicType(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintMapType(FLispString &out, ZCC_TreeNode *node) +static void PrintMapType(FLispString &out, const ZCC_TreeNode *node) { ZCC_MapType *tnode = (ZCC_MapType *)node; out.Open("map-type"); @@ -515,7 +502,17 @@ static void PrintMapType(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintDynArrayType(FLispString &out, ZCC_TreeNode *node) +static void PrintMapIteratorType(FLispString &out, const ZCC_TreeNode *node) +{ + ZCC_MapIteratorType *tnode = (ZCC_MapIteratorType *)node; + out.Open("map-iterator-type"); + PrintNodes(out, tnode->ArraySize); + PrintNodes(out, tnode->KeyType); + PrintNodes(out, tnode->ValueType); + out.Close(); +} + +static void PrintDynArrayType(FLispString &out, const ZCC_TreeNode *node) { ZCC_DynArrayType *tnode = (ZCC_DynArrayType *)node; out.Open("dyn-array-type"); @@ -524,7 +521,7 @@ static void PrintDynArrayType(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintClassType(FLispString &out, ZCC_TreeNode *node) +static void PrintClassType(FLispString &out, const ZCC_TreeNode *node) { ZCC_ClassType *tnode = (ZCC_ClassType *)node; out.Open("class-type"); @@ -548,14 +545,14 @@ static void OpenExprType(FLispString &out, EZCCExprType type) out.Open(buf); } -static void PrintExpression(FLispString &out, ZCC_TreeNode *node) +static void PrintExpression(FLispString &out, const ZCC_TreeNode *node) { ZCC_Expression *enode = (ZCC_Expression *)node; OpenExprType(out, enode->Operation); out.Close(); } -static void PrintExprID(FLispString &out, ZCC_TreeNode *node) +static void PrintExprID(FLispString &out, const ZCC_TreeNode *node) { ZCC_ExprID *enode = (ZCC_ExprID *)node; assert(enode->Operation == PEX_ID); @@ -564,7 +561,7 @@ static void PrintExprID(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintExprTypeRef(FLispString &out, ZCC_TreeNode *node) +static void PrintExprTypeRef(FLispString &out, const ZCC_TreeNode *node) { ZCC_ExprTypeRef *enode = (ZCC_ExprTypeRef *)node; assert(enode->Operation == PEX_TypeRef); @@ -583,7 +580,7 @@ static void PrintExprTypeRef(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintExprConstant(FLispString &out, ZCC_TreeNode *node) +static void PrintExprConstant(FLispString &out, const ZCC_TreeNode *node) { ZCC_ExprConstant *enode = (ZCC_ExprConstant *)node; assert(enode->Operation == PEX_ConstValue); @@ -611,7 +608,7 @@ static void PrintExprConstant(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintExprFuncCall(FLispString &out, ZCC_TreeNode *node) +static void PrintExprFuncCall(FLispString &out, const ZCC_TreeNode *node) { ZCC_ExprFuncCall *enode = (ZCC_ExprFuncCall *)node; assert(enode->Operation == PEX_FuncCall); @@ -621,7 +618,7 @@ static void PrintExprFuncCall(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintExprClassCast(FLispString &out, ZCC_TreeNode *node) +static void PrintExprClassCast(FLispString &out, const ZCC_TreeNode *node) { ZCC_ClassCast *enode = (ZCC_ClassCast *)node; assert(enode->Operation == PEX_ClassCast); @@ -631,7 +628,7 @@ static void PrintExprClassCast(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintStaticArray(FLispString &out, ZCC_TreeNode *node) +static void PrintStaticArray(FLispString &out, const ZCC_TreeNode *node) { ZCC_StaticArrayStatement *enode = (ZCC_StaticArrayStatement *)node; out.Open("static-array-stmt"); @@ -641,7 +638,7 @@ static void PrintStaticArray(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintExprMemberAccess(FLispString &out, ZCC_TreeNode *node) +static void PrintExprMemberAccess(FLispString &out, const ZCC_TreeNode *node) { ZCC_ExprMemberAccess *enode = (ZCC_ExprMemberAccess *)node; assert(enode->Operation == PEX_MemberAccess); @@ -651,7 +648,7 @@ static void PrintExprMemberAccess(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintExprUnary(FLispString &out, ZCC_TreeNode *node) +static void PrintExprUnary(FLispString &out, const ZCC_TreeNode *node) { ZCC_ExprUnary *enode = (ZCC_ExprUnary *)node; OpenExprType(out, enode->Operation); @@ -659,7 +656,7 @@ static void PrintExprUnary(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintExprBinary(FLispString &out, ZCC_TreeNode *node) +static void PrintExprBinary(FLispString &out, const ZCC_TreeNode *node) { ZCC_ExprBinary *enode = (ZCC_ExprBinary *)node; OpenExprType(out, enode->Operation); @@ -668,7 +665,7 @@ static void PrintExprBinary(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintExprTrinary(FLispString &out, ZCC_TreeNode *node) +static void PrintExprTrinary(FLispString &out, const ZCC_TreeNode *node) { ZCC_ExprTrinary *enode = (ZCC_ExprTrinary *)node; OpenExprType(out, enode->Operation); @@ -678,7 +675,7 @@ static void PrintExprTrinary(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintVectorInitializer(FLispString &out, ZCC_TreeNode *node) +static void PrintVectorInitializer(FLispString &out, const ZCC_TreeNode *node) { ZCC_VectorValue *enode = (ZCC_VectorValue *)node; OpenExprType(out, enode->Operation); @@ -689,7 +686,7 @@ static void PrintVectorInitializer(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintFuncParam(FLispString &out, ZCC_TreeNode *node) +static void PrintFuncParam(FLispString &out, const ZCC_TreeNode *node) { ZCC_FuncParm *pnode = (ZCC_FuncParm *)node; out.Break(); @@ -699,13 +696,13 @@ static void PrintFuncParam(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintStatement(FLispString &out, ZCC_TreeNode *node) +static void PrintStatement(FLispString &out, const ZCC_TreeNode *node) { out.Open("statement"); out.Close(); } -static void PrintCompoundStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintCompoundStmt(FLispString &out, const ZCC_TreeNode *node) { ZCC_CompoundStmt *snode = (ZCC_CompoundStmt *)node; out.Break(); @@ -714,7 +711,7 @@ static void PrintCompoundStmt(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintDefault(FLispString &out, ZCC_TreeNode *node) +static void PrintDefault(FLispString &out, const ZCC_TreeNode *node) { ZCC_Default *snode = (ZCC_Default *)node; out.Break(); @@ -723,21 +720,21 @@ static void PrintDefault(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintContinueStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintContinueStmt(FLispString &out, const ZCC_TreeNode *node) { out.Break(); out.Open("continue-stmt"); out.Close(); } -static void PrintBreakStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintBreakStmt(FLispString &out, const ZCC_TreeNode *node) { out.Break(); out.Open("break-stmt"); out.Close(); } -static void PrintReturnStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintReturnStmt(FLispString &out, const ZCC_TreeNode *node) { ZCC_ReturnStmt *snode = (ZCC_ReturnStmt *)node; out.Break(); @@ -746,7 +743,7 @@ static void PrintReturnStmt(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintExpressionStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintExpressionStmt(FLispString &out, const ZCC_TreeNode *node) { ZCC_ExpressionStmt *snode = (ZCC_ExpressionStmt *)node; out.Break(); @@ -755,7 +752,7 @@ static void PrintExpressionStmt(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintIterationStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintIterationStmt(FLispString &out, const ZCC_TreeNode *node) { ZCC_IterationStmt *snode = (ZCC_IterationStmt *)node; out.Break(); @@ -770,7 +767,7 @@ static void PrintIterationStmt(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintIfStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintIfStmt(FLispString &out, const ZCC_TreeNode *node) { ZCC_IfStmt *snode = (ZCC_IfStmt *)node; out.Break(); @@ -783,7 +780,7 @@ static void PrintIfStmt(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintSwitchStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintSwitchStmt(FLispString &out, const ZCC_TreeNode *node) { ZCC_SwitchStmt *snode = (ZCC_SwitchStmt *)node; out.Break(); @@ -794,7 +791,7 @@ static void PrintSwitchStmt(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintCaseStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintCaseStmt(FLispString &out, const ZCC_TreeNode *node) { ZCC_CaseStmt *snode = (ZCC_CaseStmt *)node; out.Break(); @@ -810,7 +807,7 @@ static void BadAssignOp(FLispString &out, int op) out.Add(buf, len); } -static void PrintAssignStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintAssignStmt(FLispString &out, const ZCC_TreeNode *node) { ZCC_AssignStmt *snode = (ZCC_AssignStmt *)node; out.Open("assign-stmt"); @@ -819,7 +816,7 @@ static void PrintAssignStmt(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintLocalVarStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintLocalVarStmt(FLispString &out, const ZCC_TreeNode *node) { ZCC_LocalVarStmt *snode = (ZCC_LocalVarStmt *)node; out.Open("local-var-stmt"); @@ -828,7 +825,7 @@ static void PrintLocalVarStmt(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintFuncParamDecl(FLispString &out, ZCC_TreeNode *node) +static void PrintFuncParamDecl(FLispString &out, const ZCC_TreeNode *node) { ZCC_FuncParamDecl *dnode = (ZCC_FuncParamDecl *)node; out.Break(); @@ -840,7 +837,7 @@ static void PrintFuncParamDecl(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintConstantDef(FLispString &out, ZCC_TreeNode *node) +static void PrintConstantDef(FLispString &out, const ZCC_TreeNode *node) { ZCC_ConstantDef *dnode = (ZCC_ConstantDef *)node; out.Break(); @@ -850,7 +847,7 @@ static void PrintConstantDef(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintDeclarator(FLispString &out, ZCC_TreeNode *node) +static void PrintDeclarator(FLispString &out, const ZCC_TreeNode *node) { ZCC_Declarator *dnode = (ZCC_Declarator *)node; out.Break(); @@ -860,7 +857,7 @@ static void PrintDeclarator(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintVarDeclarator(FLispString &out, ZCC_TreeNode *node) +static void PrintVarDeclarator(FLispString &out, const ZCC_TreeNode *node) { ZCC_VarDeclarator *dnode = (ZCC_VarDeclarator *)node; out.Break(); @@ -871,7 +868,7 @@ static void PrintVarDeclarator(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintFuncDeclarator(FLispString &out, ZCC_TreeNode *node) +static void PrintFuncDeclarator(FLispString &out, const ZCC_TreeNode *node) { ZCC_FuncDeclarator *dnode = (ZCC_FuncDeclarator *)node; out.Break(); @@ -885,7 +882,7 @@ static void PrintFuncDeclarator(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintDeclFlags(FLispString &out, ZCC_TreeNode *node) +static void PrintDeclFlags(FLispString &out, const ZCC_TreeNode *node) { auto dnode = (ZCC_DeclFlags *)node; out.Break(); @@ -895,7 +892,7 @@ static void PrintDeclFlags(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintFlagStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintFlagStmt(FLispString &out, const ZCC_TreeNode *node) { auto dnode = (ZCC_FlagStmt *)node; out.Break(); @@ -905,7 +902,7 @@ static void PrintFlagStmt(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintPropertyStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintPropertyStmt(FLispString &out, const ZCC_TreeNode *node) { auto dnode = (ZCC_PropertyStmt *)node; out.Break(); @@ -915,7 +912,7 @@ static void PrintPropertyStmt(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintMixinDef(FLispString &out, ZCC_TreeNode *node) +static void PrintMixinDef(FLispString &out, const ZCC_TreeNode *node) { ZCC_MixinDef *mdnode = (ZCC_MixinDef *)node; out.Break(); @@ -925,7 +922,7 @@ static void PrintMixinDef(FLispString &out, ZCC_TreeNode *node) out.Close(); } -static void PrintMixinStmt(FLispString &out, ZCC_TreeNode *node) +static void PrintMixinStmt(FLispString &out, const ZCC_TreeNode *node) { ZCC_MixinStmt *msnode = (ZCC_MixinStmt *)node; out.Break(); @@ -934,7 +931,20 @@ static void PrintMixinStmt(FLispString &out, ZCC_TreeNode *node) out.Close(); } -void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode *) = +static void PrintArrayIterationStmt(FLispString &out, const ZCC_TreeNode *node) +{ + auto inode = (ZCC_ArrayIterationStmt *)node; + out.Break(); + out.Open("array-iteration-stmt"); + PrintVarName(out, inode->ItName); + out.Break(); + PrintNodes(out, inode->ItArray); + out.Break(); + PrintNodes(out, inode->LoopStatement); + out.Close(); +} + +static const NodePrinterFunc TreeNodePrinter[] = { PrintIdentifier, PrintClass, @@ -955,6 +965,7 @@ void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode * PrintType, PrintBasicType, PrintMapType, + PrintMapIteratorType, PrintDynArrayType, PrintClassType, PrintExpression, @@ -995,11 +1006,27 @@ void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode * PrintFlagDef, PrintMixinDef, PrintMixinStmt, + PrintArrayIterationStmt, }; -FString ZCC_PrintAST(ZCC_TreeNode *root) +FString ZCC_PrintAST(const ZCC_TreeNode *root) { FLispString out; PrintNodes(out, root); return out; } + +static void PrintNode(FLispString &out, const ZCC_TreeNode *node) +{ + static_assert(countof(TreeNodePrinter) == NUM_AST_NODE_TYPES, "All AST node types should have printers defined for them"); + if (node->NodeType >= 0 && node->NodeType < NUM_AST_NODE_TYPES) + { + TreeNodePrinter[node->NodeType](out, node); + } + else + { + out.Open("unknown-node-type"); + out.AddInt(node->NodeType); + out.Close(); + } +} diff --git a/source/common/scripting/frontend/zcc-parse.lemon b/source/common/scripting/frontend/zcc-parse.lemon index 27d74fc87..560f15ad8 100644 --- a/source/common/scripting/frontend/zcc-parse.lemon +++ b/source/common/scripting/frontend/zcc-parse.lemon @@ -932,7 +932,7 @@ type_name(X) ::= DOT dottable_id(A). /* Type names can also be used as identifiers in contexts where type names * are not normally allowed. */ %fallback IDENTIFIER - SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 VECTOR4 NAME MAP ARRAY VOID STATE COLOR SOUND UINT8 INT8 UINT16 INT16 PROPERTY. + SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 VECTOR4 NAME MAP MAPITERATOR ARRAY VOID STATE COLOR SOUND UINT8 INT8 UINT16 INT16 PROPERTY. /* Aggregate types */ %type aggregate_type {ZCC_Type *} @@ -944,7 +944,7 @@ type_name(X) ::= DOT dottable_id(A). %type array_size{ZCC_Expression *} %type array_size_expr{ZCC_Expression *} -aggregate_type(X) ::= MAP(T) LT type_or_array(A) COMMA type_or_array(B) GT. /* Hash table */ +aggregate_type(X) ::= MAP(T) LT type_or_array(A) COMMA type_or_array(B) GT. /* ZSMap */ { NEW_AST_NODE(MapType,map,T); map->KeyType = A; @@ -952,6 +952,14 @@ aggregate_type(X) ::= MAP(T) LT type_or_array(A) COMMA type_or_array(B) GT. /* H X = map; } +aggregate_type(X) ::= MAPITERATOR(T) LT type_or_array(A) COMMA type_or_array(B) GT. /* ZSMapIterator */ +{ + NEW_AST_NODE(MapIteratorType,map_it,T); + map_it->KeyType = A; + map_it->ValueType = B; + X = map_it; +} + aggregate_type(X) ::= ARRAY(T) LT type_or_array(A) GT. /* TArray */ { NEW_AST_NODE(DynArrayType,arr,T); diff --git a/source/common/scripting/frontend/zcc_compile.cpp b/source/common/scripting/frontend/zcc_compile.cpp index b6fa2340a..7777b7327 100644 --- a/source/common/scripting/frontend/zcc_compile.cpp +++ b/source/common/scripting/frontend/zcc_compile.cpp @@ -1853,16 +1853,96 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n } case AST_MapType: - if (allowarraytypes) + { + if(AST.ParseVersion < MakeVersion(4, 10, 0)) { - Error(field, "%s: Map types not implemented yet", name.GetChars()); - // Todo: Decide what we allow here and if it makes sense to allow more complex constructs. - auto mtype = static_cast(ztype); - retval = NewMap(DetermineType(outertype, field, name, mtype->KeyType, false, false), DetermineType(outertype, field, name, mtype->ValueType, false, false)); + Error(field, "Map not accessible to ZScript version %d.%d.%d", AST.ParseVersion.major, AST.ParseVersion.minor, AST.ParseVersion.revision); break; } - break; + // Todo: Decide what we allow here and if it makes sense to allow more complex constructs. + auto mtype = static_cast(ztype); + + auto keytype = DetermineType(outertype, field, name, mtype->KeyType, false, false); + auto valuetype = DetermineType(outertype, field, name, mtype->ValueType, false, false); + + if (keytype->GetRegType() == REGT_INT) + { + if (keytype->Size != 4) + { + Error(field, "Map<%s , ...> not implemented yet", keytype->DescriptiveName()); + break; + } + } + else if (keytype->GetRegType() != REGT_STRING) + { + Error(field, "Map<%s , ...> not implemented yet", keytype->DescriptiveName()); + break; + } + + switch(valuetype->GetRegType()) + { + case REGT_FLOAT: + case REGT_INT: + case REGT_STRING: + case REGT_POINTER: + if (valuetype->GetRegCount() > 1) + { + Error(field, "%s : Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName()); + break; + } + + retval = NewMap(keytype, valuetype); + break; + default: + Error(field, "%s: Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName()); + } + + break; + } + case AST_MapIteratorType: + { + if(AST.ParseVersion < MakeVersion(4, 10, 0)) + { + Error(field, "MapIterator not accessible to ZScript version %d.%d.%d", AST.ParseVersion.major, AST.ParseVersion.minor, AST.ParseVersion.revision); + break; + } + // Todo: Decide what we allow here and if it makes sense to allow more complex constructs. + auto mtype = static_cast(ztype); + + auto keytype = DetermineType(outertype, field, name, mtype->KeyType, false, false); + auto valuetype = DetermineType(outertype, field, name, mtype->ValueType, false, false); + + if (keytype->GetRegType() == REGT_INT) + { + if (keytype->Size != 4) + { + Error(field, "MapIterator<%s , ...> not implemented yet", keytype->DescriptiveName()); + } + } + else if (keytype->GetRegType() != REGT_STRING) + { + Error(field, "MapIterator<%s , ...> not implemented yet", keytype->DescriptiveName()); + } + + switch(valuetype->GetRegType()) + { + case REGT_FLOAT: + case REGT_INT: + case REGT_STRING: + case REGT_POINTER: + if (valuetype->GetRegCount() > 1) + { + Error(field, "%s : Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName()); + break; + } + retval = NewMapIterator(keytype, valuetype); + break; + default: + Error(field, "%s: Base type for map value types must be integral, but got %s", name.GetChars(), valuetype->DescriptiveName()); + } + break; + } case AST_DynArrayType: { auto atype = static_cast(ztype); @@ -2357,7 +2437,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool { auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false); int flags = 0; - if ((type->isStruct() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeQuaternion && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4 && type != TypeFQuaternion) || type->isDynArray()) + if ((type->isStruct() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeQuaternion && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4 && type != TypeFQuaternion) || type->isDynArray() || type->isMap() || type->isMapIterator()) { // Structs are being passed by pointer, but unless marked 'out' that pointer must be readonly. type = NewPointer(type /*, !(p->Flags & ZCC_Out)*/); diff --git a/source/common/scripting/frontend/zcc_parser.cpp b/source/common/scripting/frontend/zcc_parser.cpp index be771fd84..2343e4909 100644 --- a/source/common/scripting/frontend/zcc_parser.cpp +++ b/source/common/scripting/frontend/zcc_parser.cpp @@ -220,6 +220,7 @@ static void InitTokenMap() TOKENDEF2(TK_Vector3, ZCC_VECTOR3, NAME_Vector3); TOKENDEF2(TK_Name, ZCC_NAME, NAME_Name); TOKENDEF2(TK_Map, ZCC_MAP, NAME_Map); + TOKENDEF2(TK_MapIterator, ZCC_MAPITERATOR,NAME_MapIterator); TOKENDEF2(TK_Array, ZCC_ARRAY, NAME_Array); TOKENDEF2(TK_Include, ZCC_INCLUDE, NAME_Include); TOKENDEF (TK_Void, ZCC_VOID); @@ -896,6 +897,19 @@ ZCC_TreeNode *TreeNodeDeepCopy_Internal(ZCC_AST *ast, ZCC_TreeNode *orig, bool c break; } + case AST_MapIteratorType: + { + TreeNodeDeepCopy_Start(MapIteratorType); + + // ZCC_Type + copy->ArraySize = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ArraySize, true, copiedNodesList)); + // AST_MapIteratorType + copy->KeyType = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->KeyType, true, copiedNodesList)); + copy->ValueType = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ValueType, true, copiedNodesList)); + + break; + } + case AST_DynArrayType: { TreeNodeDeepCopy_Start(DynArrayType); @@ -1128,7 +1142,7 @@ ZCC_TreeNode *TreeNodeDeepCopy_Internal(ZCC_AST *ast, ZCC_TreeNode *orig, bool c { TreeNodeDeepCopy_Start(ArrayIterationStmt); - // ZCC_IterationStmt + // ZCC_ArrayIterationStmt copy->ItName = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ItName, true, copiedNodesList)); copy->LoopStatement = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->LoopStatement, true, copiedNodesList)); copy->ItArray = static_cast(TreeNodeDeepCopy_Internal(ast, origCasted->ItArray, true, copiedNodesList)); diff --git a/source/common/scripting/frontend/zcc_parser.h b/source/common/scripting/frontend/zcc_parser.h index d9710dd95..7d2cce2e8 100644 --- a/source/common/scripting/frontend/zcc_parser.h +++ b/source/common/scripting/frontend/zcc_parser.h @@ -98,6 +98,7 @@ enum EZCCTreeNodeType AST_Type, AST_BasicType, AST_MapType, + AST_MapIteratorType, AST_DynArrayType, AST_ClassType, AST_Expression, @@ -367,6 +368,12 @@ struct ZCC_MapType : ZCC_Type ZCC_Type *ValueType; }; +struct ZCC_MapIteratorType : ZCC_Type +{ + ZCC_Type *KeyType; + ZCC_Type *ValueType; +}; + struct ZCC_DynArrayType : ZCC_Type { ZCC_Type *ElementType; @@ -601,7 +608,7 @@ struct ZCC_MixinStmt : ZCC_Statement ENamedName MixinName; }; -FString ZCC_PrintAST(ZCC_TreeNode *root); +FString ZCC_PrintAST(const ZCC_TreeNode *root); struct ZCC_AST diff --git a/source/common/utility/engineerrors.cpp b/source/common/utility/engineerrors.cpp index 1e2e2e82e..4d0125819 100644 --- a/source/common/utility/engineerrors.cpp +++ b/source/common/utility/engineerrors.cpp @@ -36,6 +36,7 @@ bool gameisdead; #ifdef _WIN32 +#include #include #include "zstring.h" void I_DebugPrint(const char *cp) @@ -46,10 +47,31 @@ void I_DebugPrint(const char *cp) OutputDebugStringW(wstr.c_str()); } } + +void I_DebugPrintf(const char *fmt,...) +{ + if (IsDebuggerPresent()) + { + va_list args; + va_start(args, fmt); + + FString s; + s.VFormat(fmt, args); + + va_end(args); + + auto wstr = WideString(s); + OutputDebugStringW(wstr.c_str()); + } +} #else void I_DebugPrint(const char *cp) { -} +} + +void I_DebugPrintf(const char *fmt,...) +{ +} #endif #include "engineerrors.h" diff --git a/source/common/utility/tarray.h b/source/common/utility/tarray.h index 1df7e5d39..a7decd4f3 100644 --- a/source/common/utility/tarray.h +++ b/source/common/utility/tarray.h @@ -916,6 +916,9 @@ public: typedef struct { const KT Key; VT Value; } Pair; typedef const Pair ConstPair; + typedef KT KeyType; + typedef VT ValueType; + TMap() { NumUsed = 0; SetNodeVector(1); } TMap(hash_t size) { NumUsed = 0; SetNodeVector(size); } ~TMap() { ClearNodeVector(); } diff --git a/source/common/utility/vectors.h b/source/common/utility/vectors.h index 439b7c7ec..c6ba414d7 100644 --- a/source/common/utility/vectors.h +++ b/source/common/utility/vectors.h @@ -1547,6 +1547,16 @@ inline TAngle interpolatedvalue(const TAngle &oang, const TAngle &ang, return oang + (deltaangle(oang, ang) * interpfrac); } +template +inline TRotator interpolatedvalue(const TRotator &oang, const TRotator &ang, const double interpfrac) +{ + return TRotator( + interpolatedvalue(oang.Pitch, ang.Pitch, interpfrac), + interpolatedvalue(oang.Yaw, ang.Yaw, interpfrac), + interpolatedvalue(oang.Roll, ang.Roll, interpfrac) + ); +} + template inline T interpolatedvalue(const T& oval, const T& val, const double interpfrac) { diff --git a/source/games/duke/src/spawn.cpp b/source/games/duke/src/spawn.cpp index 38af13985..12c294615 100644 --- a/source/games/duke/src/spawn.cpp +++ b/source/games/duke/src/spawn.cpp @@ -430,7 +430,7 @@ void initshell(DDukeActor* actj, DDukeActor* act, bool isshell) ang = ps[snum].angle.ang - mapangle((krand() & 63) + 8); //Fine tune act->temp_data[0] = krand() & 1; - act->spr.pos.Z = 3 + ps[snum].pos.Z + ps[snum].pyoff + (ps[snum].horizon.sum().Tan() * 8.) + (!isshell ? 3 : 0); + act->spr.pos.Z = 3 + ps[snum].pos.Z + ps[snum].pyoff + (ps[snum].horizon.sum().Sin() * 8.) + (!isshell ? 3 : 0); act->vel.Z = -krandf(1); } else diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index cf74d9647..8d4ccd016 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -14,6 +14,7 @@ version "4.10" #include "zscript/engine/ui/menu/menu.zs" #include "zscript/engine/ui/menu/menuitembase.zs" #include "zscript/engine/ui/menu/messagebox.zs" +#include "zscript/engine/ui/menu/custommessagebox.zs" #include "zscript/engine/ui/menu/optionmenu.zs" #include "zscript/engine/ui/menu/optionmenuitems.zs" #include "zscript/engine/ui/menu/reverbedit.zs" diff --git a/wadsrc/static/zscript/engine/base.zs b/wadsrc/static/zscript/engine/base.zs index 9000d3895..089109373 100644 --- a/wadsrc/static/zscript/engine/base.zs +++ b/wadsrc/static/zscript/engine/base.zs @@ -893,3 +893,16 @@ struct Translation version("2.4") } } +// Convenient way to attach functions to Quat +struct QuatStruct native +{ + native static Quat SLerp(Quat from, Quat to, double t); + native static Quat NLerp(Quat from, Quat to, double t); + native static Quat FromAngles(double yaw, double pitch, double roll); + native static Quat AxisAngle(Vector3 xyz, double angle); + native Quat Conjugate(); + native Quat Inverse(); + // native double Length(); + // native double LengthSquared(); + // native Quat Unit(); +} diff --git a/wadsrc/static/zscript/engine/maps.zs b/wadsrc/static/zscript/engine/maps.zs new file mode 100644 index 000000000..d10ffe5d1 --- /dev/null +++ b/wadsrc/static/zscript/engine/maps.zs @@ -0,0 +1,447 @@ + +struct Map_I32_I8 native +{ + native void Copy(Map_I32_I8 other); + native void Move(Map_I32_I8 other); + native void Swap(Map_I32_I8 other); + native void Clear(); + native uint CountUsed(); + + native int Get(int key); + native bool CheckKey(int key); + native void Insert(int key,int value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_I8 native +{ + native bool Init(Map_I32_I8 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native int GetValue(); + native void SetValue(int value); +} + +struct Map_I32_I16 native +{ + native void Copy(Map_I32_I16 other); + native void Move(Map_I32_I16 other); + native void Swap(Map_I32_I16 other); + native void Clear(); + native uint CountUsed(); + + native int Get(int key); + native bool CheckKey(int key); + native void Insert(int key,int value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_I16 native +{ + native bool Init(Map_I32_I16 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native int GetValue(); + native void SetValue(int value); +} + +struct Map_I32_I32 native +{ + native void Copy(Map_I32_I32 other); + native void Move(Map_I32_I32 other); + native void Swap(Map_I32_I32 other); + native void Clear(); + native uint CountUsed(); + + native int Get(int key); + native bool CheckKey(int key); + native void Insert(int key,int value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_I32 native +{ + native bool Init(Map_I32_I32 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native int GetValue(); + native void SetValue(int value); +} + +struct Map_I32_F32 native +{ + native void Copy(Map_I32_F32 other); + native void Move(Map_I32_F32 other); + native void Swap(Map_I32_F32 other); + native void Clear(); + native uint CountUsed(); + + native double Get(int key); + native bool CheckKey(int key); + native void Insert(int key,double value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_F32 native +{ + native bool Init(Map_I32_F32 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native double GetValue(); + native void SetValue(double value); +} + +struct Map_I32_F64 native +{ + native void Copy(Map_I32_F64 other); + native void Move(Map_I32_F64 other); + native void Swap(Map_I32_F64 other); + native void Clear(); + native uint CountUsed(); + + native double Get(int key); + native bool CheckKey(int key); + native void Insert(int key,double value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_F64 native +{ + native bool Init(Map_I32_F64 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native double GetValue(); + native void SetValue(double value); +} + +struct Map_I32_Obj native +{ + native void Copy(Map_I32_Obj other); + native void Move(Map_I32_Obj other); + native void Swap(Map_I32_Obj other); + native void Clear(); + native uint CountUsed(); + + native Object Get(int key); + native bool CheckKey(int key); + native void Insert(int key,Object value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_Obj native +{ + native bool Init(Map_I32_Obj other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native Object GetValue(); + native void SetValue(Object value); +} + +struct Map_I32_Ptr native +{ + native void Copy(Map_I32_Ptr other); + native void Move(Map_I32_Ptr other); + native void Swap(Map_I32_Ptr other); + native void Clear(); + native uint CountUsed(); + + native voidptr Get(int key); + native bool CheckKey(int key); + native void Insert(int key,voidptr value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_Ptr native +{ + native bool Init(Map_I32_Ptr other); + native bool Next(); + + native int GetKey(); + native voidptr GetValue(); + native void SetValue(voidptr value); +} + +struct Map_I32_Str native +{ + native void Copy(Map_I32_Str other); + native void Move(Map_I32_Str other); + native void Swap(Map_I32_Str other); + native void Clear(); + native uint CountUsed(); + + native String Get(int key); + native bool CheckKey(int key); + native void Insert(int key,String value); + native void InsertNew(int key); + native void Remove(int key); +} + +struct MapIterator_I32_Str native +{ + native bool Init(Map_I32_Str other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native int GetKey(); + native String GetValue(); + native void SetValue(String value); +} + +// --------------- + +struct Map_Str_I8 native +{ + native void Copy(Map_Str_I8 other); + native void Move(Map_Str_I8 other); + native void Swap(Map_Str_I8 other); + native void Clear(); + native uint CountUsed(); + + native int Get(String key); + native bool CheckKey(String key); + native void Insert(String key,int value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_I8 native +{ + native bool Init(Map_Str_I8 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native int GetValue(); + native void SetValue(int value); +} + +struct Map_Str_I16 native +{ + native void Copy(Map_Str_I16 other); + native void Move(Map_Str_I16 other); + native void Swap(Map_Str_I16 other); + native void Clear(); + native uint CountUsed(); + + native int Get(String key); + native bool CheckKey(String key); + native void Insert(String key,int value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_I16 native +{ + native bool Init(Map_Str_I16 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native int GetValue(); + native void SetValue(int value); +} + +struct Map_Str_I32 native +{ + native void Copy(Map_Str_I32 other); + native void Move(Map_Str_I32 other); + native void Swap(Map_Str_I32 other); + native void Clear(); + native uint CountUsed(); + + native int Get(String key); + native bool CheckKey(String key); + native void Insert(String key,int value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_I32 native +{ + native bool Init(Map_Str_I32 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native int GetValue(); + native void SetValue(int value); +} + +struct Map_Str_F32 native +{ + native void Copy(Map_Str_F32 other); + native void Move(Map_Str_F32 other); + native void Swap(Map_Str_F32 other); + native void Clear(); + native uint CountUsed(); + + native double Get(String key); + native bool CheckKey(String key); + native void Insert(String key,double value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_F32 native +{ + native bool Init(Map_Str_F32 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native double GetValue(); + native void SetValue(double value); +} + +struct Map_Str_F64 native +{ + native void Copy(Map_Str_F64 other); + native void Move(Map_Str_F64 other); + native void Swap(Map_Str_F64 other); + native void Clear(); + native uint CountUsed(); + + native double Get(String key); + native bool CheckKey(String key); + native void Insert(String key,double value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_F64 native +{ + native bool Init(Map_Str_F64 other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native double GetValue(); + native void SetValue(double value); +} + +struct Map_Str_Obj native +{ + native void Copy(Map_Str_Obj other); + native void Move(Map_Str_Obj other); + native void Swap(Map_Str_Obj other); + native void Clear(); + native uint CountUsed(); + + native Object Get(String key); + native bool CheckKey(String key); + native void Insert(String key,Object value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_Obj native +{ + native bool Init(Map_Str_Obj other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native Object GetValue(); + native void SetValue(Object value); +} + +struct Map_Str_Ptr native +{ + native void Copy(Map_Str_Ptr other); + native void Move(Map_Str_Ptr other); + native void Swap(Map_Str_Ptr other); + native void Clear(); + native uint CountUsed(); + + native voidptr Get(String key); + native bool CheckKey(String key); + native void Insert(String key,voidptr value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_Ptr native +{ + native bool Init(Map_Str_Ptr other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native voidptr GetValue(); + native void SetValue(voidptr value); +} + +struct Map_Str_Str native +{ + native void Copy(Map_Str_Str other); + native void Move(Map_Str_Str other); + native void Swap(Map_Str_Str other); + native void Clear(); + native uint CountUsed(); + + native String Get(String key); + native bool CheckKey(String key); + native void Insert(String key,String value); + native void InsertNew(String key); + native void Remove(String key); +} + +struct MapIterator_Str_Str native +{ + native bool Init(Map_Str_Str other); + native bool ReInit(); + + native bool Valid(); + native bool Next(); + + native String GetKey(); + native String GetValue(); + native void SetValue(String value); +} diff --git a/wadsrc/static/zscript/engine/ui/menu/custommessagebox.zs b/wadsrc/static/zscript/engine/ui/menu/custommessagebox.zs new file mode 100644 index 000000000..68a79417c --- /dev/null +++ b/wadsrc/static/zscript/engine/ui/menu/custommessagebox.zs @@ -0,0 +1,264 @@ +/* +** +**--------------------------------------------------------------------------- +** Copyright 2010-2017 Christoph Oelckers +** Copyright 2022 Ricardo Luis Vaz Silva +** +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +class CustomMessageBoxMenuBase : Menu abstract +{ + BrokenLines mMessage; + uint messageSelection; + int mMouseLeft, mMouseRight, mMouseY; + + Font textFont, arrowFont; + int destWidth, destHeight; + String selector; + + abstract uint OptionCount(); + abstract String OptionName(uint index); + abstract int OptionXOffset(uint index); + + abstract int OptionForShortcut(int char_key, out bool activate); // -1 for no shortcut, activate = true if this executes the option immediately + + //============================================================================= + // + // + // + //============================================================================= + + virtual void Init(Menu parent, String message, bool playsound = false) + { + Super.Init(parent); + messageSelection = 0; + mMouseLeft = 140; + mMouseY = 0x80000000; + textFont = null; + + if (!generic_ui) + { + if (SmallFont && SmallFont.CanPrint(message) && SmallFont.CanPrint("$TXT_YES") && SmallFont.CanPrint("$TXT_NO")) textFont = SmallFont; + else if (OriginalSmallFont && OriginalSmallFont.CanPrint(message) && OriginalSmallFont.CanPrint("$TXT_YES") && OriginalSmallFont.CanPrint("$TXT_NO")) textFont = OriginalSmallFont; + } + + if (!textFont) + { + arrowFont = textFont = NewSmallFont; + int factor = (CleanXfac+1) / 2; + destWidth = screen.GetWidth() / factor; + destHeight = screen.GetHeight() / factor; + selector = "▶"; + } + else + { + arrowFont = ConFont; + destWidth = CleanWidth; + destHeight = CleanHeight; + selector = "\xd"; + } + + int mr1 = destWidth/2 + 10 + textFont.StringWidth(Stringtable.Localize("$TXT_YES")); + int mr2 = destWidth/2 + 10 + textFont.StringWidth(Stringtable.Localize("$TXT_NO")); + mMouseRight = MAX(mr1, mr2); + mParentMenu = parent; + mMessage = textFont.BreakLines(Stringtable.Localize(message), int(300/NotifyFontScale)); + if (playsound) + { + MenuSound ("menu/prompt"); + } + } + + //============================================================================= + // + // + // + //============================================================================= + + override void Drawer () + { + int i; + double y; + let fontheight = textFont.GetHeight() * NotifyFontScale; + + y = destHeight / 2; + + int c = mMessage.Count(); + y -= c * fontHeight / 2; + + for (i = 0; i < c; i++) + { + screen.DrawText (textFont, Font.CR_UNTRANSLATED, destWidth/2 - mMessage.StringWidth(i)*NotifyFontScale/2, y, mMessage.StringAt(i), DTA_VirtualWidth, destWidth, DTA_VirtualHeight, destHeight, DTA_KeepRatio, true, + DTA_ScaleX, NotifyFontScale, DTA_ScaleY, NotifyFontScale); + y += fontheight; + } + + y += fontheight; + mMouseY = int(y); + + let n = optionCount(); + for(uint i = 0; i < n; i++) + { + screen.DrawText(textFont, messageSelection == i? OptionMenuSettings.mFontColorSelection : OptionMenuSettings.mFontColor, (destWidth / 2) + OptionXOffset(i), y + (fontheight * i), Stringtable.Localize(optionName(i)), DTA_VirtualWidth, destWidth, DTA_VirtualHeight, destHeight, DTA_KeepRatio, true, DTA_ScaleX, NotifyFontScale, DTA_ScaleY, NotifyFontScale); + } + + if (messageSelection >= 0) + { + if ((MenuTime() % 8) < 6) + { + screen.DrawText(arrowFont, OptionMenuSettings.mFontColorSelection, + (destWidth/2 - 11) + OptionXOffset(messageSelection), y + fontheight * messageSelection, selector, DTA_VirtualWidth, destWidth, DTA_VirtualHeight, destHeight, DTA_KeepRatio, true); + } + } + } + + + //============================================================================= + // + // + // + //============================================================================= + + protected void CloseSound() + { + MenuSound (GetCurrentMenu() != NULL? "menu/backup" : "menu/dismiss"); + } + + //============================================================================= + // + // + // + //============================================================================= + + abstract void HandleResult(int index); // -1 = escape + + //============================================================================= + // + // + // + //============================================================================= + + override bool OnUIEvent(UIEvent ev) + { + if (ev.type == UIEvent.Type_KeyDown) + { + // tolower + int ch = ev.KeyChar; + ch = ch >= 65 && ch <91? ch + 32 : ch; + + bool activate; + int opt = optionForShortcut(ch,activate); + + if(opt >= 0){ + if(activate || opt == messageSelection) { + HandleResult(messageSelection); + } else { + messageSelection = opt; + } + return true; + } + return false; + } + return Super.OnUIEvent(ev); + } + + override bool OnInputEvent(InputEvent ev) + { + if (ev.type == InputEvent.Type_KeyDown) + { + Close(); + return true; + } + return Super.OnInputEvent(ev); + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MenuEvent(int mkey, bool fromcontroller) + { + if (mkey == MKEY_Up) + { + MenuSound("menu/cursor"); + if (messageSelection == 0) messageSelection = optionCount(); + messageSelection--; + return true; + } + else if (mkey == MKEY_Down) + { + MenuSound("menu/cursor"); + messageSelection++; + if (messageSelection == optionCount()) messageSelection = 0; + return true; + } + else if (mkey == MKEY_Enter) + { + HandleResult(messageSelection); + return true; + } + else if (mkey == MKEY_Back) + { + HandleResult(-1); + return true; + } + return false; + } + + //============================================================================= + // + // + // + //============================================================================= + + override bool MouseEvent(int type, int x, int y) + { + int fh = textFont.GetHeight() + 1; + + // convert x/y from screen to virtual coordinates, according to CleanX/Yfac use in DrawTexture + x = x * destWidth / screen.GetWidth(); + y = y * destHeight / screen.GetHeight(); + + int n = OptionCount(); + + if (x >= mMouseLeft && x <= mMouseRight && y >= mMouseY && y < mMouseY + (n * fh)) + { + messageSelection = (y - mMouseY) / fh; + } + if (type == MOUSE_Release) + { + return MenuEvent(MKEY_Enter, true); + } + return true; + } + + +} diff --git a/wadsrc/static/zscript/engine/ui/menu/imagescroller.zs b/wadsrc/static/zscript/engine/ui/menu/imagescroller.zs index d56d770ce..5c7865f03 100644 --- a/wadsrc/static/zscript/engine/ui/menu/imagescroller.zs +++ b/wadsrc/static/zscript/engine/ui/menu/imagescroller.zs @@ -41,6 +41,8 @@ class ImageScrollerDescriptor : MenuDescriptor native native double textScale; native bool mAnimatedTransition; native bool mAnimated; + native bool mDontBlur; + native bool mDontDim; native int virtWidth, virtHeight; } @@ -168,8 +170,10 @@ class ImageScrollerMenu : Menu mParentMenu = parent; index = 0; mDesc = desc; - AnimatedTransition = desc.mAnimatedTransition; - Animated = desc.mAnimated; + AnimatedTransition = mDesc.mAnimatedTransition; + Animated = mDesc.mAnimated; + DontBlur = mDesc.mDontBlur; + DontDim = mDesc.mDontDim; current = mDesc.mItems[0]; current.onStartPage(); previous = null; diff --git a/wadsrc/static/zscript/engine/ui/menu/listmenu.zs b/wadsrc/static/zscript/engine/ui/menu/listmenu.zs index 66d9a1ce6..672c0ebc7 100644 --- a/wadsrc/static/zscript/engine/ui/menu/listmenu.zs +++ b/wadsrc/static/zscript/engine/ui/menu/listmenu.zs @@ -57,6 +57,8 @@ class ListMenuDescriptor : MenuDescriptor native native bool mCenter; native bool mAnimatedTransition; native bool mAnimated; + native bool mDontBlur; + native bool mDontDim; native int mVirtWidth, mVirtHeight; native void Reset(); @@ -89,6 +91,8 @@ class ListMenu : Menu mDesc = desc; AnimatedTransition = mDesc.mAnimatedTransition; Animated = mDesc.mAnimated; + DontBlur = mDesc.mDontBlur; + DontDim = mDesc.mDontDim; if (desc.mCenter) { double center = 160; diff --git a/wadsrc/static/zscript/engine/ui/menu/optionmenu.zs b/wadsrc/static/zscript/engine/ui/menu/optionmenu.zs index 1928d70ab..7338885bf 100644 --- a/wadsrc/static/zscript/engine/ui/menu/optionmenu.zs +++ b/wadsrc/static/zscript/engine/ui/menu/optionmenu.zs @@ -55,6 +55,9 @@ class OptionMenuDescriptor : MenuDescriptor native native int mIndent; native int mPosition; native bool mDontDim; + native bool mDontBlur; + native bool mAnimatedTransition; + native bool mAnimated; native Font mFont; void Reset() @@ -106,6 +109,9 @@ class OptionMenu : Menu mParentMenu = parent; mDesc = desc; DontDim = desc.mDontDim; + DontBlur = desc.mDontBlur; + AnimatedTransition = desc.mAnimatedTransition; + Animated = desc.mAnimated; let itemCount = mDesc.mItems.size(); if (itemCount > 0)