GLK: FROTZ: Added remaining memory methods

This commit is contained in:
Paul Gilbert 2018-11-12 08:41:57 -08:00 committed by Paul Gilbert
parent 5507242395
commit e1de76b491
5 changed files with 217 additions and 1 deletions

View File

@ -320,6 +320,103 @@ void Mem::storew(zword addr, zword value) {
storeb((zword)(addr + 1), lo(value));
}
void Mem::free_undo(int count) {
undo_t *p;
if (count > undo_count)
count = undo_count;
while (count--) {
p = first_undo;
if (curr_undo == first_undo)
curr_undo = curr_undo->next;
first_undo = first_undo->next;
free(p);
undo_count--;
}
if (first_undo)
first_undo->prev = NULL;
else
last_undo = NULL;
}
void Mem::reset_memory() {
story_fp = nullptr;
blorb_ofs = 0;
blorb_len = 0;
if (undo_mem) {
free_undo(undo_count);
delete undo_mem;
}
undo_mem = nullptr;
undo_count = 0;
delete[] zmp;
zmp = nullptr;
}
long Mem::mem_diff(zbyte *a, zbyte *b, zword mem_size, zbyte *diff) {
unsigned size = mem_size;
zbyte *p = diff;
unsigned j;
zbyte c = 0;
for (;;) {
for (j = 0; size > 0 && (c = *a++ ^ *b++) == 0; j++)
size--;
if (size == 0) break;
size--;
if (j > 0x8000) {
*p++ = 0;
*p++ = 0xff;
*p++ = 0xff;
j -= 0x8000;
}
if (j > 0) {
*p++ = 0;
j--;
if (j <= 0x7f) {
*p++ = j;
} else {
*p++ = (j & 0x7f) | 0x80;
*p++ = (j & 0x7f80) >> 7;
}
}
*p++ = c;
*(b - 1) ^= c;
}
return p - diff;
}
void Mem::mem_undiff(zbyte *diff, long diff_length, zbyte *dest) {
zbyte c;
while (diff_length) {
c = *diff++;
diff_length--;
if (c == 0) {
unsigned runlen;
if (!diff_length)
return; // Incomplete run
runlen = *diff++;
diff_length--;
if (runlen & 0x80) {
if (!diff_length)
return; // Incomplete extended run
c = *diff++;
diff_length--;
runlen = (runlen & 0x7f) | (((unsigned)c) << 7);
}
dest += runlen + 1;
} else {
*dest++ ^= c;
}
}
}
} // End of namespace Scott
} // End of namespace Gargoyle

View File

@ -229,6 +229,11 @@ protected:
*/
void storew(zword addr, zword value);
/**
* Free count undo blocks from the beginning of the undo list
*/
void free_undo(int count);
/**
* Generates a runtime error
*/
@ -238,6 +243,26 @@ protected:
* Called when the flags are changed
*/
virtual void flagsChanged(zbyte value) = 0;
/**
* Close the story file and deallocate memory.
*/
void reset_memory();
/**
* Set diff to a Quetzal-like difference between a and b,
* copying a to b as we go. It is assumed that diff points to a
* buffer which is large enough to hold the diff.
* mem_size is the number of bytes to compare.
* Returns the number of bytes copied to diff.
*
*/
long mem_diff(zbyte *a, zbyte *b, zword mem_size, zbyte *diff);
/**
* Applies a quetzal-like diff to dest
*/
void mem_undiff(zbyte *diff, long diff_length, zbyte *dest);
public:
/**
* Constructor

View File

@ -219,6 +219,18 @@ private:
*/
virtual void flagsChanged(zbyte value) override;
/**
* This function does the dirty work for z_save_undo.
*/
int save_undo();
/**
* This function does the dirty work for z_restore_undo.
*/
int restore_undo();
/**@}*/
/**
* \defgroup Object support methods
* @{

View File

@ -26,7 +26,6 @@ namespace Gargoyle {
namespace Frotz {
// TODO: Implement method stubs
static void save_undo() {}
static zword os_read_mouse() { return 0; }

View File

@ -35,5 +35,88 @@ void Processor::flagsChanged(zbyte value) {
}
}
int Processor::save_undo() {
long diff_size;
zword stack_size;
undo_t *p;
if (_undo_slots == 0)
// undo feature unavailable
return -1;
// save undo possible
while (last_undo != curr_undo) {
p = last_undo;
last_undo = last_undo->prev;
delete p;
undo_count--;
}
if (last_undo)
last_undo->next = nullptr;
else
first_undo = nullptr;
if (undo_count == _undo_slots)
free_undo(1);
diff_size = mem_diff(zmp, prev_zmp, h_dynamic_size, undo_diff);
stack_size = _stack + STACK_SIZE - _sp;
do {
p = (undo_t *) malloc(sizeof(undo_t) + diff_size + stack_size * sizeof(*_sp));
if (p == nullptr)
free_undo(1);
} while (!p && undo_count);
if (p == nullptr)
return -1;
GET_PC(p->pc);
p->frame_count = _frameCount;
p->diff_size = diff_size;
p->stack_size = stack_size;
p->frame_offset = _fp - _stack;
memcpy(p + 1, undo_diff, diff_size);
memcpy((zbyte *)(p + 1) + diff_size, _sp, stack_size * sizeof(*_sp));
if (!first_undo) {
p->prev = nullptr;
first_undo = p;
} else {
last_undo->next = p;
p->prev = last_undo;
}
p->next = nullptr;
curr_undo = last_undo = p;
undo_count++;
return 1;
}
int Processor::restore_undo(void) {
if (_undo_slots == 0)
// undo feature unavailable
return -1;
if (curr_undo == nullptr)
// no saved game state
return 0;
// undo possible
memcpy(zmp, prev_zmp, h_dynamic_size);
SET_PC(curr_undo->pc);
_sp = _stack + STACK_SIZE - curr_undo->stack_size;
_fp = _stack + curr_undo->frame_offset;
_frameCount = curr_undo->frame_count;
mem_undiff((zbyte *)(curr_undo + 1), curr_undo->diff_size, prev_zmp);
memcpy(_sp, (zbyte *)(curr_undo + 1) + curr_undo->diff_size,
curr_undo->stack_size * sizeof(*_sp));
curr_undo = curr_undo->prev;
restart_header();
return 2;
}
} // End of namespace Scott
} // End of namespace Gargoyle