This commit is contained in:
Alexander Batalov 2023-05-27 17:11:46 +03:00
parent 287ed6d49c
commit cae990b65e
6 changed files with 345 additions and 278 deletions

View File

@ -349,6 +349,11 @@ int gameInitWithOptions(const char* windowTitle, bool isMapper, int font, int a4
return -1; return -1;
} }
if (!sfallArraysInit()) {
debugPrint("Failed on sfallArraysInit");
return -1;
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MISC, &gMiscMessageList); messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_MISC, &gMiscMessageList);
return 0; return 0;
@ -405,6 +410,7 @@ void gameExit()
debugPrint("\nGame Exit\n"); debugPrint("\nGame Exit\n");
// SFALL // SFALL
sfallArraysExit();
sfallListsExit(); sfallListsExit();
sfallGlobalVarsExit(); sfallGlobalVarsExit();
premadeCharactersExit(); premadeCharactersExit();

View File

@ -912,19 +912,6 @@ int _getPartyMemberCount()
return count; return count;
} }
std::vector<Object*> get_all_party_members_objects(bool include_hidden)
{
std::vector<Object*> value;
for (int index = 0; index < gPartyMembersLength; index++) {
auto p_object = gPartyMembers[index].object;
bool is_not_hidden = PID_TYPE(p_object->pid) == OBJ_TYPE_CRITTER && !critterIsDead(p_object) && !((p_object->flags & OBJECT_HIDDEN) != 0);
if (include_hidden || is_not_hidden) {
value.push_back(p_object);
}
}
return value;
}
// 0x495070 // 0x495070
static int _partyMemberNewObjID() static int _partyMemberNewObjID()
{ {
@ -1683,4 +1670,20 @@ int partyGetMaxWoundToHealByRest()
return maxWound; return maxWound;
} }
std::vector<Object*> get_all_party_members_objects(bool include_hidden)
{
std::vector<Object*> value;
value.reserve(gPartyMembersLength);
for (int index = 0; index < gPartyMembersLength; index++) {
auto object = gPartyMembers[index].object;
if (include_hidden
|| (PID_TYPE(object->pid) == OBJ_TYPE_CRITTER
&& !critterIsDead(object)
&& (object->flags & OBJECT_HIDDEN) == 0)) {
value.push_back(object);
}
}
return value;
}
} // namespace fallout } // namespace fallout

View File

