From e1de76b491e5145b837b55f7313b8fff07986572 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Mon, 12 Nov 2018 08:41:57 -0800 Subject: GLK: FROTZ: Added remaining memory methods --- engines/gargoyle/frotz/mem.cpp | 97 ++++++++++++++++++++++++++++++ engines/gargoyle/frotz/mem.h | 25 ++++++++ engines/gargoyle/frotz/processor.h | 12 ++++ engines/gargoyle/frotz/processor_input.cpp | 1 - engines/gargoyle/frotz/processor_mem.cpp | 83 +++++++++++++++++++++++++ 5 files changed, 217 insertions(+), 1 deletion(-) diff --git a/engines/gargoyle/frotz/mem.cpp b/engines/gargoyle/frotz/mem.cpp index 57b282531d..ad60b89bbb 100644 --- a/engines/gargoyle/frotz/mem.cpp +++ b/engines/gargoyle/frotz/mem.cpp @@ -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 diff --git a/engines/gargoyle/frotz/mem.h b/engines/gargoyle/frotz/mem.h index c128994df1..a45eec5e7f 100644 --- a/engines/gargoyle/frotz/mem.h +++ b/engines/gargoyle/frotz/mem.h @@ -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 diff --git a/engines/gargoyle/frotz/processor.h b/engines/gargoyle/frotz/processor.h index b70bf440a2..f9352ec19c 100644 --- a/engines/gargoyle/frotz/processor.h +++ b/engines/gargoyle/frotz/processor.h @@ -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 * @{ diff --git a/engines/gargoyle/frotz/processor_input.cpp b/engines/gargoyle/frotz/processor_input.cpp index b9c9604183..48951d2402 100644 --- a/engines/gargoyle/frotz/processor_input.cpp +++ b/engines/gargoyle/frotz/processor_input.cpp @@ -26,7 +26,6 @@ namespace Gargoyle { namespace Frotz { // TODO: Implement method stubs -static void save_undo() {} static zword os_read_mouse() { return 0; } diff --git a/engines/gargoyle/frotz/processor_mem.cpp b/engines/gargoyle/frotz/processor_mem.cpp index 1f3944ea17..71abe77c1f 100644 --- a/engines/gargoyle/frotz/processor_mem.cpp +++ b/engines/gargoyle/frotz/processor_mem.cpp @@ -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 -- cgit v1.2.3