mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Fixed a crash bug when using sorting functions on an associative array(map) (from Mr.Stalin)
Added support for sorting associative arrays by keys or values.
This commit is contained in:
@@ -129,14 +129,18 @@
|
|||||||
#define array_exists(array) (len_array(array) != -1)
|
#define array_exists(array) (len_array(array) != -1)
|
||||||
// remove all elements from array
|
// remove all elements from array
|
||||||
#define clear_array(array) resize_array(array, 0)
|
#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)
|
#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)
|
#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)
|
#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)
|
#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
|
// remove element from map or just replace value with 0 for list
|
||||||
#define unset_array(array, item) set_array(array, item, 0)
|
#define unset_array(array, item) set_array(array, item, 0)
|
||||||
// same as "key_pressed" but checks VK codes instead of DX codes
|
// same as "key_pressed" but checks VK codes instead of DX codes
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ DWORD sArrayElement::getHashStatic(DWORD value, DataType type) {
|
|||||||
str = (const char*)value;
|
str = (const char*)value;
|
||||||
int i;
|
int i;
|
||||||
DWORD res;
|
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];
|
res = ((res << 5) + res) + str[i];
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@@ -197,28 +197,28 @@ void LoadArraysOld(HANDLE h) {
|
|||||||
dlogr("Loading arrays (old fmt)", DL_MAIN);
|
dlogr("Loading arrays (old fmt)", DL_MAIN);
|
||||||
DWORD count, unused, id, j;
|
DWORD count, unused, id, j;
|
||||||
ReadFile(h, &count, 4, &unused, 0);
|
ReadFile(h, &count, 4, &unused, 0);
|
||||||
if(unused!=4) return;
|
if (unused != 4) return;
|
||||||
sArrayVarOld var;
|
sArrayVarOld var;
|
||||||
sArrayVar varN;
|
sArrayVar varN;
|
||||||
for(DWORD i=0;i<count;i++) {
|
for (DWORD i = 0; i < count; i++) {
|
||||||
ReadFile(h, &id, 4, &unused, 0);
|
ReadFile(h, &id, 4, &unused, 0);
|
||||||
ReadFile(h, &var, 8, &unused, 0);
|
ReadFile(h, &var, 8, &unused, 0);
|
||||||
var.types=new DWORD[var.len];
|
var.types = new DWORD[var.len];
|
||||||
var.data=new char[var.len*var.datalen];
|
var.data = new char[var.len * var.datalen];
|
||||||
ReadFile(h, var.types, 4*var.len, &unused, 0);
|
ReadFile(h, var.types, 4 * var.len, &unused, 0);
|
||||||
ReadFile(h, var.data, var.len*var.datalen, &unused, 0);
|
ReadFile(h, var.data, var.len*var.datalen, &unused, 0);
|
||||||
varN.flags = 0;
|
varN.flags = 0;
|
||||||
varN.val.resize(var.len);
|
varN.val.resize(var.len);
|
||||||
for (j=0; j<var.len; j++) {
|
for (j = 0; j < var.len; j++) {
|
||||||
switch (var.types[j]) {
|
switch (var.types[j]) {
|
||||||
case DataType::INT:
|
case DataType::INT:
|
||||||
varN.val[j].set(*(long*)(&var.data[var.datalen*j]));
|
varN.val[j].set(*(long*)(&var.data[var.datalen * j]));
|
||||||
break;
|
break;
|
||||||
case DataType::FLOAT:
|
case DataType::FLOAT:
|
||||||
varN.val[j].set(*(float*)(&var.data[var.datalen*j]));
|
varN.val[j].set(*(float*)(&var.data[var.datalen * j]));
|
||||||
break;
|
break;
|
||||||
case DataType::STR:
|
case DataType::STR:
|
||||||
varN.val[j].set(&var.data[var.datalen*j], var.datalen - 1);
|
varN.val[j].set(&var.data[var.datalen * j], var.datalen - 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,7 +238,7 @@ void LoadArrays(HANDLE h) {
|
|||||||
if (unused != 4) return;
|
if (unused != 4) return;
|
||||||
sArrayVar arrayVar;
|
sArrayVar arrayVar;
|
||||||
nextarrayid = 1;
|
nextarrayid = 1;
|
||||||
for (DWORD i=0; i<count; i++) {
|
for (DWORD i = 0; i < count; i++) {
|
||||||
LoadArrayElement(&arrayVar.key, h);
|
LoadArrayElement(&arrayVar.key, h);
|
||||||
if (static_cast<long>(arrayVar.key.type) > 4 || arrayVar.key.intVal == 0) { // partial compatibility with 3.4
|
if (static_cast<long>(arrayVar.key.type) > 4 || arrayVar.key.intVal == 0) { // partial compatibility with 3.4
|
||||||
arrayVar.key.intVal = static_cast<long>(arrayVar.key.type);
|
arrayVar.key.intVal = static_cast<long>(arrayVar.key.type);
|
||||||
@@ -248,7 +248,7 @@ void LoadArrays(HANDLE h) {
|
|||||||
ReadFile(h, &elCount, 4, &unused, 0); // actual number of elements: keys+values
|
ReadFile(h, &elCount, 4, &unused, 0); // actual number of elements: keys+values
|
||||||
bool isAssoc = arrayVar.isAssoc();
|
bool isAssoc = arrayVar.isAssoc();
|
||||||
arrayVar.val.resize(elCount);
|
arrayVar.val.resize(elCount);
|
||||||
for (j=0; j<elCount; j++) { // normal and associative arrays stored and loaded equally
|
for (j = 0; j < elCount; j++) { // normal and associative arrays stored and loaded equally
|
||||||
LoadArrayElement(&arrayVar.val[j], h);
|
LoadArrayElement(&arrayVar.val[j], h);
|
||||||
if (isAssoc && (j % 2) == 0) { // only difference is that keyHash is filled with appropriate indexes
|
if (isAssoc && (j % 2) == 0) { // only difference is that keyHash is filled with appropriate indexes
|
||||||
arrayVar.keyHash[arrayVar.val[j]] = j;
|
arrayVar.keyHash[arrayVar.val[j]] = j;
|
||||||
@@ -298,12 +298,12 @@ int GetNumArrays() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GetArrays(int* _arrays) {
|
void GetArrays(int* _arrays) {
|
||||||
int pos=0;
|
int pos = 0;
|
||||||
array_citr itr=arrays.begin();
|
array_citr itr = arrays.begin();
|
||||||
while(itr!=arrays.end()) {
|
while (itr != arrays.end()) {
|
||||||
_arrays[pos++]=itr->first;
|
_arrays[pos++] = itr->first;
|
||||||
_arrays[pos++]=itr->second.size();
|
_arrays[pos++] = itr->second.size();
|
||||||
_arrays[pos++]=itr->second.flags;
|
_arrays[pos++] = itr->second.flags;
|
||||||
itr++;
|
itr++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,7 +315,7 @@ void DEGetArray(int id, DWORD* types, void* data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DESetArray(int id, const DWORD* types, const 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);
|
//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()) {
|
if (!var.isAssoc()) {
|
||||||
var.val.resize(len);
|
var.val.resize(len);
|
||||||
}
|
}
|
||||||
while(arrays.find(nextarrayid)!=arrays.end()) nextarrayid++;
|
while (arrays.find(nextarrayid) != arrays.end()) nextarrayid++;
|
||||||
if (nextarrayid == 0) nextarrayid++;
|
if (nextarrayid == 0) nextarrayid++;
|
||||||
if (arraysBehavior == 0) {
|
if (arraysBehavior == 0) {
|
||||||
var.key = sArrayElement(nextarrayid, DataType::INT);
|
var.key = sArrayElement(nextarrayid, DataType::INT);
|
||||||
savedArrays[var.key] = nextarrayid;
|
savedArrays[var.key] = nextarrayid;
|
||||||
}
|
}
|
||||||
stackArrayId = nextarrayid;
|
stackArrayId = nextarrayid;
|
||||||
arrays[nextarrayid]=var;
|
arrays[nextarrayid] = var;
|
||||||
return nextarrayid++;
|
return nextarrayid++;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD _stdcall TempArray(DWORD len, DWORD nothing) {
|
DWORD _stdcall TempArray(DWORD len, DWORD nothing) {
|
||||||
DWORD id=CreateArray(len, nothing);
|
DWORD id = CreateArray(len, nothing);
|
||||||
tempArrays.insert(id);
|
tempArrays.insert(id);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -425,8 +425,8 @@ void _stdcall SetArray(DWORD id, const ScriptValue& key, const ScriptValue& val,
|
|||||||
if (arrays[id].isAssoc()) {
|
if (arrays[id].isAssoc()) {
|
||||||
sArrayElement sEl(key.rawValue(), key.type());
|
sArrayElement sEl(key.rawValue(), key.type());
|
||||||
ArrayKeysMap::iterator elIter = arr.keyHash.find(sEl);
|
ArrayKeysMap::iterator elIter = arr.keyHash.find(sEl);
|
||||||
el = (elIter != arr.keyHash.end())
|
el = (elIter != arr.keyHash.end())
|
||||||
? elIter->second
|
? elIter->second
|
||||||
: -1;
|
: -1;
|
||||||
if (val.isInt() && val.asInt() == 0 && allowUnset) {
|
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
|
// 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) {
|
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();
|
else return arrays[id].size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _stdcall ResizeArray(DWORD id, int newlen) {
|
template <class T>
|
||||||
if (arrays.find(id) == arrays.end() || arrays[id].size() == newlen) return;
|
void ListSort(std::vector<T> &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<std::pair<sArrayElement, sArrayElement>> 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];
|
sArrayVar &arr = arrays[id];
|
||||||
if (arr.isAssoc()) {
|
if (arr.isAssoc()) {
|
||||||
// only allow to reduce number of elements (adding range of elements is meaningless for maps)
|
// 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;
|
ArrayKeysMap::iterator itHash;
|
||||||
std::vector<sArrayElement>::iterator itVal;
|
std::vector<sArrayElement>::iterator itVal;
|
||||||
int actualLen = newlen*2;
|
int actualLen = newlen * 2;
|
||||||
for (itVal = arr.val.begin() + actualLen; itVal != arr.val.end(); itVal += 2) {
|
for (itVal = arr.val.begin() + actualLen; itVal != arr.val.end(); itVal += 2) {
|
||||||
if ((itHash = arr.keyHash.find(*itVal)) != arr.keyHash.end())
|
if ((itHash = arr.keyHash.find(*itVal)) != arr.keyHash.end())
|
||||||
arr.keyHash.erase(itHash);
|
arr.keyHash.erase(itHash);
|
||||||
}
|
}
|
||||||
arr.clearRange(actualLen);
|
arr.clearRange(actualLen);
|
||||||
arr.val.resize(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 >= 0) { // actual resize
|
||||||
if (newlen > ARRAY_MAX_SIZE) // safety
|
if (newlen > ARRAY_MAX_SIZE) // safety
|
||||||
@@ -493,21 +550,10 @@ void _stdcall ResizeArray(DWORD id, int newlen) {
|
|||||||
arr.clearRange(newlen);
|
arr.clearRange(newlen);
|
||||||
arr.val.resize(newlen);
|
arr.val.resize(newlen);
|
||||||
} else { // special functions for lists...
|
} else { // special functions for lists...
|
||||||
switch (newlen) {
|
if (newlen < ARRAY_ACTION_SHUFFLE) return -1;
|
||||||
case ARRAY_ACTION_SORT: // sort ascending
|
ListSort(arr.val, newlen);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _stdcall FixArray(DWORD id) {
|
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)) {
|
(val.isString() && strcmp(el.strVal, val.asString()) == 0)) {
|
||||||
if (arrays[id].isAssoc()) { // return key instead of index for associative arrays
|
if (arrays[id].isAssoc()) { // return key instead of index for associative arrays
|
||||||
return ScriptValue(
|
return ScriptValue(
|
||||||
static_cast<DataType>(arrays[id].val[i].type),
|
static_cast<DataType>(arrays[id].val[i].type),
|
||||||
static_cast<DWORD>(arrays[id].val[i].intVal)
|
static_cast<DWORD>(arrays[id].val[i].intVal)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -96,13 +96,14 @@ class sArrayVar
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DWORD flags;
|
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
|
ArrayKeysMap keyHash; // key element => element index, for faster lookup
|
||||||
std::vector<sArrayElement> val; // list of values or key=>value pairs (even - keys, odd - values)
|
std::vector<sArrayElement> val; // list of values or key=>value pairs (even - keys, odd - values)
|
||||||
|
|
||||||
bool isAssoc() const {
|
bool isAssoc() const {
|
||||||
return (flags & ARRAYFLAG_ASSOC);
|
return (flags & ARRAYFLAG_ASSOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
// logical array size (number of elements for normal arrays; number of key=>value pairs for associative)
|
// logical array size (number of elements for normal arrays; number of key=>value pairs for associative)
|
||||||
int size() const {
|
int size() const {
|
||||||
return isAssoc()
|
return isAssoc()
|
||||||
@@ -118,6 +119,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
sArrayVar() : flags(0), key() {}
|
sArrayVar() : flags(0), key() {}
|
||||||
|
|
||||||
// free memory used by strings
|
// free memory used by strings
|
||||||
void clear() {
|
void clear() {
|
||||||
clearRange(0);
|
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
|
// number of elements in list or pairs in map
|
||||||
int _stdcall LenArray(DWORD id);
|
int _stdcall LenArray(DWORD id);
|
||||||
// change array size (only works with list)
|
// 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
|
// make temporary array persistent
|
||||||
void _stdcall FixArray(DWORD id);
|
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
|
// searches for a given element in array and returns it's index (for list) or key (for map) or int(-1) if not found
|
||||||
|
|||||||
@@ -59,10 +59,10 @@ void sf_get_array(OpcodeContext& ctx) {
|
|||||||
auto str = Substring(ctx.arg(0).asString(), ctx.arg(1).asInt(), 1);
|
auto str = Substring(ctx.arg(0).asString(), ctx.arg(1).asInt(), 1);
|
||||||
ctx.setReturn(str);
|
ctx.setReturn(str);
|
||||||
} else {
|
} 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 {
|
} 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) {
|
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) {
|
void sf_temp_array(OpcodeContext& ctx) {
|
||||||
|
|||||||
Reference in New Issue
Block a user