aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
Diffstat (limited to 'engines')
-rw-r--r--engines/sci/engine/seg_manager.cpp265
-rw-r--r--engines/sci/engine/seg_manager.h68
-rw-r--r--engines/sci/engine/segment.h2
3 files changed, 334 insertions, 1 deletions
diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp
index ab96d8b3fa..942c3f6299 100644
--- a/engines/sci/engine/seg_manager.cpp
+++ b/engines/sci/engine/seg_manager.cpp
@@ -836,7 +836,7 @@ SegmentRef SegManager::dereference(reg_t pointer) {
static void *_kernel_dereference_pointer(SegManager *segMan, reg_t pointer, int entries, bool wantRaw) {
SegmentRef ret = segMan->dereference(pointer);
- if (!ret.raw)
+ if (!ret.isValid())
return NULL;
if (ret.isRaw != wantRaw) {
@@ -872,6 +872,269 @@ char *SegManager::derefString(reg_t pointer, int entries) {
return (char *)_kernel_dereference_pointer(this, pointer, entries, true);
}
+// TODO: memcpy, strcpy and strncpy could maybe be folded into a single function
+void SegManager::strncpy(reg_t dest, const char* src, size_t n) {
+ SegmentRef dest_r = dereference(dest);
+ if (!dest_r.isValid()) {
+ warning("Attempt to strncpy to invalid pointer %04x:%04x", PRINT_REG(dest));
+ return;
+ }
+
+ if (dest_r.isRaw) {
+ // raw -> raw
+ if (n == 0xFFFFFFFFU)
+ ::strcpy((char*)dest_r.raw, src);
+ else
+ ::strncpy((char*)dest_r.raw, src, n);
+ } else {
+ // raw -> non-raw
+ reg_t* d = dest_r.reg;
+ while (n > 0) {
+ d->segment = 0; // STRINGFRAG_SEGMENT?
+ if (n > 1 && src[0]) {
+ d->offset = src[0] | (src[1] << 8);
+ } else {
+ d->offset &= 0xff00;
+ d->offset |= src[0];
+ break;
+ }
+
+ if (!src[1])
+ break;
+ src += 2;
+ n -= 2;
+ d++;
+ }
+ }
+}
+
+void SegManager::strncpy(reg_t dest, reg_t src, size_t n) {
+ SegmentRef dest_r = dereference(dest);
+ const SegmentRef src_r = dereference(src);
+ if (!src_r.isValid()) {
+ warning("Attempt to strncpy from invalid pointer %04x:%04x", PRINT_REG(src));
+ return;
+ }
+
+ if (!dest_r.isValid()) {
+ warning("Attempt to strncpy to invalid pointer %04x:%04x", PRINT_REG(dest));
+ return;
+ }
+
+
+ if (src_r.isRaw) {
+ // raw -> *
+ strncpy(dest, (const char*)src_r.raw, n);
+ } else if (dest_r.isRaw && !src_r.isRaw) {
+ // non-raw -> raw
+ const reg_t* s = src_r.reg;
+ char *d = (char*)dest_r.raw;
+ reg_t x;
+ while (n > 0) {
+ x = *s++;
+ *d++ = x.offset & 0x00ff;
+ if (n > 1 && x.offset & 0x00ff) {
+ *d++ = x.offset >> 8;
+ } else {
+ break;
+ }
+ if (!(x.offset >> 8))
+ break;
+ n -= 2;
+ }
+ } else {
+ // non-raw -> non-raw
+ const reg_t* s = src_r.reg;
+ reg_t* d = dest_r.reg;
+ reg_t x;
+ while (n > 0) {
+ x = *s++;
+ if (n > 1 && x.offset & 0x00ff) {
+ *d++ = x;
+ } else {
+ d->offset &= 0xff00;
+ d->offset |= x.offset & 0x00ff;
+ break;
+ }
+ if (!(x.offset & 0xff00))
+ break;
+ n -= 2;
+ }
+ }
+}
+
+void SegManager::strcpy(reg_t dest, const char* src) {
+ strncpy(dest, src, 0xFFFFFFFFU);
+}
+
+void SegManager::strcpy(reg_t dest, reg_t src) {
+ strncpy(dest, src, 0xFFFFFFFFU);
+}
+
+void SegManager::memcpy(reg_t dest, const byte* src, size_t n) {
+ SegmentRef dest_r = dereference(dest);
+ if (!dest_r.isValid()) {
+ warning("Attempt to memcpy to invalid pointer %04x:%04x", PRINT_REG(dest));
+ return;
+ }
+ if ((int)n > dest_r.maxSize) {
+ warning("Trying to dereference pointer %04x:%04x beyond end of segment", PRINT_REG(dest));
+ return;
+ }
+
+ if (dest_r.isRaw) {
+ // raw -> raw
+ ::memcpy((char*)dest_r.raw, src, n);
+ } else {
+ // raw -> non-raw
+ reg_t* d = dest_r.reg;
+ while (n > 0) {
+ d->segment = 0; // STRINGFRAG_SEGMENT?
+ if (n > 1) {
+ d->offset = src[0] | (src[1] << 8);
+ } else {
+ d->offset &= 0xff00;
+ d->offset |= src[0];
+ break;
+ }
+ src += 2;
+ n -= 2;
+ d++;
+ }
+ }
+}
+
+void SegManager::memcpy(reg_t dest, reg_t src, size_t n) {
+ SegmentRef dest_r = dereference(dest);
+ const SegmentRef src_r = dereference(src);
+ if (!dest_r.isValid()) {
+ warning("Attempt to memcpy to invalid pointer %04x:%04x", PRINT_REG(dest));
+ return;
+ }
+ if ((int)n > dest_r.maxSize) {
+ warning("Trying to dereference pointer %04x:%04x beyond end of segment", PRINT_REG(dest));
+ return;
+ }
+ if (!src_r.isValid()) {
+ warning("Attempt to memcpy from invalid pointer %04x:%04x", PRINT_REG(src));
+ return;
+ }
+ if ((int)n > src_r.maxSize) {
+ warning("Trying to dereference pointer %04x:%04x beyond end of segment", PRINT_REG(src));
+ return;
+ }
+
+ if (src_r.isRaw) {
+ // raw -> *
+ memcpy(dest, src_r.raw, n);
+ } else if (dest_r.isRaw) {
+ // * -> raw
+ memcpy(dest_r.raw, src, n);
+ } else {
+ // non-raw -> non-raw
+ const reg_t* s = src_r.reg;
+ reg_t* d = dest_r.reg;
+ reg_t x;
+ while (n > 0) {
+ x = *s++;
+ if (n > 1) {
+ *d++ = x;
+ } else {
+ d->offset &= 0xff00;
+ d->offset |= x.offset & 0x00ff;
+ break;
+ }
+ n -= 2;
+ }
+ }
+}
+
+void SegManager::memcpy(byte *dest, reg_t src, size_t n) {
+ const SegmentRef src_r = dereference(src);
+ if (!src_r.isValid()) {
+ warning("Attempt to memcpy from invalid pointer %04x:%04x", PRINT_REG(src));
+ return;
+ }
+ if ((int)n > src_r.maxSize) {
+ warning("Trying to dereference pointer %04x:%04x beyond end of segment", PRINT_REG(src));
+ return;
+ }
+
+ if (src_r.isRaw) {
+ // raw -> raw
+ ::memcpy(dest, src_r.raw, n);
+ } else {
+ // non-raw -> raw
+ const reg_t* s = src_r.reg;
+ reg_t x;
+ while (n > 0) {
+ x = *s++;
+ *dest++ = x.offset & 0x00ff;
+ if (n > 1) {
+ *dest++ = x.offset >> 8;
+ } else {
+ break;
+ }
+ n -= 2;
+ }
+ }
+}
+
+size_t SegManager::strlen(reg_t str) {
+ SegmentRef str_r = dereference(str);
+ if (!str_r.isValid()) {
+ warning("Attempt to call strlen on invalid pointer %04x:%04x", PRINT_REG(str));
+ return 0;
+ }
+
+ if (str_r.isRaw) {
+ return ::strlen((const char*)str_r.raw);
+ } else {
+ const reg_t* s = str_r.reg;
+ size_t n = 0;
+ while ((s->offset & 0x00ff) && (s->offset >> 8)) {
+ ++s;
+ n += 2;
+ }
+ if (s->offset & 0x00ff)
+ n++;
+ return n;
+ }
+}
+
+
+Common::String SegManager::getString(reg_t pointer, int entries)
+{
+ Common::String ret;
+ SegmentRef src_r = dereference(pointer);
+ if (!src_r.isValid()) {
+ warning("Attempt to dereference invalid pointer %04x:%04x", PRINT_REG(pointer));
+ return ret;
+ }
+ if (entries > src_r.maxSize) {
+ warning("Trying to dereference pointer %04x:%04x beyond end of segment", PRINT_REG(pointer));
+ return ret;
+ }
+ if (src_r.isRaw)
+ ret = (char*)src_r.raw;
+ else {
+ const reg_t* s = src_r.reg;
+ char c;
+ do {
+ c = s->offset & 0x00ff;
+ if (c) {
+ ret += c;
+ c = s->offset >> 8;
+ if (c) {
+ ret += c;
+ s++;
+ }
+ }
+ } while (c);
+ }
+ return ret;
+}
+
byte *SegManager::allocDynmem(int size, const char *descr, reg_t *addr) {
SegmentId seg;
diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h
index 7388fc5ec9..c8b99764bf 100644
--- a/engines/sci/engine/seg_manager.h
+++ b/engines/sci/engine/seg_manager.h
@@ -312,6 +312,74 @@ public:
char *derefString(reg_t pointer, int entries = 0);
/**
+ * Return the string referenced by pointer.
+ * pointer can point to either a raw or non-raw segment.
+ * @param pointer The pointer to dereference
+ * @parm entries The number of values expected (for checking)
+ * @return The string referenced, or an empty string if not enough
+ * entries were available.
+ */
+ Common::String getString(reg_t pointer, int entries = 0);
+
+
+ /**
+ * Copies a string from src to dest.
+ * src and dest can point to raw and non-raw segments.
+ * Conversion is performed as required.
+ */
+ void strcpy(reg_t dest, reg_t src);
+
+ /**
+ * Copies a string from src to dest.
+ * dest can point to a raw or non-raw segment.
+ * Conversion is performed as required.
+ */
+ void strcpy(reg_t dest, const char *src);
+
+ /**
+ * Copies a string from src to dest.
+ * src and dest can point to raw and non-raw segments.
+ * Conversion is performed as required. At most n characters are copied.
+ * TODO: determine if dest should always be null-terminated.
+ */
+ void strncpy(reg_t dest, reg_t src, size_t n);
+
+ /**
+ * Copies a string from src to dest.
+ * dest can point to a raw or non-raw segment.
+ * Conversion is performed as required. At most n characters are copied.
+ * TODO: determine if dest should always be null-terminated.
+ */
+ void strncpy(reg_t dest, const char *src, size_t n);
+
+ /**
+ * Copies n bytes of data from src to dest.
+ * src and dest can point to raw and non-raw segments.
+ * Conversion is performed as required.
+ */
+ void memcpy(reg_t dest, reg_t src, size_t n);
+
+ /**
+ * Copies n bytes of data from src to dest.
+ * dest can point to a raw or non-raw segment.
+ * Conversion is performed as required.
+ */
+ void memcpy(reg_t dest, const byte* src, size_t n);
+
+ /**
+ * Copies n bytes of data from src to dest.
+ * src can point to raw and non-raw segments.
+ * Conversion is performed as required.
+ */
+ void memcpy(byte *dest, reg_t src, size_t n);
+
+ /**
+ * Determine length of string at str.
+ * str can point to a raw or non-raw segment.
+ */
+ size_t strlen(reg_t str);
+
+ /**
* Finds a unique segment by type
* @param type The type of the segment to find
* @return The segment number, or -1 if the segment wasn't found
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index c912e4fc2a..a93aaa1f69 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -48,6 +48,8 @@ struct SegmentRef {
//SegmentType type;
SegmentRef() : isRaw(true), raw(0), maxSize(0) {}
+
+ bool isValid() const { return raw != 0; }
};