@ -1,10 +1,11 @@
#ifndef PARTY_MEMBER_H #ifndef PARTY_MEMBER_H
#define PARTY_MEMBER_H #define PARTY_MEMBER_H
#include <vector>
#include "db.h" #include "db.h"
#include "obj_types.h" #include "obj_types.h"
#include "scripts.h" #include "scripts.h"
#include <vector>
namespace fallout { namespace fallout {

View File

@ -1,25 +1,20 @@
#include "sfall_arrays.h" #include "sfall_arrays.h"
#include "interpreter.h"
#include <assert.h>
#include <string.h>
#include <algorithm> #include <algorithm>
#include <cstdint>
#include <map>
#include <memory> #include <memory>
#include <random> #include <random>
#include <stdexcept>
#include <string.h>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
/* #include "interpreter.h"
Used https://gist.github.com/phobos2077/6bf357c49caaf515371a13f9a2d74a41 for testing
*/
namespace fallout { namespace fallout {
static ArrayId nextArrayID = 1; static constexpr ArrayId kInitialArrayId = 1;
static ArrayId stackArrayId = 0;
#define ARRAY_MAX_STRING (255) // maximum length of string to be stored as array key or value #define ARRAY_MAX_STRING (255) // maximum length of string to be stored as array key or value
#define ARRAY_MAX_SIZE (100000) // maximum number of array elements, #define ARRAY_MAX_SIZE (100000) // maximum number of array elements,
@ -70,46 +65,26 @@ enum class ArrayElementType {
* *
*/ */
class ArrayElement { class ArrayElement {
private:
ArrayElementType type;
union {
int integerValue;
float floatValue;
char* stringValue;
void* pointerValue;
} value;
void init_from_string(const char* str, int sLen)
{
type = ArrayElementType::STRING;
if (sLen == -1) sLen = strlen(str);
if (sLen >= ARRAY_MAX_STRING) sLen = ARRAY_MAX_STRING - 1; // memory safety
value.stringValue = (char*)malloc(sLen + 1);
memcpy(value.stringValue, str, sLen);
value.stringValue[sLen] = '\0';
}
public: public:
ArrayElement() ArrayElement()
: type(ArrayElementType::INT) : type(ArrayElementType::INT)
, value( , value({ 0 })
{ 0 }) { {
// Nothing here }
};
ArrayElement(const ArrayElement& other) = delete; ArrayElement(const ArrayElement& other) = delete;
ArrayElement& operator=(const ArrayElement& rhs) = delete; ArrayElement& operator=(const ArrayElement& rhs) = delete;
ArrayElement(ArrayElement&& other)
ArrayElement(ArrayElement&& other) noexcept
: type(ArrayElementType::INT) : type(ArrayElementType::INT)
, value( , value({ 0 })
{ 0 })
{ {
std::swap(type, other.type); std::swap(type, other.type);
std::swap(value, other.value); std::swap(value, other.value);
}; }
ArrayElement& operator=(ArrayElement&& other)
ArrayElement& operator=(ArrayElement&& other) noexcept
{ {
std::swap(type, other.type); std::swap(type, other.type);
std::swap(value, other.value); std::swap(value, other.value);
@ -133,19 +108,23 @@ public:
break; break;
case VALUE_TYPE_STRING: case VALUE_TYPE_STRING:
case VALUE_TYPE_DYNAMIC_STRING: case VALUE_TYPE_DYNAMIC_STRING:
auto str = programGetString(program, programValue.opcode, programValue.integerValue); setString(programGetString(program, programValue.opcode, programValue.integerValue), -1);
init_from_string(str, -1); break;
default:
type = ArrayElementType::INT;
value.integerValue = 0;
break; break;
} }
} }
ArrayElement(const char* str) ArrayElement(const char* str)
{ {
init_from_string(str, -1); setString(str, -1);
} }
ArrayElement(const char* str, int sLen)
ArrayElement(const char* str, size_t sLen)
{ {
init_from_string(str, sLen); setString(str, sLen);
} }
ProgramValue toValue(Program* program) const ProgramValue toValue(Program* program) const
@ -155,29 +134,29 @@ public:
case ArrayElementType::INT: case ArrayElementType::INT:
out.opcode = VALUE_TYPE_INT; out.opcode = VALUE_TYPE_INT;
out.integerValue = value.integerValue; out.integerValue = value.integerValue;
return out; break;
case ArrayElementType::FLOAT: case ArrayElementType::FLOAT:
out.opcode = VALUE_TYPE_FLOAT; out.opcode = VALUE_TYPE_FLOAT;
out.floatValue = value.floatValue; out.floatValue = value.floatValue;
return out; break;
case ArrayElementType::POINTER: case ArrayElementType::POINTER:
out.opcode = VALUE_TYPE_PTR; out.opcode = VALUE_TYPE_PTR;
out.pointerValue = value.pointerValue; out.pointerValue = value.pointerValue;
return out; break;
case ArrayElementType::STRING: case ArrayElementType::STRING:
out.opcode = VALUE_TYPE_DYNAMIC_STRING; out.opcode = VALUE_TYPE_DYNAMIC_STRING;
out.integerValue = programPushString(program, value.stringValue); out.integerValue = programPushString(program, value.stringValue);
return out; break;
default:
throw(std::exception());
} }
return out;
} }
bool operator<(ArrayElement const& other) const bool operator<(ArrayElement const& other) const
{ {
if (type != other.type) { if (type != other.type) {
return type < other.type; return type < other.type;
}; }
switch (type) { switch (type) {
case ArrayElementType::INT: case ArrayElementType::INT:
return value.integerValue < other.value.integerValue; return value.integerValue < other.value.integerValue;
@ -188,14 +167,16 @@ public:
case ArrayElementType::STRING: case ArrayElementType::STRING:
return strcmp(value.stringValue, other.value.stringValue) < 0; return strcmp(value.stringValue, other.value.stringValue) < 0;
default: default:
throw(std::exception()); return false;
} }
} }
bool operator==(ArrayElement const& other) const bool operator==(ArrayElement const& other) const
{ {
if (type != other.type) { if (type != other.type) {
return false; return false;
}; }
switch (type) { switch (type) {
case ArrayElementType::INT: case ArrayElementType::INT:
return value.integerValue == other.value.integerValue; return value.integerValue == other.value.integerValue;
@ -206,7 +187,7 @@ public:
case ArrayElementType::STRING: case ArrayElementType::STRING:
return strcmp(value.stringValue, other.value.stringValue) == 0; return strcmp(value.stringValue, other.value.stringValue) == 0;
default: default:
throw(std::exception()); return false;
} }
} }
@ -214,68 +195,103 @@ public:
{ {
if (type == ArrayElementType::STRING) { if (type == ArrayElementType::STRING) {
free(value.stringValue); free(value.stringValue);
}; }
} }
private:
void setString(const char* str, size_t sLen)
{
type = ArrayElementType::STRING;
if (sLen == -1) {
sLen = strlen(str);
}
if (sLen >= ARRAY_MAX_STRING) {
sLen = ARRAY_MAX_STRING - 1; // memory safety
}
value.stringValue = (char*)malloc(sLen + 1);
memcpy(value.stringValue, str, sLen);
value.stringValue[sLen] = '\0';
}
ArrayElementType type;
union {
int integerValue;
float floatValue;
char* stringValue;
void* pointerValue;
} value;
}; };
class SFallArray { class SFallArray {
protected:
uint32_t mFlags;
public: public:
virtual int size() = 0; SFallArray(unsigned int flags)
: flags(flags)
{
}
virtual ~SFallArray() = default;
virtual ProgramValue GetArrayKey(int index, Program* program) = 0; virtual ProgramValue GetArrayKey(int index, Program* program) = 0;
virtual ProgramValue GetArray(const ProgramValue& key, Program* program) = 0; virtual ProgramValue GetArray(const ProgramValue& key, Program* program) = 0;
virtual void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program) = 0; virtual void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program) = 0;
virtual void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset) = 0; virtual void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset) = 0;
virtual void ResizeArray(int newLen) = 0; virtual void ResizeArray(int newLen) = 0;
virtual ProgramValue ScanArray(const ProgramValue& value, Program* program) = 0; virtual ProgramValue ScanArray(const ProgramValue& value, Program* program) = 0;
virtual int size() = 0;
virtual ~SFallArray() = default; bool isReadOnly() const
{
return (flags & SFALL_ARRAYFLAG_CONSTVAL) != 0;
}
protected:
unsigned int flags;
}; };
class SFallArrayList : public SFallArray { class SFallArrayList : public SFallArray {
private:
std::vector<ArrayElement> values;
public: public:
SFallArrayList() = delete; SFallArrayList() = delete;
SFallArrayList(unsigned int len, uint32_t flags) SFallArrayList(unsigned int len, unsigned int flags)
: SFallArray(flags)
{ {
values.resize(len); values.resize(len);
mFlags = flags;
} }
int size() int size()
{ {
return values.size(); return static_cast<int>(values.size());
} }
ProgramValue GetArrayKey(int index, Program* program) ProgramValue GetArrayKey(int index, Program* program)
{ {
if (index < -1 || index > size()) { if (index < -1 || index > size()) {
return ProgramValue(0); return ProgramValue(0);
}; }
if (index == -1) { // special index to indicate if array is associative if (index == -1) { // special index to indicate if array is associative
return ProgramValue(0); return ProgramValue(0);
}; }
return ProgramValue(index); return ProgramValue(index);
} }
ProgramValue GetArray(const ProgramValue& key, Program* program) ProgramValue GetArray(const ProgramValue& key, Program* program)
{ {
auto element_index = key.asInt(); auto index = key.asInt();
if (element_index < 0 || element_index >= size()) { if (index < 0 || index >= size()) {
return ProgramValue(0); return ProgramValue(0);
}; }
return values[element_index].toValue(program);
return values[index].toValue(program);
} }
void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program) void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program)
{ {
SetArray(key, ArrayElement { val, program }, allowUnset); SetArray(key, ArrayElement { val, program }, allowUnset);
} }
void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset) void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset)
{ {
if (key.isInt()) { if (key.isInt()) {
@ -290,11 +306,14 @@ public:
{ {
if (newLen == -1 || size() == newLen) { if (newLen == -1 || size() == newLen) {
return; return;
}; }
if (newLen == 0) { if (newLen == 0) {
values.clear(); values.clear();
} else if (newLen > 0) { } else if (newLen > 0) {
if (newLen > ARRAY_MAX_SIZE) newLen = ARRAY_MAX_SIZE; // safety if (newLen > ARRAY_MAX_SIZE) {
newLen = ARRAY_MAX_SIZE; // safety
}
values.resize(newLen); values.resize(newLen);
} else if (newLen >= ARRAY_ACTION_SHUFFLE) { } else if (newLen >= ARRAY_ACTION_SHUFFLE) {
@ -304,156 +323,190 @@ public:
ProgramValue ScanArray(const ProgramValue& value, Program* program) ProgramValue ScanArray(const ProgramValue& value, Program* program)
{ {
auto arrValue = ArrayElement { value, program }; auto element = ArrayElement { value, program };
for (size_t i = 0; i < size(); i++) { for (int i = 0; i < size(); i++) {
if (arrValue == values[i]) { if (element == values[i]) {
return ProgramValue(i); return ProgramValue(i);
} }
} }
return ProgramValue(-1); return ProgramValue(-1);
} }
private:
std::vector<ArrayElement> values;
}; };
class SFallArrayAssoc : public SFallArray { class SFallArrayAssoc : public SFallArray {
private:
std::vector<ArrayElement> keys;
std::map<ArrayElement, ArrayElement> map;
void MapSort(int newLen);
public: public:
SFallArrayAssoc() = delete; SFallArrayAssoc() = delete;
SFallArrayAssoc(uint32_t flags) SFallArrayAssoc(unsigned int flags)
: SFallArray(flags)
{ {
mFlags = flags;
} }
int size() int size()
{ {
return keys.size(); return static_cast<int>(pairs.size());
} }
ProgramValue GetArrayKey(int index, Program* program) ProgramValue GetArrayKey(int index, Program* program)
{ {
if (index < -1 || index > size()) { if (index < -1 || index > size()) {
return ProgramValue(0); return ProgramValue(0);
}; }
if (index == -1) { // special index to indicate if array is associative if (index == -1) { // special index to indicate if array is associative
return ProgramValue(1); return ProgramValue(1);
}; }
return keys[index].toValue(program); return pairs[index].key.toValue(program);
} }
ProgramValue GetArray(const ProgramValue& key, Program* program) ProgramValue GetArray(const ProgramValue& key, Program* program)
{ {
auto iter = map.find(ArrayElement { key, program }); auto keyEl = ArrayElement { key, program };
if (iter == map.end()) { auto it = std::find_if(pairs.begin(), pairs.end(), [&keyEl](const KeyValuePair& pair) -> bool {
return ProgramValue(0); return pair.key == keyEl;
}; });
return iter->second.toValue(program); if (it == pairs.end()) {
return ProgramValue(0);
}
auto index = it - pairs.begin();
return pairs[index].value.toValue(program);
} }
void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program) void SetArray(const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program)
{ {
auto keyEl = ArrayElement { key, program }; auto keyEl = ArrayElement { key, program };
auto it = std::find_if(pairs.begin(), pairs.end(), [&keyEl](const KeyValuePair& pair) -> bool {
return pair.key == keyEl;
});
auto iter = map.find(keyEl); if (it != pairs.end() && isReadOnly()) {
bool lookupMap = (mFlags & SFALL_ARRAYFLAG_CONSTVAL) != 0;
if (iter != map.end() && lookupMap) {
// don't update value of key // don't update value of key
return; return;
} }
if (allowUnset && !lookupMap && val.isInt() && val.asInt() == 0) { if (allowUnset && !isReadOnly() && val.isInt() && val.asInt() == 0) {
// 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
if (iter != map.end()) { if (it != pairs.end()) {
map.erase(iter); pairs.erase(it);
if (keys.size() == 0) {
throw(std::exception());
}
auto it = std::find(keys.begin(),
keys.end(), keyEl);
if (it == keys.end()) {
throw(std::exception());
};
keys.erase(it);
} }
} else { } else {
if (iter == map.end()) { if (it == pairs.end()) {
// size check // size check
if (size() >= ARRAY_MAX_SIZE) return; if (size() >= ARRAY_MAX_SIZE) {
return;
}
keys.push_back(std::move(keyEl)); pairs.push_back(KeyValuePair { std::move(keyEl), ArrayElement { val, program } });
// Not very good that we copy string into map key and into keys array
map.emplace(ArrayElement { key, program }, ArrayElement { val, program });
} else { } else {
auto newValue = ArrayElement { val, program }; auto index = it - pairs.begin();
std::swap(map.at(keyEl), newValue); std::swap(pairs[index].value, ArrayElement { val, program });
} }
} }
} }
void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset) void SetArray(const ProgramValue& key, ArrayElement&& val, bool allowUnset)
{ {
throw std::invalid_argument("This method is not used for associative arrays thus it is not implemented"); assert(false && "This method is not used for associative arrays thus it is not implemented");
} }
void ResizeArray(int newLen) void ResizeArray(int newLen)
{ {
if (newLen == -1 || size() == newLen) { if (newLen == -1 || size() == newLen) {
return; return;
}; }
// 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 >= 0 && newLen < size()) { if (newLen >= 0 && newLen < size()) {
for (size_t i = newLen; i < size(); i++) { pairs.resize(newLen);
map.erase(keys[i]);
};
keys.resize(newLen);
} else if (newLen < 0) { } else if (newLen < 0) {
if (newLen < (ARRAY_ACTION_SHUFFLE - 2)) return; if (newLen < (ARRAY_ACTION_SHUFFLE - 2)) return;
MapSort(newLen); MapSort(newLen);
} }
}; }
ProgramValue ScanArray(const ProgramValue& value, Program* program) ProgramValue ScanArray(const ProgramValue& value, Program* program)
{ {
auto valueEl = ArrayElement { value, program }; auto valueEl = ArrayElement { value, program };
for (const auto& pair : map) { auto it = std::find_if(pairs.begin(), pairs.end(), [&valueEl](const KeyValuePair& pair) {
if (valueEl == pair.second) { return pair.value == valueEl;
return pair.first.toValue(program); });
}
if (it == pairs.end()) {
return ProgramValue(-1);
} }
return ProgramValue(-1);
return it->key.toValue(program);
} }
private:
struct KeyValuePair {
ArrayElement key;
ArrayElement value;
};
void MapSort(int type)
{
bool sortByValue = false;
if (type < ARRAY_ACTION_SHUFFLE) {
type += 4;
sortByValue = true;
}
if (sortByValue) {
ListSort(pairs, type, [](const KeyValuePair& a, const KeyValuePair& b) -> bool {
return a.value < b.value;
});
} else {
ListSort(pairs, type, [](const KeyValuePair& a, const KeyValuePair& b) -> bool {
return a.key < b.key;
});
}
}
std::vector<KeyValuePair> pairs;
}; };
void SFallArrayAssoc::MapSort(int type) struct SfallArraysState {
std::unordered_map<ArrayId, std::unique_ptr<SFallArray>> arrays;
std::unordered_set<ArrayId> temporaryArrayIds;
int nextArrayId = kInitialArrayId;
};
static SfallArraysState* _state = nullptr;
bool sfallArraysInit()
{ {
bool sortByValue = false; _state = new (std::nothrow) SfallArraysState();
if (type < ARRAY_ACTION_SHUFFLE) { if (_state == nullptr) {
type += 4; return false;
sortByValue = true;
} }
if (sortByValue) { return true;
ListSort(keys, type, [this](const ArrayElement& a, const ArrayElement& b) -> bool { }
return map.at(a) < map.at(b);
}); void sfallArraysReset()
} else { {
ListSort(keys, type, std::less<ArrayElement>()); if (_state != nullptr) {
_state->arrays.clear();
_state->temporaryArrayIds.clear();
_state->nextArrayId = kInitialArrayId;
} }
} }
std::unordered_map<ArrayId, std::unique_ptr<SFallArray>> arrays; void sfallArraysExit()
std::unordered_set<ArrayId> temporaryArrays; {
if (_state != nullptr) {
delete _state;
}
}
ArrayId CreateArray(int len, uint32_t flags) ArrayId CreateArray(int len, unsigned int flags)
{ {
flags = (flags & ~1); // reset 1 bit flags = (flags & ~1); // reset 1 bit
@ -461,151 +514,148 @@ ArrayId CreateArray(int len, uint32_t flags)
flags |= SFALL_ARRAYFLAG_ASSOC; flags |= SFALL_ARRAYFLAG_ASSOC;
} else if (len > ARRAY_MAX_SIZE) { } else if (len > ARRAY_MAX_SIZE) {
len = ARRAY_MAX_SIZE; // safecheck len = ARRAY_MAX_SIZE; // safecheck
}; }
ArrayId array_id = nextArrayID++; ArrayId arrayId = _state->nextArrayId++;
stackArrayId = array_id;
if (flags & SFALL_ARRAYFLAG_ASSOC) { if (flags & SFALL_ARRAYFLAG_ASSOC) {
arrays.emplace(std::make_pair(array_id, std::make_unique<SFallArrayAssoc>(flags))); _state->arrays.emplace(std::make_pair(arrayId, std::make_unique<SFallArrayAssoc>(flags)));
} else { } else {
arrays.emplace(std::make_pair(array_id, std::make_unique<SFallArrayList>(len, flags))); _state->arrays.emplace(std::make_pair(arrayId, std::make_unique<SFallArrayList>(len, flags)));
} }
return array_id; return arrayId;
} }
ArrayId CreateTempArray(int len, uint32_t flags) ArrayId CreateTempArray(int len, unsigned int flags)
{ {
ArrayId array_id = CreateArray(len, flags); ArrayId arrayId = CreateArray(len, flags);
temporaryArrays.insert(array_id); _state->temporaryArrayIds.insert(arrayId);
return array_id; return arrayId;
} }
SFallArray* get_array_by_id(ArrayId array_id) SFallArray* get_array_by_id(ArrayId arrayId)
{ {
auto it = arrays.find(array_id); auto it = _state->arrays.find(arrayId);
if (it == arrays.end()) { if (it == _state->arrays.end()) {
return nullptr; return nullptr;
} else {
return it->second.get();
} }
return it->second.get();
} }
ProgramValue GetArrayKey(ArrayId array_id, int index, Program* program) ProgramValue GetArrayKey(ArrayId arrayId, int index, Program* program)
{ {
auto arr = get_array_by_id(array_id); auto arr = get_array_by_id(arrayId);
if (!arr) { if (arr == nullptr) {
return ProgramValue(0); return ProgramValue(0);
}; }
return arr->GetArrayKey(index, program); return arr->GetArrayKey(index, program);
} }
int LenArray(ArrayId array_id) int LenArray(ArrayId arrayId)
{ {
auto arr = get_array_by_id(array_id); auto arr = get_array_by_id(arrayId);
if (!arr) { if (arr == nullptr) {
return -1; return -1;
}; }
return arr->size(); return arr->size();
} }
ProgramValue GetArray(ArrayId array_id, const ProgramValue& key, Program* program) ProgramValue GetArray(ArrayId arrayId, const ProgramValue& key, Program* program)
{ {
auto arr = get_array_by_id(array_id); auto arr = get_array_by_id(arrayId);
if (arr == nullptr) {
if (!arr) {
return ProgramValue(0); return ProgramValue(0);
}; }
return arr->GetArray(key, program); return arr->GetArray(key, program);
} }
void SetArray(ArrayId array_id, const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program) void SetArray(ArrayId arrayId, const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program)
{ {
auto arr = get_array_by_id(array_id); auto arr = get_array_by_id(arrayId);
if (!arr) { if (arr == nullptr) {
return; return;
} }
arr->SetArray(key, val, allowUnset, program); arr->SetArray(key, val, allowUnset, program);
} }
void FreeArray(ArrayId array_id) void FreeArray(ArrayId arrayId)
{ {
// TODO: remove from saved_arrays // TODO: remove from saved_arrays
arrays.erase(array_id); _state->arrays.erase(arrayId);
} }
void FixArray(ArrayId id) void FixArray(ArrayId arrayId)
{ {
temporaryArrays.erase(id); _state->temporaryArrayIds.erase(arrayId);
} }
void DeleteAllTempArrays() void DeleteAllTempArrays()
{ {
for (auto it = temporaryArrays.begin(); it != temporaryArrays.end(); ++it) { for (auto it = _state->temporaryArrayIds.begin(); it != _state->temporaryArrayIds.end(); ++it) {
FreeArray(*it); FreeArray(*it);
} }
temporaryArrays.clear(); _state->temporaryArrayIds.clear();
} }
void sfallArraysReset() void ResizeArray(ArrayId arrayId, int newLen)
{ {
temporaryArrays.clear(); auto arr = get_array_by_id(arrayId);
arrays.clear(); if (arr == nullptr) {
nextArrayID = 1;
stackArrayId = 0;
}
void ResizeArray(ArrayId array_id, int newLen)
{
auto arr = get_array_by_id(array_id);
if (!arr) {
return; return;
}; }
arr->ResizeArray(newLen); arr->ResizeArray(newLen);
} }
int StackArray(const ProgramValue& key, const ProgramValue& val, Program* program) int StackArray(const ProgramValue& key, const ProgramValue& val, Program* program)
{ {
if (stackArrayId == 0) { // CE: Sfall uses eponymous global variable which is always the id of the
// last created array.
ArrayId stackArrayId = _state->nextArrayId - 1;
auto arr = get_array_by_id(stackArrayId);
if (arr == nullptr) {
return 0; return 0;
} }
auto arr = get_array_by_id(stackArrayId);
if (!arr) {
return 0;
};
auto size = arr->size(); auto size = arr->size();
if (size >= ARRAY_MAX_SIZE) return 0; if (size >= ARRAY_MAX_SIZE) {
if (key.asInt() >= size) arr->ResizeArray(size + 1); return 0;
}
if (key.asInt() >= size) {
arr->ResizeArray(size + 1);
}
SetArray(stackArrayId, key, val, false, program); SetArray(stackArrayId, key, val, false, program);
return 0; return 0;
} }
ProgramValue ScanArray(ArrayId array_id, const ProgramValue& val, Program* program) ProgramValue ScanArray(ArrayId arrayId, const ProgramValue& val, Program* program)
{ {
auto arr = get_array_by_id(array_id); auto arr = get_array_by_id(arrayId);
if (!arr) { if (arr == nullptr) {
return ProgramValue(-1); return ProgramValue(-1);
}; }
return arr->ScanArray(val, program); return arr->ScanArray(val, program);
} }
ArrayId StringSplit(const char* str, const char* split) ArrayId StringSplit(const char* str, const char* split)
{ {
int splitLen = strlen(split); size_t splitLen = strlen(split);
ArrayId array_id = CreateTempArray(0, 0); ArrayId arrayId = CreateTempArray(0, 0);
auto arr = get_array_by_id(array_id); auto arr = get_array_by_id(arrayId);
if (!splitLen) { if (splitLen == 0) {
int count = strlen(str); int count = static_cast<int>(strlen(str));
arr->ResizeArray(count); arr->ResizeArray(count);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
@ -613,30 +663,35 @@ ArrayId StringSplit(const char* str, const char* split)
} }
} else { } else {
int count = 1; int count = 1;
const char *ptr = str, *newptr; const char* ptr = str;
while (true) { while (true) {
newptr = strstr(ptr, split); const char* newptr = strstr(ptr, split);
if (!newptr) break; if (newptr == nullptr) {
break;
}
count++; count++;
ptr = newptr + splitLen; ptr = newptr + splitLen;
} }
arr->ResizeArray(count); arr->ResizeArray(count);
ptr = str;
count = 0; count = 0;
ptr = str;
while (true) { while (true) {
newptr = strstr(ptr, split); const char* newptr = strstr(ptr, split);
int len = (newptr) ? newptr - ptr : strlen(ptr); size_t len = (newptr != nullptr) ? newptr - ptr : strlen(ptr);
arr->SetArray(ProgramValue { count++ }, ArrayElement { ptr, len }, false); arr->SetArray(ProgramValue { count }, ArrayElement { ptr, len }, false);
if (!newptr) { if (newptr == nullptr) {
break; break;
} }
count++;
ptr = newptr + splitLen; ptr = newptr + splitLen;
} }
} }
return array_id; return arrayId;
} }
} }

