diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index d54aeec4..6a2b03d3 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -129,14 +129,18 @@ #define array_exists(array) (len_array(array) != -1) // remove all elements from array #define clear_array(array) resize_array(array, 0) -// sort array in ascending order +// sort array or map by key in ascending order #define sort_array(array) resize_array(array, -2) -// sort array in descending order +// sort array or map by key in descending order #define sort_array_reverse(array) resize_array(array, -3) -// reverse elements in list +// reverse elements in list/map #define reverse_array(array) resize_array(array, -4) -// randomly shuffle elements in list +// randomly shuffle elements in list/map #define shuffle_array(array) resize_array(array, -5) +// sort map in ascending order by value +#define sort_map_value(array) resize_array(array, -6) +// sort map in descending order by value +#define sort_map_value_desc(array) resize_array(array, -7) // remove element from map or just replace value with 0 for list #define unset_array(array, item) set_array(array, item, 0) // same as "key_pressed" but checks VK codes instead of DX codes diff --git a/sfall/Modules/Scripting/Arrays.cpp b/sfall/Modules/Scripting/Arrays.cpp index c327bc29..56738c82 100644 --- a/sfall/Modules/Scripting/Arrays.cpp +++ b/sfall/Modules/Scripting/Arrays.cpp @@ -109,7 +109,7 @@ DWORD sArrayElement::getHashStatic(DWORD value, DataType type) { str = (const char*)value; int i; DWORD res; - for (i=0, res=0; str[i]!='\0'; i++) { + for (i = 0, res = 0; str[i] != '\0'; i++) { res = ((res << 5) + res) + str[i]; } return res; @@ -197,28 +197,28 @@ void LoadArraysOld(HANDLE h) { dlogr("Loading arrays (old fmt)", DL_MAIN); DWORD count, unused, id, j; ReadFile(h, &count, 4, &unused, 0); - if(unused!=4) return; + if (unused != 4) return; sArrayVarOld var; sArrayVar varN; - for(DWORD i=0;i(arrayVar.key.type) > 4 || arrayVar.key.intVal == 0) { // partial compatibility with 3.4 arrayVar.key.intVal = static_cast(arrayVar.key.type); @@ -248,7 +248,7 @@ void LoadArrays(HANDLE h) { ReadFile(h, &elCount, 4, &unused, 0); // actual number of elements: keys+values bool isAssoc = arrayVar.isAssoc(); arrayVar.val.resize(elCount); - for (j=0; jfirst; - _arrays[pos++]=itr->second.size(); - _arrays[pos++]=itr->second.flags; + int pos = 0; + array_citr itr = arrays.begin(); + while (itr != arrays.end()) { + _arrays[pos++] = itr->first; + _arrays[pos++] = itr->second.size(); + _arrays[pos++] = itr->second.flags; itr++; } } @@ -315,7 +315,7 @@ void DEGetArray(int id, DWORD* types, void* data) { } void DESetArray(int id, const DWORD* types, const void* data) { - //if(types) memcpy(arrays[id].types, types, arrays[id].len*4); + //if (types) memcpy(arrays[id].types, types, arrays[id].len * 4); //memcpy(arrays[id].data, data, arrays[id].len*arrays[id].datalen); } @@ -332,19 +332,19 @@ DWORD _stdcall CreateArray(int len, DWORD nothing) { if (!var.isAssoc()) { var.val.resize(len); } - while(arrays.find(nextarrayid)!=arrays.end()) nextarrayid++; + while (arrays.find(nextarrayid) != arrays.end()) nextarrayid++; if (nextarrayid == 0) nextarrayid++; if (arraysBehavior == 0) { var.key = sArrayElement(nextarrayid, DataType::INT); savedArrays[var.key] = nextarrayid; } stackArrayId = nextarrayid; - arrays[nextarrayid]=var; + arrays[nextarrayid] = var; return nextarrayid++; } DWORD _stdcall TempArray(DWORD len, DWORD nothing) { - DWORD id=CreateArray(len, nothing); + DWORD id = CreateArray(len, nothing); tempArrays.insert(id); return id; } @@ -425,8 +425,8 @@ void _stdcall SetArray(DWORD id, const ScriptValue& key, const ScriptValue& val, if (arrays[id].isAssoc()) { sArrayElement sEl(key.rawValue(), key.type()); ArrayKeysMap::iterator elIter = arr.keyHash.find(sEl); - el = (elIter != arr.keyHash.end()) - ? elIter->second + el = (elIter != arr.keyHash.end()) + ? elIter->second : -1; if (val.isInt() && val.asInt() == 0 && allowUnset) { // after assigning zero to a key, no need to store it, because "get_array" returns 0 for non-existent keys: try unset @@ -464,27 +464,84 @@ void _stdcall SetArray(DWORD id, const ScriptValue& key, const ScriptValue& val, } int _stdcall LenArray(DWORD id) { - if(arrays.find(id)==arrays.end()) return -1; + if (arrays.find(id)==arrays.end()) return -1; else return arrays[id].size(); } -void _stdcall ResizeArray(DWORD id, int newlen) { - if (arrays.find(id) == arrays.end() || arrays[id].size() == newlen) return; +template +void ListSort(std::vector &arr, int type) { + switch (type) { + case ARRAY_ACTION_SORT: // sort ascending + std::sort(arr.begin(), arr.end()); + break; + case ARRAY_ACTION_RSORT: // sort descending + std::sort(arr.rbegin(), arr.rend()); + break; + case ARRAY_ACTION_REVERSE: // reverse elements + std::reverse(arr.rbegin(), arr.rend()); + break; + case ARRAY_ACTION_SHUFFLE: // shuffle elements + std::random_shuffle(arr.rbegin(), arr.rend()); + break; + } +} + +void MapSort(sArrayVar& arr, int type) { + std::vector> map; + bool sortByValue = false; + if (type < ARRAY_ACTION_SHUFFLE) { + type += 4; + sortByValue = true; + } + + sArrayElement key, val; + for (size_t i = 0; i < arr.val.size(); ++i) { + if (sortByValue) { + val = arr.val[i++]; // map key > value + key = arr.val[i]; // map value > key + } else { + key = arr.val[i]; // key + val = arr.val[++i]; // value + } + map.emplace_back(std::make_pair(key, val)); + } + ListSort(map, type); + + arr.val.clear(); + arr.keyHash.clear(); + for (size_t i = 0; i < map.size(); ++i) { + auto el = arr.val.size(); + if (sortByValue) { + arr.val.emplace_back(map[i].second); // map value > key + arr.val.emplace_back(map[i].first); // map key > value + } else { + arr.val.emplace_back(map[i].first); + arr.val.emplace_back(map[i].second); + } + arr.keyHash[arr.val[el]] = el; + } +} + +long _stdcall ResizeArray(DWORD id, int newlen) { + if (newlen == -1 || arrays.find(id) == arrays.end() || arrays[id].size() == newlen) return 0; sArrayVar &arr = arrays[id]; if (arr.isAssoc()) { // only allow to reduce number of elements (adding range of elements is meaningless for maps) - if (newlen < arrays[id].size()) { + if (newlen >= 0 && newlen < arrays[id].size()) { ArrayKeysMap::iterator itHash; std::vector::iterator itVal; - int actualLen = newlen*2; + int actualLen = newlen * 2; for (itVal = arr.val.begin() + actualLen; itVal != arr.val.end(); itVal += 2) { if ((itHash = arr.keyHash.find(*itVal)) != arr.keyHash.end()) arr.keyHash.erase(itHash); } arr.clearRange(actualLen); arr.val.resize(actualLen); + } else if (newlen < 0) { + if (newlen < (ARRAY_ACTION_SHUFFLE - 2)) return -1; + MapSort(arr, newlen); } - return; + return 0; } if (newlen >= 0) { // actual resize if (newlen > ARRAY_MAX_SIZE) // safety @@ -493,21 +550,10 @@ void _stdcall ResizeArray(DWORD id, int newlen) { arr.clearRange(newlen); arr.val.resize(newlen); } else { // special functions for lists... - switch (newlen) { - case ARRAY_ACTION_SORT: // sort ascending - std::sort(arr.val.begin(), arr.val.end()); - break; - case ARRAY_ACTION_RSORT: // sort descending - std::sort(arr.val.rbegin(), arr.val.rend()); - break; - case ARRAY_ACTION_REVERSE: // reverse elements - std::reverse(arr.val.rbegin(), arr.val.rend()); - break; - case ARRAY_ACTION_SHUFFLE: // shuffle elements - std::random_shuffle(arr.val.rbegin(), arr.val.rend()); - break; - } + if (newlen < ARRAY_ACTION_SHUFFLE) return -1; + ListSort(arr.val, newlen); } + return 0; } void _stdcall FixArray(DWORD id) { @@ -526,7 +572,7 @@ ScriptValue _stdcall ScanArray(DWORD id, const ScriptValue& val) { (val.isString() && strcmp(el.strVal, val.asString()) == 0)) { if (arrays[id].isAssoc()) { // return key instead of index for associative arrays return ScriptValue( - static_cast(arrays[id].val[i].type), + static_cast(arrays[id].val[i].type), static_cast(arrays[id].val[i].intVal) ); } else { diff --git a/sfall/Modules/Scripting/Arrays.h b/sfall/Modules/Scripting/Arrays.h index 1949db9b..42f9542e 100644 --- a/sfall/Modules/Scripting/Arrays.h +++ b/sfall/Modules/Scripting/Arrays.h @@ -96,13 +96,14 @@ class sArrayVar { public: DWORD flags; - sArrayElement key; // array associated key, if it was saved + sArrayElement key; // array associated key, if it was saved ArrayKeysMap keyHash; // key element => element index, for faster lookup std::vector val; // list of values or key=>value pairs (even - keys, odd - values) bool isAssoc() const { return (flags & ARRAYFLAG_ASSOC); } + // logical array size (number of elements for normal arrays; number of key=>value pairs for associative) int size() const { return isAssoc() @@ -118,6 +119,7 @@ public: } sArrayVar() : flags(0), key() {} + // free memory used by strings void clear() { clearRange(0); @@ -168,7 +170,7 @@ void _stdcall SetArray(DWORD id, const ScriptValue& key, const ScriptValue& val, // number of elements in list or pairs in map int _stdcall LenArray(DWORD id); // change array size (only works with list) -void _stdcall ResizeArray(DWORD id, int newlen); +long _stdcall ResizeArray(DWORD id, int newlen); // make temporary array persistent void _stdcall FixArray(DWORD id); // searches for a given element in array and returns it's index (for list) or key (for map) or int(-1) if not found diff --git a/sfall/Modules/Scripting/Handlers/Arrays.cpp b/sfall/Modules/Scripting/Handlers/Arrays.cpp index bb430f63..ab90f0e8 100644 --- a/sfall/Modules/Scripting/Handlers/Arrays.cpp +++ b/sfall/Modules/Scripting/Handlers/Arrays.cpp @@ -59,10 +59,10 @@ void sf_get_array(OpcodeContext& ctx) { auto str = Substring(ctx.arg(0).asString(), ctx.arg(1).asInt(), 1); ctx.setReturn(str); } else { - ctx.printOpcodeError("get_array - index must be numeric when used on a string."); + ctx.printOpcodeError("get_array() - index must be numeric when used on a string."); } } else { - ctx.printOpcodeError("get-array - argument 0 must be an array ID or a string."); + ctx.printOpcodeError("get_array() - argument 0 must be an array ID or a string."); } } @@ -77,7 +77,9 @@ void sf_len_array(OpcodeContext& ctx) { } void sf_resize_array(OpcodeContext& ctx) { - ResizeArray(ctx.arg(0).asInt(), ctx.arg(1).asInt()); + if (ResizeArray(ctx.arg(0).asInt(), ctx.arg(1).asInt())) { + ctx.printOpcodeError("resize_array() - array sorting error."); + } } void sf_temp_array(OpcodeContext& ctx) {