mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
758 lines
20 KiB
C++
758 lines
20 KiB
C++
#include <set>
|
|
#include <algorithm>
|
|
|
|
#include "Arrays.h"
|
|
#include "FalloutEngine.h"
|
|
#include "ScriptExtender.h"
|
|
|
|
/*
|
|
GLOBAL variable for arrays
|
|
*/
|
|
DWORD arraysBehavior = 1; // 0 - backward compatible with pre-3.4, 1 - permanent arrays don't get stored in savegames by default
|
|
|
|
// arrays map: arrayId => arrayVar
|
|
ArraysMap arrays;
|
|
// auto-incremented ID
|
|
DWORD nextArrayID = 1;
|
|
// temp arrays: set of arrayId
|
|
std::set<DWORD> tempArrays;
|
|
// saved arrays: arrayKey => arrayId
|
|
ArrayKeysMap savedArrays;
|
|
// special array ID for array expressions
|
|
DWORD stackArrayId;
|
|
|
|
static char get_all_arrays_special_key[] = "...all_arrays...";
|
|
|
|
sArrayElement::sArrayElement() : len(0), intVal(0), type(DATATYPE_NONE) { }
|
|
|
|
sArrayElement::sArrayElement( DWORD _val, DWORD _dataType ) : len(0), type(_dataType), intVal(_val)
|
|
{
|
|
if (DATATYPE_STR == _dataType) len = strlen((char*)_val);
|
|
}
|
|
|
|
sArrayElement::sArrayElement(const long& other)
|
|
{
|
|
set(other);
|
|
}
|
|
|
|
void sArrayElement::clear()
|
|
{
|
|
if (strVal && type == DATATYPE_STR) delete[] strVal;
|
|
}
|
|
|
|
void sArrayElement::setByType( DWORD val, DWORD dataType )
|
|
{
|
|
switch(dataType) {
|
|
case DATATYPE_STR:
|
|
set((char*)val);
|
|
break;
|
|
case DATATYPE_INT:
|
|
set(*(long*)&val);
|
|
break;
|
|
case DATATYPE_FLOAT:
|
|
set(*(float*)&val);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void sArrayElement::set( long val )
|
|
{
|
|
clear();
|
|
type = DATATYPE_INT;
|
|
intVal = val;
|
|
}
|
|
|
|
void sArrayElement::set( float val )
|
|
{
|
|
clear();
|
|
type = DATATYPE_FLOAT;
|
|
floatVal = val;
|
|
}
|
|
|
|
void sArrayElement::set( const char* val, int sLen /*= -1*/ )
|
|
{
|
|
clear();
|
|
type = DATATYPE_STR;
|
|
if (sLen == -1) sLen = strlen(val);
|
|
if (sLen >= ARRAY_MAX_STRING) sLen = ARRAY_MAX_STRING - 1; // memory safety
|
|
len = sLen + 1;
|
|
strVal = new char[len];
|
|
memcpy(strVal, val, sLen);
|
|
strVal[sLen] = '\0';
|
|
}
|
|
|
|
void sArrayElement::unset()
|
|
{
|
|
clear();
|
|
type = DATATYPE_NONE;
|
|
len = intVal = 0;
|
|
}
|
|
|
|
DWORD sArrayElement::getHashStatic(DWORD value, DWORD type) {
|
|
switch (type) {
|
|
case DATATYPE_STR: {
|
|
const char* str = (const char*)value;
|
|
DWORD res = 0;
|
|
for (int i = 0; str[i] != '\0'; i++) {
|
|
res = ((res << 5) + res) + str[i];
|
|
}
|
|
return res;
|
|
}
|
|
case DATATYPE_INT:
|
|
case DATATYPE_FLOAT:
|
|
return value;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
bool sArrayElement::operator<( const sArrayElement &el ) const
|
|
{
|
|
if (type < el.type) return true;
|
|
if (type == el.type) {
|
|
switch (type) {
|
|
case DATATYPE_STR:
|
|
return strcmp(strVal, el.strVal) < 0;
|
|
case DATATYPE_FLOAT:
|
|
return floatVal < el.floatVal;
|
|
case DATATYPE_INT:
|
|
default:
|
|
return intVal < el.intVal;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool sArrayElement_EqualFunc::operator()( const sArrayElement &elA, const sArrayElement &elB ) const
|
|
{
|
|
if (elA.type != elB.type) return false;
|
|
|
|
switch (elA.type) {
|
|
case DATATYPE_STR:
|
|
return strcmp(elA.strVal, elB.strVal) == 0;
|
|
case DATATYPE_INT:
|
|
case DATATYPE_FLOAT:
|
|
return elA.intVal == elB.intVal;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void sArrayVar::clearRange( int from, int to /*= -1*/ )
|
|
{
|
|
if (to == -1) to = val.size();
|
|
std::vector<sArrayElement>::iterator it, itTo = val.begin() + to;
|
|
for (it = val.begin() + from; it < itTo; ++it) {
|
|
it->clear();
|
|
}
|
|
}
|
|
|
|
void SaveArrayElement(sArrayElement* el, HANDLE h)
|
|
{
|
|
DWORD unused;
|
|
WriteFile(h, &el->type, 4, &unused, 0);
|
|
if (el->type == DATATYPE_STR) {
|
|
WriteFile(h, &el->len, 4, &unused, 0);
|
|
WriteFile(h, el->strVal, el->len, &unused, 0);
|
|
} else {
|
|
WriteFile(h, &el->intVal, 4, &unused, 0);
|
|
}
|
|
}
|
|
|
|
bool LoadArrayElement(sArrayElement* el, HANDLE h)
|
|
{
|
|
DWORD unused;
|
|
ReadFile(h, &el->type, 4, &unused, 0);
|
|
if (el->type == DATATYPE_STR) {
|
|
ReadFile(h, &el->len, 4, &unused, 0);
|
|
if (el->len > 0) {
|
|
el->strVal = new char[el->len];
|
|
ReadFile(h, el->strVal, el->len, &unused, 0);
|
|
} else
|
|
el->strVal = nullptr;
|
|
} else {
|
|
ReadFile(h, &el->intVal, 4, &unused, 0);
|
|
}
|
|
return (el->len) ? (unused != el->len) : (unused != 4);
|
|
}
|
|
|
|
static long LoadArraysOld(HANDLE h) {
|
|
DWORD count, unused, id;
|
|
ReadFile(h, &count, 4, &unused, 0); // count of saved arrays
|
|
if (unused != 4) return -1;
|
|
if (!count) return 0;
|
|
|
|
dlogr("Loading arrays (old fmt)", DL_MAIN);
|
|
|
|
sArrayVarOld var;
|
|
sArrayVar varN;
|
|
|
|
for (DWORD i = 0; i < count; i++) {
|
|
ReadFile(h, &id, 4, &unused, 0);
|
|
ReadFile(h, &var, 8, &unused, 0);
|
|
if (unused != 8) return -1;
|
|
|
|
var.types = new DWORD[var.len];
|
|
var.data = new char[var.len * var.datalen];
|
|
|
|
ReadFile(h, var.types, (4 * var.len), &unused, 0);
|
|
ReadFile(h, var.data, (var.len * var.datalen), &unused, 0);
|
|
|
|
varN.flags = 0;
|
|
varN.val.resize(var.len);
|
|
for (size_t j = 0; j < var.len; j++) {
|
|
switch (var.types[j]) {
|
|
case DATATYPE_INT:
|
|
varN.val[j].set(*(long*)(&var.data[var.datalen * j]));
|
|
break;
|
|
case DATATYPE_FLOAT:
|
|
varN.val[j].set(*(float*)(&var.data[var.datalen * j]));
|
|
break;
|
|
case DATATYPE_STR:
|
|
varN.val[j].set(&var.data[var.datalen * j], var.datalen - 1);
|
|
break;
|
|
}
|
|
}
|
|
delete[] var.types;
|
|
delete[] var.data;
|
|
|
|
varN.key = sArrayElement(id, DATATYPE_INT);
|
|
arrays.insert(array_pair(id, varN));
|
|
savedArrays[varN.key] = id;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
long LoadArrays(HANDLE h) {
|
|
nextArrayID = 1;
|
|
|
|
long result = LoadArraysOld(h);
|
|
if (result) return result;
|
|
|
|
DWORD count, unused, elCount;
|
|
ReadFile(h, &count, 4, &unused, 0); // count of saved arrays
|
|
if (unused != 4 && !result) return 1;
|
|
|
|
dlogr("Loading arrays (new fmt)", DL_MAIN);
|
|
if (unused != 4) return -1;
|
|
|
|
sArrayVar arrayVar;
|
|
for (DWORD i = 0; i < count; i++) {
|
|
if (LoadArrayElement(&arrayVar.key, h)) return -1;
|
|
if (arrayVar.key.intVal == 0 || static_cast<long>(arrayVar.key.type) >= 4) { // partial compatibility with 3.4
|
|
arrayVar.key.intVal = static_cast<long>(arrayVar.key.type);
|
|
arrayVar.key.type = DATATYPE_INT;
|
|
}
|
|
|
|
ReadFile(h, &arrayVar.flags, 4, &unused, 0);
|
|
ReadFile(h, &elCount, 4, &unused, 0); // actual number of elements: keys+values
|
|
if (unused != 4) return -1;
|
|
|
|
bool isAssoc = arrayVar.isAssoc();
|
|
arrayVar.val.resize(elCount);
|
|
for (size_t j = 0; j < elCount; j++) { // normal and associative arrays stored and loaded equally
|
|
if (LoadArrayElement(&arrayVar.val[j], h)) return -1;
|
|
if (isAssoc && (j % 2) == 0) { // only difference is that keyHash is filled with appropriate indexes
|
|
arrayVar.keyHash[arrayVar.val[j]] = j;
|
|
}
|
|
}
|
|
while (arrays.find(nextArrayID) != arrays.end()) nextArrayID++;
|
|
if (nextArrayID == 0) nextArrayID++;
|
|
|
|
arrays.insert(array_pair(nextArrayID, arrayVar));
|
|
savedArrays[arrayVar.key] = nextArrayID++;
|
|
arrayVar.keyHash.clear();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void SaveArrays(HANDLE h) {
|
|
DWORD elCount, unused, count = 0;
|
|
WriteFile(h, &count, 4, &unused, 0); // this is for backward compatibility with 3.3-
|
|
|
|
array_itr arIt;
|
|
ArrayKeysMap::iterator it = savedArrays.begin();
|
|
while (it != savedArrays.end()) {
|
|
arIt = arrays.find(it->second);
|
|
if (arIt == arrays.end()) {
|
|
savedArrays.erase(it++);
|
|
} else {
|
|
++count;
|
|
++it;
|
|
}
|
|
}
|
|
WriteFile(h, &count, 4, &unused, 0);
|
|
|
|
for (it = savedArrays.begin(); it != savedArrays.end(); ++it) {
|
|
arIt = arrays.find(it->second);
|
|
if (arIt != arrays.end()) {
|
|
sArrayVar &var = arrays[it->second];
|
|
SaveArrayElement(&var.key, h);
|
|
WriteFile(h, &var.flags, 4, &unused, 0);
|
|
elCount = var.val.size();
|
|
WriteFile(h, &elCount, 4, &unused, 0);
|
|
for (std::vector<sArrayElement>::iterator it = var.val.begin(); it != var.val.end(); ++it) {
|
|
SaveArrayElement(&(*it), h);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int GetNumArrays() {
|
|
return arrays.size();
|
|
}
|
|
|
|
void GetArrays(int* _arrays) {
|
|
int pos = 0;
|
|
array_citr itr = arrays.begin();
|
|
while (itr != arrays.end()) {
|
|
_arrays[pos++] = itr->first; // array id
|
|
_arrays[pos++] = itr->second.isAssoc() ? 1 : 0;
|
|
_arrays[pos++] = itr->second.val.size();
|
|
_arrays[pos++] = itr->second.flags;
|
|
itr++;
|
|
}
|
|
}
|
|
|
|
void DEGetArray(int id, DWORD* types, char* data) {
|
|
int pos = 0;
|
|
if (types != nullptr) {
|
|
for (size_t i = 0; i < arrays[id].val.size(); i++) {
|
|
const sArrayElement& arVal = arrays[id].val[i];
|
|
types[pos++] = (DWORD)arVal.type;
|
|
types[pos++] = (arVal.type == DATATYPE_STR) ? arVal.len : 4; // in bytes
|
|
}
|
|
} else {
|
|
for (size_t i = 0; i < arrays[id].val.size(); i++) {
|
|
const sArrayElement& arVal = arrays[id].val[i];
|
|
if (arVal.type != DATATYPE_STR) {
|
|
*(long*)(data + pos) = arVal.intVal;
|
|
pos += 4;
|
|
} else {
|
|
strcpy(data + pos, arVal.strVal);
|
|
pos += arVal.len;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DESetArray(int id, const DWORD* types, const char* data) {
|
|
int pos = 0;
|
|
for (size_t i = 0; i < arrays[id].val.size(); i++) {
|
|
sArrayElement& arVal = arrays[id].val[i];
|
|
switch (arVal.type) {
|
|
case DATATYPE_NONE:
|
|
pos += 4;
|
|
break;
|
|
case DATATYPE_INT:
|
|
arVal.intVal = *(long*)(data + pos);
|
|
pos += 4;
|
|
break;
|
|
case DATATYPE_FLOAT:
|
|
arVal.floatVal = *(float*)(data + pos);
|
|
pos += 4;
|
|
break;
|
|
case DATATYPE_STR:
|
|
strcpy(arVal.strVal, data + pos);
|
|
pos += arVal.len;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Array manipulation functions for script operators
|
|
TODO: move somewhere else
|
|
*/
|
|
|
|
const char* _stdcall GetSfallTypeName(DWORD dataType) {
|
|
switch (dataType) {
|
|
case DATATYPE_NONE:
|
|
return "(none)";
|
|
case DATATYPE_STR:
|
|
return "string";
|
|
case DATATYPE_FLOAT:
|
|
return "float";
|
|
case DATATYPE_INT:
|
|
return "integer";
|
|
default:
|
|
return "(unknown)";
|
|
}
|
|
}
|
|
|
|
DWORD _stdcall getSfallTypeByScriptType(DWORD varType) {
|
|
switch (varType & 0xFFFF) {
|
|
case VAR_TYPE_STR2:
|
|
case VAR_TYPE_STR:
|
|
return DATATYPE_STR;
|
|
case VAR_TYPE_FLOAT:
|
|
return DATATYPE_FLOAT;
|
|
case VAR_TYPE_INT:
|
|
default:
|
|
return DATATYPE_INT;
|
|
}
|
|
}
|
|
|
|
DWORD _stdcall getScriptTypeBySfallType(DWORD dataType) {
|
|
switch (dataType) {
|
|
case DATATYPE_STR:
|
|
return VAR_TYPE_STR;
|
|
case DATATYPE_FLOAT:
|
|
return VAR_TYPE_FLOAT;
|
|
case DATATYPE_INT:
|
|
default:
|
|
return VAR_TYPE_INT;
|
|
}
|
|
}
|
|
|
|
DWORD _stdcall CreateArray(int len, DWORD flags) {
|
|
sArrayVar var;
|
|
var.flags = (flags & 0xFFFFFFFE); // reset 1 bit
|
|
if (len < 0) {
|
|
var.flags |= ARRAYFLAG_ASSOC;
|
|
} else if (len > ARRAY_MAX_SIZE) {
|
|
len = ARRAY_MAX_SIZE; // safecheck
|
|
}
|
|
if (!var.isAssoc()) {
|
|
var.val.resize(len);
|
|
}
|
|
|
|
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;
|
|
return nextArrayID++;
|
|
}
|
|
|
|
DWORD _stdcall TempArray(DWORD len, DWORD flags) {
|
|
DWORD id = CreateArray(len, flags);
|
|
tempArrays.insert(id);
|
|
return id;
|
|
}
|
|
|
|
void _stdcall FreeArray(DWORD id) {
|
|
array_itr it = arrays.find(id);
|
|
if (it != arrays.end()) {
|
|
savedArrays.erase(it->second.key);
|
|
it->second.clear();
|
|
arrays.erase(id);
|
|
}
|
|
}
|
|
|
|
void DeleteAllTempArrays() {
|
|
if (!tempArrays.empty()) {
|
|
for (std::set<DWORD>::iterator it = tempArrays.begin(); it != tempArrays.end(); ++it) {
|
|
FreeArray(*it);
|
|
}
|
|
tempArrays.clear();
|
|
}
|
|
}
|
|
|
|
DWORD _stdcall GetArrayKey(DWORD id, int index, DWORD* resultType) {
|
|
*resultType = VAR_TYPE_INT;
|
|
if (arrays.find(id) == arrays.end() || index < -1 || index > arrays[id].size()) return 0;
|
|
if (index == -1) // special index to indicate if array is associative
|
|
return (DWORD)arrays[id].isAssoc();
|
|
if (arrays[id].isAssoc()) {
|
|
index *= 2;
|
|
// for associative array - return key at the specified index
|
|
switch(arrays[id].val[index].type) {
|
|
case DATATYPE_INT:
|
|
return *(DWORD *)&arrays[id].val[index].intVal;
|
|
case DATATYPE_FLOAT:
|
|
*resultType = VAR_TYPE_FLOAT;
|
|
return *(DWORD *)&arrays[id].val[index].intVal;
|
|
case DATATYPE_STR:
|
|
*resultType = VAR_TYPE_STR;
|
|
return (DWORD)arrays[id].val[index].strVal;
|
|
case DATATYPE_NONE:
|
|
default:
|
|
return 0;
|
|
}
|
|
} else {
|
|
// just return index for normal array..
|
|
return index;
|
|
}
|
|
}
|
|
|
|
DWORD _stdcall GetArray(DWORD id, DWORD key, DWORD keyType, DWORD* resultType) {
|
|
*resultType = VAR_TYPE_INT;
|
|
if (arrays.find(id) == arrays.end()) return 0;
|
|
int el;
|
|
sArrayVar &arr = arrays[id];
|
|
if (arr.isAssoc()) {
|
|
ArrayKeysMap::iterator it = arr.keyHash.find(sArrayElement(key, getSfallTypeByScriptType(keyType)));
|
|
if (it != arr.keyHash.end())
|
|
el = it->second + 1;
|
|
else
|
|
return 0;
|
|
} else {
|
|
el = key;
|
|
// check for invalid index
|
|
if (el < 0 || el >= arr.size()) return 0;
|
|
}
|
|
switch (arr.val[el].type) {
|
|
case DATATYPE_NONE:
|
|
return 0;
|
|
case DATATYPE_INT:
|
|
return *(DWORD *)&arr.val[el].intVal;
|
|
case DATATYPE_FLOAT:
|
|
*resultType = VAR_TYPE_FLOAT;
|
|
return *(DWORD *)&arr.val[el].intVal;
|
|
case DATATYPE_STR:
|
|
*resultType = VAR_TYPE_STR;
|
|
return (DWORD)arr.val[el].strVal;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void _stdcall SetArray(DWORD id, DWORD key, DWORD keyType, DWORD val, DWORD valType, DWORD allowUnset) {
|
|
keyType = getSfallTypeByScriptType(keyType);
|
|
valType = getSfallTypeByScriptType(valType);
|
|
if (arrays.find(id) == arrays.end()) return;
|
|
int el;
|
|
sArrayVar &arr = arrays[id];
|
|
if (arrays[id].isAssoc()) {
|
|
sArrayElement sEl(key, keyType);
|
|
ArrayKeysMap::iterator elIter = arr.keyHash.find(sEl);
|
|
el = (elIter != arr.keyHash.end())
|
|
? elIter->second
|
|
: -1;
|
|
|
|
bool lookupMap = (arr.flags & ARRAYFLAG_CONSTVAL) != 0;
|
|
if (lookupMap && el != -1) return; // don't update value of key
|
|
|
|
if (allowUnset && !lookupMap && (valType == DATATYPE_INT && val == 0)) {
|
|
// after assigning zero to a key, no need to store it, because "get_array" returns 0 for non-existent keys: try unset
|
|
if (el >= 0) {
|
|
// remove from hashtable
|
|
arr.keyHash.erase(elIter);
|
|
// shift all keyHash references
|
|
for (elIter = arr.keyHash.begin(); elIter != arr.keyHash.end(); ++elIter) {
|
|
if (elIter->second >= (DWORD)el)
|
|
elIter->second -= 2;
|
|
}
|
|
// remove key=>value pair from vector
|
|
arr.clearRange(el, el + 2);
|
|
arr.val.erase(arr.val.begin() + el, arr.val.begin() + (el + 2));
|
|
}
|
|
} else {
|
|
if (el == -1) {
|
|
// size check
|
|
if (arr.size() >= ARRAY_MAX_SIZE) return;
|
|
// add pair
|
|
el = arr.val.size();
|
|
arr.val.resize(el + 2);
|
|
arr.val[el].setByType(key, keyType); // copy data
|
|
arr.keyHash[arr.val[el]] = el;
|
|
}
|
|
arr.val[el+1].setByType(val, valType);
|
|
}
|
|
} else {
|
|
// only update normal array if key is an integer and within array size
|
|
el = key;
|
|
if (keyType == DATATYPE_INT && arr.size() > el) {
|
|
arr.val[el].setByType(val, valType);
|
|
}
|
|
}
|
|
}
|
|
|
|
int _stdcall LenArray(DWORD id) {
|
|
if (arrays.find(id) == arrays.end()) return -1;
|
|
return arrays[id].size();
|
|
}
|
|
|
|
template <class T>
|
|
static 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;
|
|
}
|
|
}
|
|
|
|
static void MapSort(sArrayVar& arr, int type) {
|
|
std::vector<std::pair<sArrayElement, sArrayElement>> vmap;
|
|
vmap.reserve(arr.val.size());
|
|
|
|
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
|
|
}
|
|
vmap.push_back(std::make_pair(key, val));
|
|
}
|
|
ListSort(vmap, type);
|
|
|
|
arr.val.clear();
|
|
arr.keyHash.clear();
|
|
for (size_t i = 0; i < vmap.size(); ++i) {
|
|
size_t el = arr.val.size();
|
|
if (sortByValue) {
|
|
arr.val.push_back(vmap[i].second); // map value > key
|
|
arr.val.push_back(vmap[i].first); // map key > value
|
|
} else {
|
|
arr.val.push_back(vmap[i].first);
|
|
arr.val.push_back(vmap[i].second);
|
|
}
|
|
arr.keyHash[arr.val[el]] = el;
|
|
}
|
|
}
|
|
|
|
void _stdcall ResizeArray(DWORD id, int newlen) {
|
|
if (newlen == -1 || arrays.find(id) == arrays.end() || arrays[id].size() == newlen) return;
|
|
sArrayVar &arr = arrays[id];
|
|
if (arr.isAssoc()) {
|
|
// only allow to reduce number of elements (adding range of elements is meaningless for maps)
|
|
if (newlen >= 0 && newlen < arrays[id].size()) {
|
|
ArrayKeysMap::iterator itHash;
|
|
std::vector<sArrayElement>::iterator itVal;
|
|
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)) {
|
|
DebugPrintf("\nOPCODE ERROR: resize_array() - array sorting error.");
|
|
return;
|
|
}
|
|
MapSort(arr, newlen);
|
|
}
|
|
return;
|
|
}
|
|
if (newlen >= 0) { // actual resize
|
|
if (newlen > ARRAY_MAX_SIZE) // safety
|
|
newlen = ARRAY_MAX_SIZE;
|
|
if (newlen < arr.size())
|
|
arr.clearRange(newlen);
|
|
arr.val.resize(newlen);
|
|
} else { // special functions for lists...
|
|
if (newlen < ARRAY_ACTION_SHUFFLE) {
|
|
DebugPrintf("\nOPCODE ERROR: resize_array() - array sorting error.");
|
|
return;
|
|
}
|
|
ListSort(arr.val, newlen);
|
|
}
|
|
}
|
|
|
|
void _stdcall FixArray(DWORD id) {
|
|
tempArrays.erase(id);
|
|
}
|
|
|
|
int _stdcall ScanArray(DWORD id, DWORD val, DWORD datatype, DWORD* resultType) {
|
|
*resultType = VAR_TYPE_INT;
|
|
datatype = getSfallTypeByScriptType(datatype);
|
|
if (arrays.find(id) == arrays.end()) return -1;
|
|
char step = arrays[id].isAssoc() ? 2 : 1;
|
|
for (DWORD i = 0; i < arrays[id].val.size(); i += step) {
|
|
sArrayElement &el = arrays[id].val[i + step - 1];
|
|
if (el.type == datatype) {
|
|
if ((datatype != DATATYPE_STR && *(DWORD*)&(el.intVal) == val) ||
|
|
(datatype == DATATYPE_STR && strcmp(el.strVal, (char*)val) == 0)) {
|
|
if (arrays[id].isAssoc()) { // return key instead of index for associative arrays
|
|
*resultType = getScriptTypeBySfallType(arrays[id].val[i].type);
|
|
return *(DWORD *)&arrays[id].val[i].intVal;
|
|
} else {
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
DWORD _stdcall LoadArray(DWORD key, DWORD keyType) {
|
|
int dataType = getSfallTypeByScriptType(keyType);
|
|
if (dataType != DATATYPE_INT || key != 0) { // returns arrayId by it's key (ignoring int(0) because it is used to "unsave" array)
|
|
sArrayElement keyEl = sArrayElement(key, dataType);
|
|
|
|
if (keyEl.type == DATATYPE_STR && strcmp(keyEl.strVal, get_all_arrays_special_key) == 0) { // this is a special case to get temp array containing all saved arrays
|
|
DWORD tmpArrId = TempArray(savedArrays.size(), 0);
|
|
if (tmpArrId > 0) {
|
|
std::vector<sArrayElement>::iterator elIt;
|
|
ArrayKeysMap::iterator it;
|
|
sArrayVar &tmpArr = arrays[tmpArrId];
|
|
for (it = savedArrays.begin(), elIt = tmpArr.val.begin(); it != savedArrays.end(); ++it, ++elIt) {
|
|
elIt->set(it->first);
|
|
}
|
|
std::sort(tmpArr.val.begin(), tmpArr.val.end()); // sort in ascending order
|
|
return tmpArrId;
|
|
}
|
|
} else {
|
|
ArrayKeysMap::iterator it = savedArrays.find(keyEl);
|
|
if (it != savedArrays.end()) {
|
|
return it->second;
|
|
}
|
|
}
|
|
}
|
|
return 0; // not found
|
|
}
|
|
|
|
void _stdcall SaveArray(DWORD key, DWORD keyType, DWORD id) {
|
|
array_itr it = arrays.find(id), it2;
|
|
int dataType = getSfallTypeByScriptType(keyType);
|
|
if (it != arrays.end()) {
|
|
if (dataType != DATATYPE_INT || key != 0) {
|
|
// make array permanent
|
|
FixArray(it->first);
|
|
// if another array is saved under the same key, clear it
|
|
ArrayKeysMap::iterator sIt = savedArrays.find(sArrayElement(key, dataType));
|
|
if (sIt != savedArrays.end() && sIt->second != id && (it2 = arrays.find(sIt->second)) != arrays.end()) {
|
|
it2->second.key.unset();
|
|
}
|
|
it->second.key.setByType(key, dataType);
|
|
savedArrays[it->second.key] = id;
|
|
} else { // int(0) is used to "unsave" array without destroying it
|
|
int num = savedArrays.erase(it->second.key);
|
|
it->second.key.unset();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
Sets element to array created in last CreateArray call (used for array expressions)
|
|
For normal arrays, this method should be called in strict linear index order:
|
|
(create_array(0, 0) + arrayexpr(0, 20) + arrayexpr(1, 25) + arrayexpr(2, 777) + ...)
|
|
|
|
For assoc arrays order doesn't matter:
|
|
(create_array(0, 1) + arrayexpr("key1", "val1") + arrayexpr("key2", 25) + arrayexpr(3, 6.1242) + ...)
|
|
|
|
Should always return 0!
|
|
*/
|
|
DWORD _stdcall StackArray(DWORD key, DWORD keyType, DWORD val, DWORD valType) {
|
|
DWORD id = stackArrayId;
|
|
if (id == 0 || arrays.find(id) == arrays.end()) return 0;
|
|
if (!arrays[id].isAssoc()) {
|
|
// automatically resize array to fit one new element
|
|
ResizeArray(id, arrays[id].size() + 1);
|
|
}
|
|
SetArray(id, key, keyType, val, valType, 0);
|
|
return 0;
|
|
}
|