Files
scummvm-cursorfix/engines/wintermute/base/scriptables/script_ext_mem_buffer.cpp
2026-02-02 04:50:13 +01:00

530 lines
15 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on WME Lite.
* http://dead-code.org/redir.php?target=wmelite
* Copyright (c) 2011 Jan Nedoma
*/
#include "engines/wintermute/base/base_scriptable.h"
#include "engines/wintermute/base/scriptables/script_stack.h"
#include "engines/wintermute/base/scriptables/script.h"
#include "engines/wintermute/base/scriptables/script_value.h"
#include "engines/wintermute/base/scriptables/script_ext_mem_buffer.h"
#include "common/file.h"
namespace Wintermute {
IMPLEMENT_PERSISTENT(SXMemBuffer, false)
BaseScriptable *makeSXMemBuffer(BaseGame *inGame, ScStack *stack) {
return new SXMemBuffer(inGame, stack);
}
//////////////////////////////////////////////////////////////////////////
SXMemBuffer::SXMemBuffer(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) {
stack->correctParams(1);
_buffer = nullptr;
_size = 0;
int newSize = stack->pop()->getInt();
resize(MAX(0, newSize));
}
//////////////////////////////////////////////////////////////////////////
SXMemBuffer::SXMemBuffer(BaseGame *inGame, void *buffer) : BaseScriptable(inGame) {
_size = 0;
_buffer = buffer;
}
//////////////////////////////////////////////////////////////////////////
SXMemBuffer::~SXMemBuffer() {
cleanup();
}
//////////////////////////////////////////////////////////////////////////
void *SXMemBuffer::scToMemBuffer() {
return _buffer;
}
//////////////////////////////////////////////////////////////////////////
void SXMemBuffer::cleanup() {
if (_size) {
free(_buffer);
}
_buffer = nullptr;
_size = 0;
}
//////////////////////////////////////////////////////////////////////////
bool SXMemBuffer::resize(int newSize) {
int oldSize = _size;
if (_size == 0) {
_buffer = malloc(newSize);
if (_buffer) {
_size = newSize;
}
} else {
void *newBuf = realloc(_buffer, newSize);
if (!newBuf) {
if (newSize == 0) {
_buffer = newBuf;
_size = newSize;
} else {
return STATUS_FAILED;
}
} else {
_buffer = newBuf;
_size = newSize;
}
}
if (_buffer && _size > oldSize) {
memset((byte *)_buffer + oldSize, 0, _size - oldSize);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
bool SXMemBuffer::checkBounds(ScScript *script, int start, int length) {
if (_buffer == nullptr) {
script->runtimeError("Cannot use Set/Get methods on an uninitialized memory buffer");
return false;
}
if (_size == 0) {
return true;
}
if (start < 0 || length == 0 || start + length > _size) {
script->runtimeError("Set/Get method call is out of bounds");
return false;
} else {
return true;
}
}
//////////////////////////////////////////////////////////////////////////
const char *SXMemBuffer::scToString() {
return "[membuffer object]";
}
//////////////////////////////////////////////////////////////////////////
bool SXMemBuffer::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
//////////////////////////////////////////////////////////////////////////
// SetSize
//////////////////////////////////////////////////////////////////////////
if (strcmp(name, "SetSize") == 0) {
stack->correctParams(1);
int newSize = stack->pop()->getInt();
newSize = MAX(0, newSize);
if (DID_SUCCEED(resize(newSize))) {
stack->pushBool(true);
} else {
stack->pushBool(false);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// GetBool
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetBool") == 0) {
stack->correctParams(1);
int start = stack->pop()->getInt();
if (!checkBounds(script, start, sizeof(byte))) {
stack->pushNULL();
} else {
stack->pushBool(*((byte *)_buffer + start) != 0);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// GetByte
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetByte") == 0) {
stack->correctParams(1);
int start = stack->pop()->getInt();
if (!checkBounds(script, start, sizeof(byte))) {
stack->pushNULL();
} else {
stack->pushInt(*(byte *)((byte *)_buffer + start));
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// GetShort
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetShort") == 0) {
stack->correctParams(1);
int start = stack->pop()->getInt();
if (!checkBounds(script, start, sizeof(short))) {
stack->pushNULL();
} else {
stack->pushInt(65536 + * (short *)((byte *)_buffer + start));
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// GetInt / GetLong
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetInt") == 0 || strcmp(name, "GetLong") == 0) {
stack->correctParams(1);
int start = stack->pop()->getInt();
if (!checkBounds(script, start, sizeof(int))) {
stack->pushNULL();
} else {
stack->pushInt(*(int *)((byte *)_buffer + start));
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// GetFloat
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetFloat") == 0) {
stack->correctParams(1);
int start = stack->pop()->getInt();
if (!checkBounds(script, start, sizeof(float))) {
stack->pushNULL();
} else {
stack->pushFloat(*(float *)((byte *)_buffer + start));
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// GetDouble
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetDouble") == 0) {
stack->correctParams(1);
int start = stack->pop()->getInt();
if (!checkBounds(script, start, sizeof(double))) {
stack->pushNULL();
} else {
stack->pushFloat(*(double *)((byte *)_buffer + start));
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// GetString
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetString") == 0) {
stack->correctParams(2);
int start = stack->pop()->getInt();
int length = stack->pop()->getInt();
// find end of string
if (length == 0 && start >= 0 && start < _size) {
for (int i = start; i < _size; i++) {
if (((char *)_buffer)[i] == '\0') {
length = i - start;
break;
}
}
}
if (!checkBounds(script, start, length)) {
stack->pushNULL();
} else {
char *str = new char[length + 1];
strncpy(str, (const char *)_buffer + start, length);
str[length] = '\0';
stack->pushString(str);
delete[] str;
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// GetPointer
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "GetPointer") == 0) {
stack->correctParams(1);
int start = stack->pop()->getInt();
if (!checkBounds(script, start, sizeof(void *))) {
stack->pushNULL();
} else {
void *pointer = *(void **)((byte *)_buffer + start);
SXMemBuffer *buf = new SXMemBuffer(_game, pointer);
stack->pushNative(buf, false);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// SetBool
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetBool") == 0) {
stack->correctParams(2);
int start = stack->pop()->getInt();
bool val = stack->pop()->getBool();
if (!checkBounds(script, start, sizeof(byte))) {
stack->pushBool(false);
} else {
*((byte *)_buffer + start) = (byte)val;
stack->pushBool(true);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// SetByte
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetByte") == 0) {
stack->correctParams(2);
int start = stack->pop()->getInt();
byte val = (byte)stack->pop()->getInt();
if (!checkBounds(script, start, sizeof(byte))) {
stack->pushBool(false);
} else {
*(byte *)((byte *)_buffer + start) = val;
stack->pushBool(true);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// SetShort
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetShort") == 0) {
stack->correctParams(2);
int start = stack->pop()->getInt();
short val = (short)stack->pop()->getInt();
if (!checkBounds(script, start, sizeof(short))) {
stack->pushBool(false);
} else {
*(short *)((byte *)_buffer + start) = val;
stack->pushBool(true);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// SetInt / SetLong
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetInt") == 0 || strcmp(name, "SetLong") == 0) {
stack->correctParams(2);
int start = stack->pop()->getInt();
int val = stack->pop()->getInt();
if (!checkBounds(script, start, sizeof(int))) {
stack->pushBool(false);
} else {
*(int *)((byte *)_buffer + start) = val;
stack->pushBool(true);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// SetFloat
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetFloat") == 0) {
stack->correctParams(2);
int start = stack->pop()->getInt();
float val = (float)stack->pop()->getFloat();
if (!checkBounds(script, start, sizeof(float))) {
stack->pushBool(false);
} else {
*(float *)((byte *)_buffer + start) = val;
stack->pushBool(true);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// SetDouble
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetDouble") == 0) {
stack->correctParams(2);
int start = stack->pop()->getInt();
double val = stack->pop()->getFloat();
if (!checkBounds(script, start, sizeof(double))) {
stack->pushBool(false);
} else {
*(double *)((byte *)_buffer + start) = val;
stack->pushBool(true);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// SetString
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetString") == 0) {
stack->correctParams(2);
int start = stack->pop()->getInt();
const char *val = stack->pop()->getString();
if (!checkBounds(script, start, strlen(val) + 1)) {
stack->pushBool(false);
} else {
memcpy((byte *)_buffer + start, val, strlen(val) + 1);
stack->pushBool(true);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// SetPointer
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "SetPointer") == 0) {
stack->correctParams(2);
int start = stack->pop()->getInt();
/* ScValue *val = */ stack->pop();
if (!checkBounds(script, start, sizeof(void *))) {
stack->pushBool(false);
} else {
/*
int pointer = (int)Val->getMemBuffer();
memcpy((byte *)_buffer+Start, &Pointer, sizeof(void*));
stack->pushBool(true);
*/
// TODO: fix
debug(3, "SXMemBuffer::ScCallMethod - SetPointer Bounds FIXME");
stack->pushBool(false);
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
// DEBUG_Dump
//////////////////////////////////////////////////////////////////////////
else if (strcmp(name, "DEBUG_Dump") == 0) {
stack->correctParams(0);
if (_buffer && _size) {
warning("SXMemBuffer::ScCallMethod - DEBUG_Dump");
Common::DumpFile f;
f.open("buffer.bin");
f.write(_buffer, _size);
f.close();
}
stack->pushNULL();
return STATUS_OK;
} else {
return STATUS_FAILED;
}
}
//////////////////////////////////////////////////////////////////////////
ScValue *SXMemBuffer::scGetProperty(const char *name) {
_scValue->setNULL();
//////////////////////////////////////////////////////////////////////////
// Type (RO)
//////////////////////////////////////////////////////////////////////////
if (strcmp(name, "Type") == 0) {
_scValue->setString("membuffer");
return _scValue;
}
//////////////////////////////////////////////////////////////////////////
// Size (RO)
//////////////////////////////////////////////////////////////////////////
if (strcmp(name, "Size") == 0) {
_scValue->setInt(_size);
return _scValue;
} else {
return BaseScriptable::scGetProperty(name);
}
}
//////////////////////////////////////////////////////////////////////////
bool SXMemBuffer::scSetProperty(const char *name, ScValue *value) {
/*
//////////////////////////////////////////////////////////////////////////
// Length
//////////////////////////////////////////////////////////////////////////
if (strcmp(name, "Length")==0) {
int origLength = _length;
_length = MAX(value->getInt(0), 0);
char propName[20];
if (_length < origLength) {
for(int i=_length; i < origLength; i++) {
Common::sprintf_s(propName, "%d", i);
_values->DeleteProp(propName);
}
}
return STATUS_OK;
}
else*/ return BaseScriptable::scSetProperty(name, value);
}
//////////////////////////////////////////////////////////////////////////
bool SXMemBuffer::persist(BasePersistenceManager *persistMgr) {
BaseScriptable::persist(persistMgr);
persistMgr->transferSint32(TMEMBER(_size));
if (persistMgr->getIsSaving()) {
if (_size > 0) {
persistMgr->putBytes((byte *)_buffer, _size);
}
} else {
if (_size > 0) {
_buffer = malloc(_size);
persistMgr->getBytes((byte *)_buffer, _size);
} else {
_buffer = nullptr;
}
}
return STATUS_OK;
}
//////////////////////////////////////////////////////////////////////////
int SXMemBuffer::scCompare(BaseScriptable *val) {
if (_buffer == val->scToMemBuffer()) {
return 0;
} else {
return 1;
}
}
} // End of namespace Wintermute