scummvm/engine/lua/lsave.cpp
2008-08-03 13:02:45 +00:00

426 lines
12 KiB
C++

#include "engine/savegame.h"
#include "engine/engine.h"
#include "common/endian.h"
#include "common/debug.h"
#include "engine/lua/ltask.h"
#include "engine/lua/lauxlib.h"
#include "engine/lua/lmem.h"
#include "engine/lua/ldo.h"
#include "engine/lua/ltm.h"
#include "engine/lua/ltable.h"
#include "engine/lua/lvm.h"
#include "engine/lua/lopcodes.h"
#include "engine/lua/lstring.h"
#include "engine/lua/lua.h"
PointerId makeIdFromPointer(void *ptr) {
PointerId pointer;
#ifdef TARGET_64BITS
uint64 v = (uint64)ptr;
pointer.low = v & 0xffffffff;
pointer.hi = v >> 32;
#else
pointer.low = (uint32)ptr;
pointer.hi = 0;
#endif
return pointer;
}
void *makePointerFromId(PointerId ptr) {
void *pointer;
#ifdef TARGET_64BITS
uint64 v = ptr.low | ((uint64)ptr.hi << 32);
pointer = (void *)v;
#else
pointer = (void *)ptr.low;
#endif
return pointer;
}
SaveCallback saveCallbackPtr = NULL;
static void saveObjectValue(TObject *object, SaveSint32 saveSint32, SaveUint32 saveUint32) {
saveSint32(object->ttype);
switch (object->ttype) {
case LUA_T_CPROTO:
case LUA_T_CMARK:
{
luaL_libList *list = list_of_libs;
int32 idObj = 0;
while (list) {
for (int32 l = 0; l < list->number; l++) {
if (list->list[l].func == object->value.f) {
idObj = (idObj << 16) | l;
saveSint32(idObj);
saveSint32(0);
return;
}
}
list = list->next;
idObj++;
}
assert(0);
break;
}
case LUA_T_NUMBER:
case LUA_T_TASK:
{
byte *udata = (byte *)(&object->value.n);
uint32 v;
#if defined(SYSTEM_LITTLE_ENDIAN)
byte b[4];
b[0] = udata[3];
b[1] = udata[2];
b[2] = udata[1];
b[3] = udata[0];
v = *(uint32 *)b;
#else
memcpy(&v, udata, 4);
#endif
saveUint32(v);
saveUint32(0);
}
break;
case LUA_T_NIL:
{
saveUint32(0);
saveUint32(0);
}
break;
case LUA_T_ARRAY:
{
saveUint32(makeIdFromPointer(object->value.a).low);
saveUint32(makeIdFromPointer(object->value.a).hi);
}
break;
case LUA_T_USERDATA:
case LUA_T_STRING:
{
saveUint32(makeIdFromPointer(object->value.ts).low);
saveUint32(makeIdFromPointer(object->value.ts).hi);
}
break;
case LUA_T_PROTO:
case LUA_T_PMARK:
{
saveUint32(makeIdFromPointer(object->value.tf).low);
saveUint32(makeIdFromPointer(object->value.tf).hi);
}
break;
case LUA_T_CLOSURE:
case LUA_T_CLMARK:
{
saveUint32(makeIdFromPointer(object->value.cl).low);
saveUint32(makeIdFromPointer(object->value.cl).hi);
}
break;
case LUA_T_LINE:
{
saveSint32(object->value.i);
saveSint32(0);
}
break;
default:
if (object->ttype == MKID_BE('ACTR') || object->ttype == MKID_BE('COLR') || object->ttype == MKID_BE('STAT') || object->ttype == MKID_BE('FONT')
|| object->ttype == MKID_BE('VBUF') || object->ttype == MKID_BE('PRIM') || object->ttype == MKID_BE('TEXT')) {
saveUint32(makeIdFromPointer(object->value.ts).low);
saveUint32(makeIdFromPointer(object->value.ts).hi);
} else {
error("saveObjectValue: Unsupported object type");
}
}
}
static int32 opcodeSizeTable[] = {
1, 2, 1, 2, 1, 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1,
1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3,
1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1,
3, 2, 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1,
1, 1, 1, 3, 1, 2, 3, 2, 4, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 1, 1,
3, 2, 2, 2, 2, 3, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 3, 2, 1, 1,
1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2,
1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3,
2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 3, 2, 1, 1, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 2, 3, 2, 4, 2, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3, 2, 3,
2, 3, 2, 3, 2, 3, 2, 1, 1, 3, 2, 2, 2, 2, 3, 2, 1, 1
};
void lua_Save(SaveStream saveStream, SaveSint32 saveSint32, SaveUint32 saveUint32) {
printf("lua_Save() started.\n");
lua_collectgarbage(0);
int32 i, l;
int32 countElements = 0;
int32 maxStringLength = 0;
// Check for max length for strings and count them
for (i = 0; i < NUM_HASHS; i++) {
stringtable *tempStringTable = &L->string_root[i];
for (int32 l = 0; l < tempStringTable->size; l++) {
if (tempStringTable->hash[l] && tempStringTable->hash[l] != &EMPTY) {
countElements++;
if (tempStringTable->hash[l]->constindex != -1) {
if (maxStringLength < tempStringTable->hash[l]->u.s.len) {
maxStringLength = tempStringTable->hash[l]->u.s.len;
}
}
}
}
}
// save number of strings
saveSint32(countElements);
// save number of closures
countElements = 0;
GCnode *tempNode;
tempNode = L->rootcl.next;
while (tempNode) {
countElements++;
tempNode = tempNode->next;
}
saveSint32(countElements);
// save number of tables
countElements = 0;
tempNode = L->roottable.next;
while (tempNode) {
countElements++;
tempNode = tempNode->next;
}
saveSint32(countElements);
// save number of prototypes
countElements = 0;
tempNode = L->rootproto.next;
while (tempNode) {
countElements++;
tempNode = tempNode->next;
}
saveSint32(countElements);
// save number of global strings
countElements = 0;
tempNode = L->rootglobal.next;
while (tempNode) {
countElements++;
tempNode = tempNode->next;
}
saveSint32(countElements);
// save maximum length for string
saveSint32(maxStringLength);
//printf("1: %d\n", g_engine->_savedState->getBufferPos());
// save hash tables for strings and user data
TaggedString *tempString;
for (i = 0; i < NUM_HASHS; i++) {
stringtable *tempStringTable = &L->string_root[i];
for (l = 0; l < tempStringTable->size; l++) {
if (tempStringTable->hash[l] && tempStringTable->hash[l] != &EMPTY) {
tempString = tempStringTable->hash[l];
saveUint32(makeIdFromPointer(tempString).low);
saveUint32(makeIdFromPointer(tempString).hi);
saveSint32(tempString->constindex);
if (tempString->constindex != -1) {
saveObjectValue(&tempString->u.s.globalval, saveSint32, saveUint32);
saveSint32(tempString->u.s.len);
saveStream(tempString->str, tempString->u.s.len);
} else {
if (saveCallbackPtr) {
PointerId ptr = makeIdFromPointer(tempString->u.d.v);
ptr = saveCallbackPtr(tempString->u.d.tag, ptr, saveSint32);
tempString->u.d.v = makePointerFromId(ptr);
}
saveObjectValue((TObject *)&tempString->u.d, saveSint32, saveUint32);
}
}
}
}
//printf("2: %d\n", g_engine->_savedState->getBufferPos());
Closure *tempClosure = (Closure *)L->rootcl.next;
while (tempClosure) {
saveUint32(makeIdFromPointer(tempClosure).low);
saveUint32(makeIdFromPointer(tempClosure).hi);
saveSint32(tempClosure->nelems);
for (i = 0; i <= tempClosure->nelems; i++) {
saveObjectValue(&tempClosure->consts[i], saveSint32, saveUint32);
}
tempClosure = (Closure *)tempClosure->head.next;
}
Hash *tempHash = (Hash *)L->roottable.next;
while (tempHash) {
saveUint32(makeIdFromPointer(tempHash).low);
saveUint32(makeIdFromPointer(tempHash).hi);
saveSint32(tempHash->nhash);
int32 countUsedHash = 0;
for(i = 0; i < tempHash->nhash; i++) {
Node *newNode = &tempHash->node[i];
if (newNode->ref.ttype != LUA_T_NIL && newNode->val.ttype != LUA_T_NIL) {
countUsedHash++;
}
}
saveSint32(countUsedHash);
saveSint32(tempHash->htag);
for (i = 0; i < tempHash->nhash; i++) {
Node *newNode = &tempHash->node[i];
if (newNode->ref.ttype != LUA_T_NIL && newNode->val.ttype != LUA_T_NIL) {
saveObjectValue(&tempHash->node[i].ref, saveSint32, saveUint32);
saveObjectValue(&tempHash->node[i].val, saveSint32, saveUint32);
}
}
tempHash = (Hash *)tempHash->head.next;
}
TProtoFunc *tempProtoFunc = (TProtoFunc *)L->rootproto.next;
while (tempProtoFunc) {
saveUint32(makeIdFromPointer(tempProtoFunc).low);
saveUint32(makeIdFromPointer(tempProtoFunc).hi);
saveUint32(makeIdFromPointer(tempProtoFunc->fileName).low);
saveUint32(makeIdFromPointer(tempProtoFunc->fileName).hi);
saveSint32(tempProtoFunc->lineDefined);
saveSint32(tempProtoFunc->nconsts);
for (i = 0; i < tempProtoFunc->nconsts; i++) {
saveObjectValue(&tempProtoFunc->consts[i], saveSint32, saveUint32);
}
int32 countVariables = 0;
if (tempProtoFunc->locvars) {
for (; tempProtoFunc->locvars[countVariables++].line != -1;) { }
}
saveSint32(countVariables);
for (i = 0; i < countVariables; i++) {
saveUint32(makeIdFromPointer(tempProtoFunc->locvars[i].varname).low);
saveUint32(makeIdFromPointer(tempProtoFunc->locvars[i].varname).hi);
saveSint32(tempProtoFunc->locvars[i].line);
}
byte *codePtr = tempProtoFunc->code + 2;
byte *tmpPtr = codePtr;
int32 opcodeId;
do {
opcodeId = *tmpPtr;
tmpPtr += opcodeSizeTable[opcodeId];
} while (opcodeId != ENDCODE);
int32 codeSize = (tmpPtr - codePtr) + 2;
saveSint32(codeSize);
saveStream(tempProtoFunc->code, codeSize);
tempProtoFunc = (TProtoFunc *)tempProtoFunc->head.next;
}
tempString = (TaggedString *)L->rootglobal.next;
while (tempString) {
saveUint32(makeIdFromPointer(tempString).low);
saveUint32(makeIdFromPointer(tempString).hi);
tempString = (TaggedString *)tempString->head.next;
}
saveObjectValue(&L->errorim, saveSint32, saveUint32);
IM *tempIm = L->IMtable;
saveSint32(L->IMtable_size);
for (i = 0; i < L->IMtable_size; i++) {
for (l = 0; l < IM_N; l++) {
saveObjectValue(&tempIm->int_method[l], saveSint32, saveUint32);
}
tempIm++;
}
saveSint32(L->last_tag);
saveSint32(L->refSize);
for (i = 0 ; i < L->refSize; i++) {
saveObjectValue(&L->refArray[i].o, saveSint32, saveUint32);
saveSint32(L->refArray[i].status);
}
saveSint32(L->GCthreshold);
saveSint32(L->nblocks);
saveSint32(L->Mbuffsize);
saveStream(L->Mbuffer, L->Mbuffsize);
int32 MbaseOffset = L->Mbuffbase - L->Mbuffer;
saveSint32(MbaseOffset);
saveSint32(L->Mbuffnext);
saveSint32(globalTaskSerialId);
int32 countTasks = 0;
lua_Task *tempTask = L->root_task->next;
while (tempTask) {
countTasks++;
tempTask = tempTask->next;
}
saveSint32(countTasks);
tempTask = L->root_task->next;
while (tempTask) {
int32 stackLastSize = (tempTask->stack.last - tempTask->stack.stack) + 1;
saveSint32(stackLastSize);
int32 stackTopSize = tempTask->stack.top - tempTask->stack.stack;
saveSint32(stackTopSize);
for (i = 0; i < stackTopSize; i++) {
saveObjectValue(&tempTask->stack.stack[i], saveSint32, saveUint32);
}
saveSint32(tempTask->Cstack.base);
saveSint32(tempTask->Cstack.lua2C);
saveSint32(tempTask->Cstack.num);
saveSint32(tempTask->numCblocks);
for (i = 0; i < tempTask->numCblocks; i++) {
saveSint32(tempTask->Cblocks[i].base);
saveSint32(tempTask->Cblocks[i].lua2C);
saveSint32(tempTask->Cblocks[i].num);
}
int32 pcOffset, taskCi = -1;
saveSint32(tempTask->base_ci_size);
assert(tempTask->base_ci);
CallInfo *tempCi = tempTask->base_ci;
int32 countCi = tempTask->base_ci_size / sizeof(CallInfo);
for (i = 0; i < countCi; i++) {
saveUint32(makeIdFromPointer(tempCi->c).low);
saveUint32(makeIdFromPointer(tempCi->c).hi);
saveUint32(makeIdFromPointer(tempCi->tf).low);
saveUint32(makeIdFromPointer(tempCi->tf).hi);
if (tempCi->pc && tempTask->ci->tf)
pcOffset = tempCi->pc - tempCi->tf->code;
else
pcOffset = 0;
saveSint32(pcOffset);
saveSint32(tempCi->base);
saveSint32(tempCi->nResults);
if (tempCi == tempTask->ci)
taskCi = i;
tempCi++;
}
assert(taskCi != -1);
saveSint32(taskCi);
MbaseOffset = tempTask->Mbuffbase - tempTask->Mbuffer;
saveSint32(MbaseOffset);
saveSint32(tempTask->Mbuffnext);
saveSint32(tempTask->Tstate);
saveSint32(tempTask->id);
tempTask = tempTask->next;
}
printf("lua_Save() finished.\n");
}