View File

@ -1,9 +1,7 @@
#ifndef SFALL_ARRAYS #ifndef FALLOUT_SFALL_ARRAYS_H_
#define SFALL_ARRAYS #define FALLOUT_SFALL_ARRAYS_H_
#include "interpreter.h" #include "interpreter.h"
#include "object.h"
#include <cstdint>
namespace fallout { namespace fallout {
@ -13,21 +11,24 @@ namespace fallout {
using ArrayId = unsigned int; using ArrayId = unsigned int;
ArrayId CreateArray(int len, uint32_t flags); bool sfallArraysInit();
ArrayId CreateTempArray(int len, uint32_t flags);
ProgramValue GetArrayKey(ArrayId array_id, int index, Program* program);
int LenArray(ArrayId array_id);
ProgramValue GetArray(ArrayId array_id, const ProgramValue& key, Program* program);
void SetArray(ArrayId array_id, const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program);
void FreeArray(ArrayId array_id);
void FixArray(ArrayId id);
void ResizeArray(ArrayId array_id, int newLen);
void DeleteAllTempArrays();
void sfallArraysReset(); void sfallArraysReset();
void sfallArraysExit();
ArrayId CreateArray(int len, unsigned int flags);
ArrayId CreateTempArray(int len, unsigned int flags);
ProgramValue GetArrayKey(ArrayId arrayId, int index, Program* program);
int LenArray(ArrayId arrayId);
ProgramValue GetArray(ArrayId arrayId, const ProgramValue& key, Program* program);
void SetArray(ArrayId arrayId, const ProgramValue& key, const ProgramValue& val, bool allowUnset, Program* program);
void FreeArray(ArrayId arrayId);
void FixArray(ArrayId id);
void ResizeArray(ArrayId arrayId, int newLen);
void DeleteAllTempArrays();
int StackArray(const ProgramValue& key, const ProgramValue& val, Program* program); int StackArray(const ProgramValue& key, const ProgramValue& val, Program* program);
ProgramValue ScanArray(ArrayId array_id, const ProgramValue& val, Program* program); ProgramValue ScanArray(ArrayId arrayId, const ProgramValue& val, Program* program);
ArrayId StringSplit(const char* str, const char* split); ArrayId StringSplit(const char* str, const char* split);
} }
#endif /* SFALL_ARRAYS */
#endif /* FALLOUT_SFALL_ARRAYS_H_ */

View File

@ -1,5 +1,7 @@
#include "sfall_opcodes.h" #include "sfall_opcodes.h"
#include <string.h>
#include "animation.h" #include "animation.h"
#include "art.h" #include "art.h"
#include "combat.h" #include "combat.h"
@ -22,8 +24,6 @@
#include "svga.h" #include "svga.h"
#include "tile.h" #include "tile.h"
#include "worldmap.h" #include "worldmap.h"
#include <stdexcept>
#include <string.h>
namespace fallout { namespace fallout {
@ -470,8 +470,11 @@ static void opSubstr(Program* program)
if (startPos < 0) { if (startPos < 0) {
startPos += len; // start from end startPos += len; // start from end
if (startPos < 0) startPos = 0; if (startPos < 0) {
startPos = 0;
}
} }
if (length < 0) { if (length < 0) {
length += len - startPos; // cutoff at end length += len - startPos; // cutoff at end
if (length == 0) { if (length == 0) {
@ -480,17 +483,21 @@ static void opSubstr(Program* program)
} }
length = abs(length); // length can't be negative length = abs(length); // length can't be negative
} }
// check position // check position
if (startPos >= len) { if (startPos >= len) {
// start position is out of string length, return empty string // start position is out of string length, return empty string
programStackPushString(program, buf); programStackPushString(program, buf);
return; return;
}; }
if (length == 0 || length + startPos > len) { if (length == 0 || length + startPos > len) {
length = len - startPos; // set the correct length, the length of characters goes beyond the end of the string length = len - startPos; // set the correct length, the length of characters goes beyond the end of the string
} }
if (length > sizeof(buf) - 1) length = sizeof(buf) - 1; if (length > sizeof(buf) - 1) {
length = sizeof(buf) - 1;
}
memcpy(buf, &str[startPos], length); memcpy(buf, &str[startPos], length);
buf[length] = '\0'; buf[length] = '\0';
@ -543,8 +550,8 @@ static void opCreateArray(Program* program)
{ {
auto flags = programStackPopInteger(program); auto flags = programStackPopInteger(program);
auto len = programStackPopInteger(program); auto len = programStackPopInteger(program);
auto array_id = CreateArray(len, flags); auto arrayId = CreateArray(len, flags);
programStackPushInteger(program, array_id); programStackPushInteger(program, arrayId);
} }
// temp_array // temp_array
@ -552,15 +559,15 @@ static void opTempArray(Program* program)
{ {
auto flags = programStackPopInteger(program); auto flags = programStackPopInteger(program);
auto len = programStackPopInteger(program); auto len = programStackPopInteger(program);
auto array_id = CreateTempArray(len, flags); auto arrayId = CreateTempArray(len, flags);
programStackPushInteger(program, array_id); programStackPushInteger(program, arrayId);
} }
// fix_array // fix_array
static void opFixArray(Program* program) static void opFixArray(Program* program)
{ {
auto array_id = programStackPopInteger(program); auto arrayId = programStackPopInteger(program);
FixArray(array_id); FixArray(arrayId);
} }
// string_split // string_split
@ -568,10 +575,8 @@ static void opStringSplit(Program* program)
{ {
auto split = programStackPopString(program); auto split = programStackPopString(program);
auto str = programStackPopString(program); auto str = programStackPopString(program);
auto arrayId = StringSplit(str, split);
auto returnValue = StringSplit(str, split); programStackPushInteger(program, arrayId);
programStackPushInteger(program, returnValue);
} }
// set_array // set_array
@ -580,7 +585,6 @@ static void opSetArray(Program* program)
auto value = programStackPopValue(program); auto value = programStackPopValue(program);
auto key = programStackPopValue(program); auto key = programStackPopValue(program);
auto arrayId = programStackPopInteger(program); auto arrayId = programStackPopInteger(program);
SetArray(arrayId, key, value, true, program); SetArray(arrayId, key, value, true, program);
} }
@ -598,7 +602,6 @@ static void opScanArray(Program* program)
{ {
auto value = programStackPopValue(program); auto value = programStackPopValue(program);
auto arrayId = programStackPopInteger(program); auto arrayId = programStackPopInteger(program);
auto returnValue = ScanArray(arrayId, value, program); auto returnValue = ScanArray(arrayId, value, program);
programStackPushValue(program, returnValue); programStackPushValue(program, returnValue);
} }
@ -607,16 +610,14 @@ static void opScanArray(Program* program)
static void opGetArray(Program* program) static void opGetArray(Program* program)
{ {
auto key = programStackPopValue(program); auto key = programStackPopValue(program);
auto arrayId = programStackPopValue(program); auto arrayId = programStackPopValue(program);
if (arrayId.isInt()) { if (arrayId.isInt()) {
auto value = GetArray(arrayId.integerValue, key, program); auto value = GetArray(arrayId.integerValue, key, program);
programStackPushValue(program, value); programStackPushValue(program, value);
} else if (arrayId.isString() && key.isInt()) { } else if (arrayId.isString() && key.isInt()) {
auto& strVal = arrayId;
auto pos = key.asInt(); auto pos = key.asInt();
auto str = programGetString(program, strVal.opcode, strVal.integerValue); auto str = programGetString(program, arrayId.opcode, arrayId.integerValue);
char buf[2] = { 0 }; char buf[2] = { 0 };
if (pos < strlen(str)) { if (pos < strlen(str)) {
@ -655,11 +656,11 @@ static void opPartyMemberList(Program* program)
{ {
auto includeHidden = programStackPopInteger(program); auto includeHidden = programStackPopInteger(program);
auto objects = get_all_party_members_objects(includeHidden); auto objects = get_all_party_members_objects(includeHidden);
auto array_id = CreateTempArray(objects.size(), SFALL_ARRAYFLAG_RESERVED); auto arrayId = CreateTempArray(objects.size(), SFALL_ARRAYFLAG_RESERVED);
for (int i = 0; i < LenArray(array_id); i++) { for (int i = 0; i < LenArray(arrayId); i++) {
SetArray(array_id, ProgramValue { i }, ProgramValue { objects[i] }, false, program); SetArray(arrayId, ProgramValue { i }, ProgramValue { objects[i] }, false, program);
} }
programStackPushInteger(program, array_id); programStackPushInteger(program, arrayId);
} }
// type_of // type_of