diff options
Diffstat (limited to 'engines')
-rw-r--r-- | engines/sci/engine/seg_manager.cpp | 265 | ||||
-rw-r--r-- | engines/sci/engine/seg_manager.h | 68 | ||||
-rw-r--r-- | engines/sci/engine/segment.h | 2 |
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; } }; |