From d3c19163847de0dbfc28972b91f43a7b4c0ded7f Mon Sep 17 00:00:00 2001 From: Willem Jan Palenstijn Date: Sun, 27 Sep 2009 01:50:26 +0000 Subject: SCI: Major string handling update. All string access to segments should now work with both raw and non-raw (reg_t) segments, using the new utility functions in segMan. There will likely be regressions. svn-id: r44388 --- engines/sci/engine/kernel.h | 4 +- engines/sci/engine/kfile.cpp | 177 ++++++++++++++++--------------- engines/sci/engine/kgraphics.cpp | 130 ++++++++++++++--------- engines/sci/engine/kmenu.cpp | 7 +- engines/sci/engine/kmisc.cpp | 38 +++---- engines/sci/engine/kpathing.cpp | 69 +++++------- engines/sci/engine/kstring.cpp | 197 ++++++++++++----------------------- engines/sci/gfx/gfx_gui.cpp | 2 +- engines/sci/gfx/gfx_state_internal.h | 2 +- engines/sci/gfx/gfx_widgets.cpp | 1 - engines/sci/gfx/menubar.cpp | 24 +++-- engines/sci/gfx/menubar.h | 2 +- 12 files changed, 291 insertions(+), 362 deletions(-) (limited to 'engines') diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 79da7032e0..9d20a99b58 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -216,9 +216,9 @@ int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvoc * @param s The current state * @param address The address to look up * @param index The relative index - * @return The referenced text, or NULL on error. + * @return The referenced text, or empty string on error. */ -char *kernel_lookup_text(EngineState *s, reg_t address, int index); +Common::String kernel_lookup_text(EngineState *s, reg_t address, int index); /******************** Priority macros/functions ********************/ /** diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index 372e7b8aa9..f4b54765f4 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -197,11 +197,11 @@ void file_open(EngineState *s, const char *filename, int mode) { } reg_t kFOpen(EngineState *s, int, int argc, reg_t *argv) { - char *name = s->segMan->derefString(argv[0]); + Common::String name = s->segMan->getString(argv[0]); int mode = argv[1].toUint16(); - debug(3, "kFOpen(%s,0x%x)", name, mode); - file_open(s, name, mode); + debug(3, "kFOpen(%s,0x%x)", name.c_str(), mode); + file_open(s, name.c_str(), mode); return s->r_acc; } @@ -250,9 +250,9 @@ void fwrite_wrapper(EngineState *s, int handle, const char *data, int length) { reg_t kFPuts(EngineState *s, int, int argc, reg_t *argv) { int handle = argv[0].toUint16(); - char *data = s->segMan->derefString(argv[1]); + Common::String data = s->segMan->getString(argv[1]); - fwrite_wrapper(s, handle, data, strlen(data)); + fwrite_wrapper(s, handle, data.c_str(), data.size()); return s->r_acc; } @@ -307,12 +307,13 @@ static void fseek_wrapper(EngineState *s, int handle, int offset, int whence) { } reg_t kFGets(EngineState *s, int, int argc, reg_t *argv) { - char *dest = s->segMan->derefString(argv[0]); int maxsize = argv[1].toUint16(); + char *buf = new char[maxsize]; int handle = argv[2].toUint16(); debug(3, "kFGets(%d,%d)", handle, maxsize); - fgets_wrapper(s, dest, maxsize, handle); + fgets_wrapper(s, buf, maxsize, handle); + s->segMan->memcpy(argv[0], (const byte*)buf, maxsize); return argv[0]; } @@ -320,14 +321,12 @@ reg_t kFGets(EngineState *s, int, int argc, reg_t *argv) { * Writes the cwd to the supplied address and returns the address in acc. */ reg_t kGetCWD(EngineState *s, int, int argc, reg_t *argv) { - char *targetaddr = s->segMan->derefString(argv[0]); - // We do not let the scripts see the file system, instead pretending // we are always in the same directory. // TODO/FIXME: Is "/" a good value? Maybe "" or "." or "C:\" are better? - strcpy(targetaddr, "/"); + s->segMan->strcpy(argv[0], "/"); - debug(3, "kGetCWD() -> %s", targetaddr); + debug(3, "kGetCWD() -> %s", "/"); return argv[0]; } @@ -352,58 +351,50 @@ enum { reg_t kDeviceInfo(EngineState *s, int, int argc, reg_t *argv) { int mode = argv[0].toUint16(); - char *game_prefix, *input_s, *output_s; switch (mode) { - case K_DEVICE_INFO_GET_DEVICE: - input_s = s->segMan->derefString(argv[1]); - output_s = s->segMan->derefString(argv[2]); - assert(input_s != output_s); + case K_DEVICE_INFO_GET_DEVICE: { + Common::String input_str = s->segMan->getString(argv[1]); - strcpy(output_s, "/"); - debug(3, "K_DEVICE_INFO_GET_DEVICE(%s) -> %s", input_s, output_s); + s->segMan->strcpy(argv[2], "/"); + debug(3, "K_DEVICE_INFO_GET_DEVICE(%s) -> %s", input_str.c_str(), "/"); break; - + } case K_DEVICE_INFO_GET_CURRENT_DEVICE: - output_s = s->segMan->derefString(argv[1]); - - strcpy(output_s, "/"); - debug(3, "K_DEVICE_INFO_GET_CURRENT_DEVICE() -> %s", output_s); + s->segMan->strcpy(argv[1], "/"); + debug(3, "K_DEVICE_INFO_GET_CURRENT_DEVICE() -> %s", "/"); break; case K_DEVICE_INFO_PATHS_EQUAL: { - char *path1_s = s->segMan->derefString(argv[1]); - char *path2_s = s->segMan->derefString(argv[2]); - debug(3, "K_DEVICE_INFO_PATHS_EQUAL(%s,%s)", path1_s, path2_s); + Common::String path1_s = s->segMan->getString(argv[1]); + Common::String path2_s = s->segMan->getString(argv[2]); + debug(3, "K_DEVICE_INFO_PATHS_EQUAL(%s,%s)", path1_s.c_str(), path2_s.c_str()); - return make_reg(0, Common::matchString(path2_s, path1_s, false, true)); + return make_reg(0, Common::matchString(path2_s.c_str(), path1_s.c_str(), false, true)); } break; - case K_DEVICE_INFO_IS_FLOPPY: - input_s = s->segMan->derefString(argv[1]); - debug(3, "K_DEVICE_INFO_IS_FLOPPY(%s)", input_s); + case K_DEVICE_INFO_IS_FLOPPY: { + Common::String input_str = s->segMan->getString(argv[1]); + debug(3, "K_DEVICE_INFO_IS_FLOPPY(%s)", input_str.c_str()); return NULL_REG; /* Never */ - + } /* SCI uses these in a less-than-portable way to delete savegames. ** Read http://www-plan.cs.colorado.edu/creichen/freesci-logs/2005.10/log20051019.html ** for more information on our workaround for this. */ case K_DEVICE_INFO_GET_SAVECAT_NAME: { - output_s = s->segMan->derefString(argv[1]); - game_prefix = s->segMan->derefString(argv[2]); - - sprintf(output_s, "__throwaway"); - debug(3, "K_DEVICE_INFO_GET_SAVECAT_NAME(%s) -> %s", game_prefix, output_s); + Common::String game_prefix = s->segMan->getString(argv[2]); + s->segMan->strcpy(argv[1], "__throwaway"); + debug(3, "K_DEVICE_INFO_GET_SAVECAT_NAME(%s) -> %s", game_prefix.c_str(), "__throwaway"); } break; case K_DEVICE_INFO_GET_SAVEFILE_NAME: { - output_s = s->segMan->derefString(argv[1]); - game_prefix = s->segMan->derefString(argv[2]); + Common::String game_prefix = s->segMan->getString(argv[2]); int savegame_id = argv[3].toUint16(); - sprintf(output_s, "__throwaway"); - debug(3, "K_DEVICE_INFO_GET_SAVEFILE_NAME(%s,%d) -> %s", game_prefix, savegame_id, output_s); + s->segMan->strcpy(argv[1], "__throwaway"); + debug(3, "K_DEVICE_INFO_GET_SAVEFILE_NAME(%s,%d) -> %s", game_prefix.c_str(), savegame_id, "__throwaway"); delete_savegame(s, savegame_id); } break; @@ -428,9 +419,9 @@ reg_t kGetSaveDir(EngineState *s, int, int argc, reg_t *argv) { } reg_t kCheckFreeSpace(EngineState *s, int, int argc, reg_t *argv) { - char *path = s->segMan->derefString(argv[0]); + Common::String path = s->segMan->getString(argv[0]); - debug(3, "kCheckFreeSpace(%s)", path); + debug(3, "kCheckFreeSpace(%s)", path.c_str()); // We simply always pretend that there is enough space. // The alternative would be to write a big test file, which is not nice // on systems where doing so is very slow. @@ -486,10 +477,10 @@ void listSavegames(Common::Array &saves) { } reg_t kCheckSaveGame(EngineState *s, int, int argc, reg_t *argv) { - char *game_id = s->segMan->derefString(argv[0]); + Common::String game_id = s->segMan->getString(argv[0]); int savedir_nr = argv[1].toUint16(); - debug(3, "kCheckSaveGame(%s, %d)", game_id, savedir_nr); + debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), savedir_nr); Common::Array saves; listSavegames(saves); @@ -522,12 +513,11 @@ reg_t kCheckSaveGame(EngineState *s, int, int argc, reg_t *argv) { } reg_t kGetSaveFiles(EngineState *s, int, int argc, reg_t *argv) { - char *game_id = s->segMan->derefString(argv[0]); - char *nametarget = s->segMan->derefString(argv[1]); - reg_t nametarget_base = argv[1]; + Common::String game_id = s->segMan->getString(argv[0]); + reg_t nametarget = argv[1]; reg_t *nameoffsets = s->segMan->derefRegPtr(argv[2], 0); - debug(3, "kGetSaveFiles(%s,%s)", game_id, nametarget); + debug(3, "kGetSaveFiles(%s)", game_id.c_str()); Common::Array saves; listSavegames(saves); @@ -556,29 +546,34 @@ reg_t kGetSaveFiles(EngineState *s, int, int argc, reg_t *argv) { ++s->r_acc.offset; // Increase number of files found nameoffsets++; // Make sure the next ID string address is written to the next pointer - strncpy(nametarget, meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH); // Copy identifier string - *(nametarget + SCI_MAX_SAVENAME_LENGTH - 1) = 0; // Make sure it's terminated - nametarget += SCI_MAX_SAVENAME_LENGTH; // Increase name offset pointer accordingly - nametarget_base.offset += SCI_MAX_SAVENAME_LENGTH; + Common::String name = meta.savegame_name; + if (name.size() > SCI_MAX_SAVENAME_LENGTH-1) + name = Common::String(meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH-1); + s->segMan->strcpy(nametarget, name.c_str()); + + // Increase name offset pointer accordingly + nametarget.offset += SCI_MAX_SAVENAME_LENGTH; } delete in; } } //free(gfname); - *nametarget = 0; // Terminate list + s->segMan->strcpy(nametarget, ""); // Terminate list return s->r_acc; } reg_t kSaveGame(EngineState *s, int, int argc, reg_t *argv) { - char *game_id = s->segMan->derefString(argv[0]); + Common::String game_id = s->segMan->getString(argv[0]); int savedir_nr = argv[1].toUint16(); int savedir_id; // Savegame ID, derived from savedir_nr and the savegame ID list - char *game_description = s->segMan->derefString(argv[2]); - char *version = argc > 3 ? strdup(s->segMan->derefString(argv[3])) : NULL; + Common::String game_description = s->segMan->getString(argv[2]); + Common::String version; + if (argc > 3) + version = s->segMan->getString(argv[3]); - debug(3, "kSaveGame(%s,%d,%s,%s)", game_id, savedir_nr, game_description, version); + debug(3, "kSaveGame(%s,%d,%s,%s)", game_id.c_str(), savedir_nr, game_description.c_str(), version.c_str()); Common::Array saves; listSavegames(saves); @@ -623,7 +618,7 @@ reg_t kSaveGame(EngineState *s, int, int argc, reg_t *argv) { return NULL_REG; } - if (gamestate_save(s, out, game_description, version)) { + if (gamestate_save(s, out, game_description.c_str(), version.c_str())) { warning("Saving the game failed."); s->r_acc = NULL_REG; } else { @@ -642,10 +637,10 @@ reg_t kSaveGame(EngineState *s, int, int argc, reg_t *argv) { } reg_t kRestoreGame(EngineState *s, int, int argc, reg_t *argv) { - char *game_id = s->segMan->derefString(argv[0]); + Common::String game_id = s->segMan->getString(argv[0]); int savedir_nr = argv[1].toUint16(); - debug(3, "kRestoreGame(%s,%d)", game_id, savedir_nr); + debug(3, "kRestoreGame(%s,%d)", game_id.c_str(), savedir_nr); Common::Array saves; listSavegames(saves); @@ -668,7 +663,7 @@ reg_t kRestoreGame(EngineState *s, int, int argc, reg_t *argv) { shrink_execution_stack(s, s->execution_stack_base + 1); } else { s->r_acc = make_reg(0, 1); - warning("Restoring failed (game_id = '%s')", game_id); + warning("Restoring failed (game_id = '%s')", game_id.c_str()); } return s->r_acc; } @@ -681,12 +676,12 @@ reg_t kRestoreGame(EngineState *s, int, int argc, reg_t *argv) { } reg_t kValidPath(EngineState *s, int, int argc, reg_t *argv) { - const char *path = s->segMan->derefString(argv[0]); + Common::String path = s->segMan->getString(argv[0]); // FIXME: For now, we only accept the (fake) root dir "/" as a valid path. - s->r_acc = make_reg(0, 0 == strcmp(path, "/")); + s->r_acc = make_reg(0, path == "/"); - debug(3, "kValidPath(%s) -> %d", path, s->r_acc.offset); + debug(3, "kValidPath(%s) -> %d", path.c_str(), s->r_acc.offset); return s->r_acc; } @@ -732,14 +727,12 @@ void DirSeeker::nextFile() { return; } - char *mem = _vm->segMan->derefString(_outbuffer); - memset(mem, 0, 13); - // TODO: Transform the string back into a format usable by the SCI scripts. // I.e., strip any TARGET- prefix. - const char *string = _iter->c_str(); - assert(string); - strncpy(mem, string, 12); + Common::String string = *_iter; + if (string.size() > 12) + string = Common::String(string.c_str(), 12); + _vm->segMan->strcpy(_outbuffer, string.c_str()); // Return the result and advance the list iterator :) _vm->r_acc = _outbuffer; @@ -753,11 +746,11 @@ reg_t kFileIO(EngineState *s, int, int argc, reg_t *argv) { switch (func_nr) { case K_FILEIO_OPEN : { - char *name = s->segMan->derefString(argv[1]); + Common::String name = s->segMan->getString(argv[1]); int mode = argv[2].toUint16(); - file_open(s, name, mode); - debug(3, "K_FILEIO_OPEN(%s,0x%x)", name, mode); + file_open(s, name.c_str(), mode); + debug(3, "K_FILEIO_OPEN(%s,0x%x)", name.c_str(), mode); break; } case K_FILEIO_CLOSE : { @@ -769,25 +762,29 @@ reg_t kFileIO(EngineState *s, int, int argc, reg_t *argv) { } case K_FILEIO_READ_RAW : { int handle = argv[1].toUint16(); - char *dest = s->segMan->derefString(argv[2]); int size = argv[3].toUint16(); + char *buf = new char[size]; debug(3, "K_FILEIO_READ_RAW(%d,%d)", handle, size); - fread_wrapper(s, dest, size, handle); + fread_wrapper(s, buf, size, handle); + s->segMan->memcpy(argv[2], (const byte*)buf, size); + delete[] buf; break; } case K_FILEIO_WRITE_RAW : { int handle = argv[1].toUint16(); - char *buf = s->segMan->derefString(argv[2]); int size = argv[3].toUint16(); + char *buf = new char[size]; + s->segMan->memcpy((byte*)buf, argv[2], size); debug(3, "K_FILEIO_WRITE_RAW(%d,%d)", handle, size); fwrite_wrapper(s, handle, buf, size); + delete[] buf; break; } case K_FILEIO_UNLINK : { - char *name = s->segMan->derefString(argv[1]); - debug(3, "K_FILEIO_UNLINK(%s)", name); + Common::String name = s->segMan->getString(argv[1]); + debug(3, "K_FILEIO_UNLINK(%s)", name.c_str()); Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); const Common::String wrappedName = ((Sci::SciEngine*)g_engine)->wrapFilename(name); @@ -797,25 +794,27 @@ reg_t kFileIO(EngineState *s, int, int argc, reg_t *argv) { break; } case K_FILEIO_READ_STRING : { - char *dest = s->segMan->derefString(argv[1]); int size = argv[2].toUint16(); + char *buf = new char[size]; int handle = argv[3].toUint16(); debug(3, "K_FILEIO_READ_STRING(%d,%d)", handle, size); - fgets_wrapper(s, dest, size, handle); + fgets_wrapper(s, buf, size, handle); + s->segMan->memcpy(argv[1], (const byte*)buf, size); + delete[] buf; return argv[1]; } case K_FILEIO_WRITE_STRING : { int handle = argv[1].toUint16(); int size = argv[3].toUint16(); - char *buf = s->segMan->derefString(argv[2]); + Common::String str = s->segMan->getString(argv[2]); debug(3, "K_FILEIO_WRITE_STRING(%d,%d)", handle, size); // CHECKME: Is the size parameter used at all? // In the LSL5 password protection it is zero, and we should // then write a full string. (Not sure if it should write the // terminating zero.) - fwrite_wrapper(s, handle, buf, strlen(buf)); + fwrite_wrapper(s, handle, str.c_str(), str.size()); break; } case K_FILEIO_SEEK : { @@ -828,16 +827,16 @@ reg_t kFileIO(EngineState *s, int, int argc, reg_t *argv) { break; } case K_FILEIO_FIND_FIRST : { - char *mask = s->segMan->derefString(argv[1]); + Common::String mask = s->segMan->getString(argv[1]); reg_t buf = argv[2]; int attr = argv[3].toUint16(); // We won't use this, Win32 might, though... - debug(3, "K_FILEIO_FIND_FIRST(%s,0x%x)", mask, attr); + debug(3, "K_FILEIO_FIND_FIRST(%s,0x%x)", mask.c_str(), attr); #ifndef WIN32 - if (strcmp(mask, "*.*") == 0) - strcpy(mask, "*"); // For UNIX + if (mask == "*.*") + mask = "*"; // For UNIX #endif - s->_dirseeker.firstFile(mask, buf); + s->_dirseeker.firstFile(mask.c_str(), buf); break; } @@ -847,7 +846,7 @@ reg_t kFileIO(EngineState *s, int, int argc, reg_t *argv) { break; } case K_FILEIO_FILE_EXISTS : { - char *name = s->segMan->derefString(argv[1]); + Common::String name = s->segMan->getString(argv[1]); // Check for regular file bool exists = Common::File::exists(name); @@ -859,7 +858,7 @@ reg_t kFileIO(EngineState *s, int, int argc, reg_t *argv) { exists = !saveFileMan->listSavefiles(name).empty(); } - debug(3, "K_FILEIO_FILE_EXISTS(%s) -> %d", name, exists); + debug(3, "K_FILEIO_FILE_EXISTS(%s) -> %d", name.c_str(), exists); return make_reg(0, exists); } default : diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index fc72e6e3f1..b57fc32dcf 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -445,7 +445,7 @@ void _k_graph_rebuild_port_with_color(EngineState *s, gfx_color_t newbgcolor) { newport = sciw_new_window(s, port->zone, port->_font, port->_color, newbgcolor, s->titlebar_port->_font, s->ega_colors[15], s->ega_colors[8], - port->title_text, port->port_flags & ~kWindowTransparent); + port->_title_text.c_str(), port->port_flags & ~kWindowTransparent); if (s->dyn_views) { int found = 0; @@ -602,29 +602,32 @@ reg_t kGraph(EngineState *s, int, int argc, reg_t *argv) { reg_t kTextSize(EngineState *s, int, int argc, reg_t *argv) { int width, height; - char *text = argv[1].segment ? s->segMan->derefString(argv[1]) : NULL; - const char *sep = NULL; + Common::String text = s->segMan->getString(argv[1]); reg_t *dest = s->segMan->derefRegPtr(argv[0], 4); int maxwidth = (argc > 3) ? argv[3].toUint16() : 0; int font_nr = argv[2].toUint16(); - if ((argc > 4) && (argv[4].segment)) - sep = s->segMan->derefString(argv[4]); + Common::String sep_str; + const char *sep = NULL; + if ((argc > 4) && (argv[4].segment)) { + sep_str = s->segMan->getString(argv[4]); + sep = sep_str.c_str(); + } if (maxwidth < 0) maxwidth = 0; dest[0] = dest[1] = NULL_REG; - if (!text || !*text || !dest) { // Empty text + if (text.empty() || !dest) { // Empty text dest[2] = dest[3] = make_reg(0, 0); debugC(2, kDebugLevelStrings, "GetTextSize: Empty string\n"); return s->r_acc; } - gfxop_get_text_params(s->gfx_state, font_nr, s->strSplit(text, sep).c_str(), maxwidth ? maxwidth : MAX_TEXT_WIDTH_MAGIC_VALUE, + gfxop_get_text_params(s->gfx_state, font_nr, s->strSplit(text.c_str(), sep).c_str(), maxwidth ? maxwidth : MAX_TEXT_WIDTH_MAGIC_VALUE, &width, &height, 0, NULL, NULL, NULL); - debugC(2, kDebugLevelStrings, "GetTextSize '%s' -> %dx%d\n", text, width, height); + debugC(2, kDebugLevelStrings, "GetTextSize '%s' -> %dx%d\n", text.c_str(), width, height); dest[2] = make_reg(0, height); // dest[3] = make_reg(0, maxwidth? maxwidth : width); @@ -1303,7 +1306,9 @@ static void _k_draw_control(EngineState *s, reg_t obj, int inverse); static void disableCertainButtons(SegManager *segMan, Common::String gameName, reg_t obj) { reg_t text_pos = GET_SEL32(obj, text); - char *text = text_pos.isNull() ? NULL : segMan->derefString(text_pos); + Common::String text; + if (!text_pos.isNull()) + text = segMan->getString(text_pos); int type = GET_SEL32V(obj, type); int state = GET_SEL32V(obj, state); @@ -1327,15 +1332,15 @@ static void disableCertainButtons(SegManager *segMan, Common::String gameName, r * that game - bringing the save/load dialog on a par with SCI0. */ // NOTE: This _only_ works with the English version - if (type == K_CONTROL_BUTTON && text && (gameName == "sq4") && - getSciVersion() < SCI_VERSION_1_1 && !strcmp(text, " Delete ")) { + if (type == K_CONTROL_BUTTON && (gameName == "sq4") && + getSciVersion() < SCI_VERSION_1_1 && text == " Delete ") { PUT_SEL32V(obj, state, (state | kControlStateDisabled) & ~kControlStateEnabled); } // Disable the "Change Directory" button, as we don't allow the game engine to // change the directory where saved games are placed // NOTE: This _only_ works with the English version - if (type == K_CONTROL_BUTTON && text && !strcmp(text, "Change\r\nDirectory")) { + if (type == K_CONTROL_BUTTON && text == "Change\r\nDirectory") { PUT_SEL32V(obj, state, (state | kControlStateDisabled) & ~kControlStateEnabled); } } @@ -1369,13 +1374,13 @@ void update_cursor_limits(int *display_offset, int *cursor, int max_displayed) { #define _K_EDIT_DELETE \ if (cursor < textlen) { \ - memmove(text + cursor, text + cursor + 1, textlen - cursor +1); \ + text.deleteChar(cursor); \ } #define _K_EDIT_BACKSPACE \ if (cursor) { \ --cursor; \ - memmove(text + cursor, text + cursor + 1, textlen - cursor +1); \ + text.deleteChar(cursor); \ --textlen; \ } @@ -1401,15 +1406,17 @@ reg_t kEditControl(EngineState *s, int, int argc, reg_t *argv) { reg_t text_pos = GET_SEL32(obj, text); int display_offset = 0; - char *text = s->segMan->derefString(text_pos); + Common::String text = s->segMan->getString(text_pos); int textlen; +#if 0 if (!text) { warning("Could not draw control: %04x:%04x does not reference text", PRINT_REG(text_pos)); return s->r_acc; } +#endif - textlen = strlen(text); + textlen = text.size(); cursor += display_offset; @@ -1432,7 +1439,7 @@ reg_t kEditControl(EngineState *s, int, int argc, reg_t *argv) { if (cursor > 0) --cursor; break; case 'k': - text[cursor] = 0; + text = Common::String(text.c_str(), cursor); break; // Terminate string case 'h': _K_EDIT_BACKSPACE; @@ -1503,20 +1510,20 @@ reg_t kEditControl(EngineState *s, int, int argc, reg_t *argv) { if (cursor == textlen) { if (textlen < max) { - text[cursor++] = key; - text[cursor] = 0; // Terminate string + text += key; + cursor++; } } else if (inserting) { if (textlen < max) { int i; for (i = textlen + 2; i >= cursor; i--) - text[i] = text[i - 1]; - text[cursor++] = key; + text.setChar(text[i - 1], i); + text.setChar(key, cursor++); } } else { // Overwriting - text[cursor++] = key; + text.setChar(key, cursor++); } if (max_displayed < max) @@ -1528,6 +1535,7 @@ reg_t kEditControl(EngineState *s, int, int argc, reg_t *argv) { } PUT_SEL32V(obj, cursor, cursor); // Write back cursor position + s->segMan->strcpy(text_pos, text.c_str()); // Write back string } case K_CONTROL_ICON: @@ -1564,7 +1572,9 @@ static void _k_draw_control(EngineState *s, reg_t obj, int inverse) { int font_nr = GET_SEL32V(obj, font); reg_t text_pos = GET_SEL32(obj, text); - const char *text = text_pos.isNull() ? NULL : s->segMan->derefString(text_pos); + Common::String text; + if (!text_pos.isNull()) + text = s->segMan->getString(text_pos); int view = GET_SEL32V(obj, view); int cel = sign_extend_byte(GET_SEL32V(obj, cel)); int loop = sign_extend_byte(GET_SEL32V(obj, loop)); @@ -1578,7 +1588,7 @@ static void _k_draw_control(EngineState *s, reg_t obj, int inverse) { switch (type) { case K_CONTROL_BUTTON: debugC(2, kDebugLevelGraphics, "drawing button %04x:%04x to %d,%d\n", PRINT_REG(obj), x, y); - ADD_TO_CURRENT_PICTURE_PORT(sciw_new_button_control(s->port, obj, area, s->strSplit(text, NULL).c_str(), font_nr, + ADD_TO_CURRENT_PICTURE_PORT(sciw_new_button_control(s->port, obj, area, s->strSplit(text.c_str(), NULL).c_str(), font_nr, (int8)(state & kControlStateFramed), (int8)inverse, (int8)(state & kControlStateDisabled))); break; @@ -1587,7 +1597,7 @@ static void _k_draw_control(EngineState *s, reg_t obj, int inverse) { debugC(2, kDebugLevelGraphics, "drawing text %04x:%04x to %d,%d, mode=%d\n", PRINT_REG(obj), x, y, mode); - ADD_TO_CURRENT_PICTURE_PORT(sciw_new_text_control(s->port, obj, area, s->strSplit(text).c_str(), font_nr, mode, + ADD_TO_CURRENT_PICTURE_PORT(sciw_new_text_control(s->port, obj, area, s->strSplit(text.c_str()).c_str(), font_nr, mode, (int8)(!!(state & kControlStateDitherFramed)), (int8)inverse)); break; @@ -1597,11 +1607,11 @@ static void _k_draw_control(EngineState *s, reg_t obj, int inverse) { max = GET_SEL32V(obj, max); cursor = GET_SEL32V(obj, cursor); - if (cursor > (signed)strlen(text)) - cursor = strlen(text); + if (cursor > (signed)text.size()) + cursor = text.size(); // update_cursor_limits(&s->save_dir_edit_offset, &cursor, max); FIXME: get rid of this? - ADD_TO_CURRENT_PICTURE_PORT(sciw_new_edit_control(s->port, obj, area, text, font_nr, (unsigned)cursor, (int8)inverse)); + ADD_TO_CURRENT_PICTURE_PORT(sciw_new_edit_control(s->port, obj, area, text.c_str(), font_nr, (unsigned)cursor, (int8)inverse)); break; case K_CONTROL_ICON: @@ -1614,8 +1624,6 @@ static void _k_draw_control(EngineState *s, reg_t obj, int inverse) { case K_CONTROL_CONTROL: case K_CONTROL_CONTROL_ALIAS: { - const char **entries_list = NULL; - const char *seeker; int entries_nr; int lsTop = GET_SEL32V(obj, lsTop) - text_pos.offset; int list_top = 0; @@ -1627,29 +1635,41 @@ static void _k_draw_control(EngineState *s, reg_t obj, int inverse) { cursor = GET_SEL32V(obj, cursor) - text_pos.offset; entries_nr = 0; - seeker = text; - while (seeker[0]) { // Count string entries in NULL terminated string list + + // NOTE: most types of pointer dereferencing don't like odd offsets + assert((entry_size & 1) == 0); + + reg_t seeker = text_pos; + // Count string entries in NULL terminated string list + while (s->segMan->strlen(seeker) > 0) { ++entries_nr; - seeker += entry_size; + seeker.offset += entry_size; } + // TODO: This is rather convoluted... It would be a lot cleaner + // if sciw_new_list_control would take a list of Common::String + Common::String *strings = 0; + const char **entries_list = NULL; + if (entries_nr) { // determine list_top, selection, and the entries_list - seeker = text; + seeker = text_pos; entries_list = (const char**)malloc(sizeof(char *) * entries_nr); + strings = new Common::String[entries_nr]; for (i = 0; i < entries_nr; i++) { - entries_list[i] = seeker; - seeker += entry_size ; - if ((seeker - text) == lsTop) + strings[i] = s->segMan->getString(seeker); + entries_list[i] = strings[i].c_str(); + seeker.offset += entry_size; + if ((seeker.offset - text_pos.offset) == lsTop) list_top = i + 1; - if ((seeker - text) == cursor) + if ((seeker.offset - text_pos.offset) == cursor) selection = i + 1; } } - ADD_TO_CURRENT_PICTURE_PORT(sciw_new_list_control(s->port, obj, area, font_nr, entries_list, entries_nr, list_top, selection, (int8)inverse)); - if (entries_nr) - free(entries_list); + + free(entries_list); + delete[] strings; } break; @@ -2511,10 +2531,14 @@ reg_t kNewWindow(EngineState *s, int, int argc, reg_t *argv) { lWhite.alpha = 0; lWhite.priority = -1; lWhite.control = -1; - const char *title = argv[4 + argextra].segment ? s->segMan->derefString(argv[4 + argextra]) : NULL; + Common::String title; + if (argv[4 + argextra].segment) { + title = s->segMan->getString(argv[4 + argextra]); + title = s->strSplit(title.c_str(), NULL); + } window = sciw_new_window(s, gfx_rect(x, y, xl, yl), s->titlebar_port->_font, fgcolor, bgcolor, - s->titlebar_port->_font, lWhite, black, title ? s->strSplit(title, NULL).c_str() : NULL, flags); + s->titlebar_port->_font, lWhite, black, title.c_str(), flags); // PQ3 and SCI1.1 games have the interpreter store underBits implicitly if (argextra) @@ -3113,7 +3137,7 @@ reg_t kDisplay(EngineState *s, int, int argc, reg_t *argv) { int temp; bool save_under = false; gfx_color_t transparent = { PaletteEntry(), 0, -1, -1, 0 }; - char *text; + Common::String text; GfxPort *port = (s->port) ? s->port : s->picture_port; bool update_immediately = true; @@ -3139,16 +3163,18 @@ reg_t kDisplay(EngineState *s, int, int argc, reg_t *argv) { if (textp.segment) { argpt = 1; - text = s->segMan->derefString(textp); + text = s->segMan->getString(textp); } else { argpt = 2; text = kernel_lookup_text(s, textp, index); } +#if 0 if (!text) { error("Display with invalid reference %04x:%04x", PRINT_REG(textp)); return NULL_REG; } +#endif while (argpt < argc) { switch (argv[argpt++].toUint16()) { @@ -3251,7 +3277,7 @@ reg_t kDisplay(EngineState *s, int, int argc, reg_t *argv) { if (halign == ALIGN_LEFT) { // If the text does not fit on the screen, move it to the left and upwards until it does - gfxop_get_text_params(s->gfx_state, font_nr, text, area.width, &area.width, &area.height, 0, NULL, NULL, NULL); + gfxop_get_text_params(s->gfx_state, font_nr, text.c_str(), area.width, &area.width, &area.height, 0, NULL, NULL, NULL); // Make the text fit on the screen if (area.x + area.width > 320) @@ -3275,7 +3301,7 @@ reg_t kDisplay(EngineState *s, int, int argc, reg_t *argv) { assert_primary_widget_lists(s); - text_handle = gfxw_new_text(s->gfx_state, area, font_nr, s->strSplit(text).c_str(), halign, ALIGN_TOP, color0, *color1, bg_color, 0); + text_handle = gfxw_new_text(s->gfx_state, area, font_nr, s->strSplit(text.c_str()).c_str(), halign, ALIGN_TOP, color0, *color1, bg_color, 0); if (!text_handle) { error("Display: Failed to create text widget"); @@ -3293,7 +3319,7 @@ reg_t kDisplay(EngineState *s, int, int argc, reg_t *argv) { debugC(2, kDebugLevelGraphics, "Saving (%d, %d) size (%d, %d) as %04x:%04x\n", save_area.x, save_area.y, save_area.width, save_area.height, PRINT_REG(s->r_acc)); } - debugC(2, kDebugLevelGraphics, "Display: Commiting text '%s'\n", text); + debugC(2, kDebugLevelGraphics, "Display: Commiting text '%s'\n", text.c_str()); //ADD_TO_CURRENT_PICTURE_PORT(text_handle); @@ -3307,12 +3333,12 @@ reg_t kDisplay(EngineState *s, int, int argc, reg_t *argv) { } static reg_t kShowMovie_Windows(EngineState *s, int argc, reg_t *argv) { - const char *filename = s->segMan->derefString(argv[1]); + Common::String filename = s->segMan->getString(argv[1]); Graphics::AVIPlayer *player = new Graphics::AVIPlayer(g_system); if (!player->open(filename)) { - warning("Failed to open movie file %s", filename); + warning("Failed to open movie file %s", filename.c_str()); return s->r_acc; } @@ -3386,13 +3412,13 @@ static reg_t kShowMovie_Windows(EngineState *s, int argc, reg_t *argv) { } static reg_t kShowMovie_DOS(EngineState *s, int argc, reg_t *argv) { - const char *filename = s->segMan->derefString(argv[0]); + Common::String filename = s->segMan->getString(argv[0]); int delay = argv[1].toUint16(); // Time between frames in ticks int frameNr = 0; SeqDecoder seq; if (!seq.loadFile(filename) && !seq.loadFile(Common::String("SEQ/") + filename)) { - warning("Failed to open movie file %s", filename); + warning("Failed to open movie file %s", filename.c_str()); return s->r_acc; } diff --git a/engines/sci/engine/kmenu.cpp b/engines/sci/engine/kmenu.cpp index c57006c3a9..5a1f32e876 100644 --- a/engines/sci/engine/kmenu.cpp +++ b/engines/sci/engine/kmenu.cpp @@ -34,8 +34,8 @@ namespace Sci { reg_t kAddMenu(EngineState *s, int, int argc, reg_t *argv) { - char *name = s->segMan->derefString(argv[0]); - char *contents = s->segMan->derefString(argv[1]); + Common::String name = s->segMan->getString(argv[0]); + Common::String contents = s->segMan->getString(argv[1]); s->_menubar->addMenu(s->gfx_state, name, contents, s->titlebar_port->_font, argv[1]); @@ -78,8 +78,7 @@ reg_t kDrawStatus(EngineState *s, int, int argc, reg_t *argv) { s->status_bar_background = bgcolor; if (text.segment) { - const char *tmp = s->segMan->derefString(text); - s->_statusBarText = tmp ? tmp : ""; + s->_statusBarText = s->segMan->getString(text); } sciw_set_status_bar(s, s->titlebar_port, s->_statusBarText, fgcolor, bgcolor); diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp index dbbb10a4d1..dca123663d 100644 --- a/engines/sci/engine/kmisc.cpp +++ b/engines/sci/engine/kmisc.cpp @@ -180,53 +180,39 @@ reg_t kMemory(EngineState *s, int, int argc, reg_t *argv) { break; case K_MEMORY_MEMCPY : { int size = argv[3].toUint16(); - byte *dest = s->segMan->derefBulkPtr(argv[1], size); - byte *src = s->segMan->derefBulkPtr(argv[2], size); - - if (dest && src) - memcpy(dest, src, size); - else { - warning("Could not execute kMemory:memcpy of %d bytes:", size); - if (!dest) { - warning(" dest ptr (%04x:%04x) invalid/memory region too small", PRINT_REG(argv[1])); - } - if (!src) { - warning(" src ptr (%04x:%04x) invalid/memory region too small", PRINT_REG(argv[2])); - } - } + s->segMan->memcpy(argv[1], argv[2], size); break; } case K_MEMORY_PEEK : { - byte *ref = s->segMan->derefBulkPtr(argv[1], 2); + SegmentRef ref = s->segMan->dereference(argv[1]); - if (!ref) { + if (!ref.isValid() || ref.maxSize < 2) { // This occurs in KQ5CD when interacting with certain objects warning("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1])); return s->r_acc; } - if (s->segMan->getSegmentType(argv[1].segment) == SEG_TYPE_LOCALS) - return *((reg_t *) ref); + if (ref.isRaw) + return make_reg(0, (int16)READ_LE_UINT16(ref.raw)); else - return make_reg(0, (int16)READ_LE_UINT16(ref)); + return *(ref.reg); break; } case K_MEMORY_POKE : { - byte *ref = s->segMan->derefBulkPtr(argv[1], 2); + SegmentRef ref = s->segMan->dereference(argv[1]); - if (!ref) { + if (!ref.isValid() || ref.maxSize < 2) { warning("Attempt to poke invalid memory at %04x:%04x", PRINT_REG(argv[1])); return s->r_acc; } - if (s->segMan->getSegmentType(argv[1].segment) == SEG_TYPE_LOCALS) - *((reg_t *) ref) = argv[2]; - else { + if (ref.isRaw) { if (argv[2].segment) { error("Attempt to poke memory reference %04x:%04x to %04x:%04x", PRINT_REG(argv[2]), PRINT_REG(argv[1])); return s->r_acc; - WRITE_LE_UINT16(ref, argv[2].offset); // ? } - } + WRITE_LE_UINT16(ref.raw, argv[2].offset); + } else + *(ref.reg) = argv[2]; break; } } diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index 6bc2a1086f..421974701e 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -257,27 +257,18 @@ struct PathfindingState { static Vertex *s_vertex_cur; // FIXME: Avoid non-const global vars -// FIXME: Temporary hack to deal with points in reg_ts -static bool polygon_is_reg_t(const byte *list, int size) { - // Check the first three reg_ts - for (int i = 0; i < (size < 3 ? size : 3); i++) - if ((((reg_t *) list) + i)->segment) - // Non-zero segment, cannot be reg_ts - return false; - - // First three segments were zero, assume reg_ts - return true; -} - -static Common::Point read_point(const byte *list, int is_reg_t, int offset) { +static Common::Point read_point(SegManager *segMan, reg_t list, int offset) { + SegmentRef list_r = segMan->dereference(list); + if (!list_r.isValid()) { + warning("Attempt to dereference invalid pointer %04x:%04x", PRINT_REG(list)); + } Common::Point point; - if (!is_reg_t) { - POLY_GET_POINT(list, offset, point); + if (list_r.isRaw) { + POLY_GET_POINT(list_r.raw, offset, point); } else { - POLY_GET_POINT_REG_T((reg_t *)list, offset, point); + POLY_GET_POINT_REG_T(list_r.reg, offset, point); } - return point; } @@ -295,14 +286,12 @@ static bool polygons_equal(SegManager *segMan, reg_t p1, reg_t p2) { if (size != GET_SEL32(p2, size).toUint16()) return false; - const byte *p1_points = segMan->derefBulkPtr(GET_SEL32(p1, points), size * POLY_POINT_SIZE); - const byte *p2_points = segMan->derefBulkPtr(GET_SEL32(p2, points), size * POLY_POINT_SIZE); - bool p1_is_reg_t = polygon_is_reg_t(p1_points, size); - bool p2_is_reg_t = polygon_is_reg_t(p2_points, size); + reg_t p1_points = GET_SEL32(p1, points); + reg_t p2_points = GET_SEL32(p2, points); // Check for the same points for (int i = 0; i < size; i++) { - if (read_point(p1_points, p1_is_reg_t, i) != read_point(p2_points, p2_is_reg_t, i)) + if (read_point(segMan, p1_points, i) != read_point(segMan, p2_points, i)) return false; } @@ -360,14 +349,12 @@ static void draw_polygon(EngineState *s, reg_t polygon) { int size = GET_SEL32(polygon, size).toUint16(); int type = GET_SEL32(polygon, type).toUint16(); Common::Point first, prev; - const byte *list = s->segMan->derefBulkPtr(points, size * POLY_POINT_SIZE); - int is_reg_t = polygon_is_reg_t(list, size); int i; - prev = first = read_point(list, is_reg_t, 0); + prev = first = read_point(segMan, points, 0); for (i = 1; i < size; i++) { - Common::Point point = read_point(list, is_reg_t, i); + Common::Point point = read_point(segMan, points, i); draw_line(s, prev, point, type); prev = point; } @@ -407,18 +394,16 @@ static void print_polygon(SegManager *segMan, reg_t polygon) { int size = GET_SEL32(polygon, size).toUint16(); int type = GET_SEL32(polygon, type).toUint16(); int i; - const byte *point_array = segMan->derefBulkPtr(points, size * POLY_POINT_SIZE); - int is_reg_t = polygon_is_reg_t(point_array, size); Common::Point point; printf("%i:", type); for (i = 0; i < size; i++) { - point = read_point(point_array, is_reg_t, i); + point = read_point(segMan, points, i); printf(" (%i, %i)", point.x, point.y); } - point = read_point(point_array, is_reg_t, 0); + point = read_point(segMan, points, 0); printf(" (%i, %i);\n", point.x, point.y); } @@ -1231,15 +1216,15 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) { int i; reg_t points = GET_SEL32(polygon, points); int size = GET_SEL32(polygon, size).toUint16(); - const byte *list = s->segMan->derefBulkPtr(points, size * POLY_POINT_SIZE); Polygon *poly = new Polygon(GET_SEL32(polygon, type).toUint16()); - int is_reg_t = polygon_is_reg_t(list, size); + + int skip = 0; // WORKAROUND: broken polygon in lsl1sci, room 350, after opening elevator // Polygon has 17 points but size is set to 19 if ((size == 19) && (s->_gameName == "lsl1sci")) { if ((s->currentRoomNumber() == 350) - && (read_point(list, is_reg_t, 18) == Common::Point(108, 137))) { + && (read_point(segMan, points, 18) == Common::Point(108, 137))) { debug(1, "Applying fix for broken polygon in lsl1sci, room 350"); size = 17; } @@ -1248,33 +1233,31 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) { // WORKAROUND: self-intersecting polygons in ECO, rooms 221, 280 and 300 if ((size == 11) && (s->_gameName == "ecoquest")) { if ((s->currentRoomNumber() == 300) - && (read_point(list, is_reg_t, 10) == Common::Point(221, 0))) { + && (read_point(segMan, points, 10) == Common::Point(221, 0))) { debug(1, "Applying fix for self-intersecting polygon in ECO, room 300"); size = 10; } } if ((size == 12) && (s->_gameName == "ecoquest")) { if ((s->currentRoomNumber() == 280) - && (read_point(list, is_reg_t, 11) == Common::Point(238, 189))) { + && (read_point(segMan, points, 11) == Common::Point(238, 189))) { debug(1, "Applying fix for self-intersecting polygon in ECO, room 280"); size = 10; } } if ((size == 16) && (s->_gameName == "ecoquest")) { if ((s->currentRoomNumber() == 221) - && (read_point(list, is_reg_t, 1) == Common::Point(419, 175))) { + && (read_point(segMan, points, 1) == Common::Point(419, 175))) { debug(1, "Applying fix for self-intersecting polygon in ECO, room 221"); // Swap the first two points - poly->vertices.insertHead(new Vertex(read_point(list, is_reg_t, 1))); - poly->vertices.insertHead(new Vertex(read_point(list, is_reg_t, 0))); - size = 14; - assert(!is_reg_t); - list += 2 * POLY_POINT_SIZE; + poly->vertices.insertHead(new Vertex(read_point(segMan, points, 1))); + poly->vertices.insertHead(new Vertex(read_point(segMan, points, 0))); + skip = 2; } } - for (i = 0; i < size; i++) { - Vertex *vertex = new Vertex(read_point(list, is_reg_t, i)); + for (i = skip; i < size; i++) { + Vertex *vertex = new Vertex(read_point(segMan, points, i)); poly->vertices.insertHead(vertex); } diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index 3d22936680..84ac720a05 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -39,12 +39,12 @@ namespace Sci { } /* Returns the string the script intended to address */ -char *kernel_lookup_text(EngineState *s, reg_t address, int index) { +Common::String kernel_lookup_text(EngineState *s, reg_t address, int index) { char *seeker; Resource *textres; if (address.segment) - return s->segMan->derefString(address); + return s->segMan->getString(address); else { int textlen; int _index = index; @@ -189,7 +189,7 @@ reg_t kSetSynonyms(EngineState *s, int, int argc, reg_t *argv) { reg_t kParse(EngineState *s, int, int argc, reg_t *argv) { SegManager *segMan = s->segMan; reg_t stringpos = argv[0]; - char *string = s->segMan->derefString(stringpos); + Common::String string = s->segMan->getString(stringpos); char *error; ResultWordList words; reg_t event = argv[1]; @@ -197,7 +197,7 @@ reg_t kParse(EngineState *s, int, int argc, reg_t *argv) { s->parser_event = event; - bool res = voc->tokenizeString(words, string, &error); + bool res = voc->tokenizeString(words, string.c_str(), &error); s->parser_valid = 0; /* not valid */ if (res && !words.empty()) { @@ -242,8 +242,7 @@ reg_t kParse(EngineState *s, int, int argc, reg_t *argv) { s->r_acc = make_reg(0, 0); PUT_SEL32V(event, claimed, 1); if (error) { - char *pbase_str = s->segMan->derefString(s->parser_base); - strcpy(pbase_str, error); + s->segMan->strcpy(s->parser_base, error); debugC(2, kDebugLevelParser, "Word unknown: %s\n", error); /* Issue warning: */ @@ -259,129 +258,66 @@ reg_t kParse(EngineState *s, int, int argc, reg_t *argv) { reg_t kStrEnd(EngineState *s, int, int argc, reg_t *argv) { reg_t address = argv[0]; - char *seeker = s->segMan->derefString(address); - - while (*seeker++) - ++address.offset; + address.offset += s->segMan->strlen(address); return address; } reg_t kStrCat(EngineState *s, int, int argc, reg_t *argv) { - char *s1 = s->segMan->derefString(argv[0]); - char *s2 = s->segMan->derefString(argv[1]); + Common::String s1 = s->segMan->getString(argv[0]); + Common::String s2 = s->segMan->getString(argv[1]); - strcat(s1, s2); + s1 += s2; + s->segMan->strcpy(argv[0], s1.c_str()); return argv[0]; } reg_t kStrCmp(EngineState *s, int, int argc, reg_t *argv) { - char *s1 = s->segMan->derefString(argv[0]); - char *s2 = s->segMan->derefString(argv[1]); + Common::String s1 = s->segMan->getString(argv[0]); + Common::String s2 = s->segMan->getString(argv[1]); if (argc > 2) - return make_reg(0, strncmp(s1, s2, argv[2].toUint16())); + return make_reg(0, strncmp(s1.c_str(), s2.c_str(), argv[2].toUint16())); else - return make_reg(0, strcmp(s1, s2)); + return make_reg(0, strcmp(s1.c_str(), s2.c_str())); } reg_t kStrCpy(EngineState *s, int, int argc, reg_t *argv) { - char *dest = s->segMan->derefString(argv[0]); - char *src = s->segMan->derefString(argv[1]); - - if (!dest) { - warning("Attempt to strcpy TO invalid pointer %04x:%04x", - PRINT_REG(argv[0])); - return NULL_REG; - } - if (!src) { - warning("Attempt to strcpy FROM invalid pointer %04x:%04x", - PRINT_REG(argv[1])); - *dest = 0; - return argv[1]; - } - if (argc > 2) { int length = argv[2].toSint16(); if (length >= 0) - strncpy(dest, src, length); - else { - if (s->segMan->_heap[argv[0].segment]->getType() == SEG_TYPE_DYNMEM) { - reg_t *srcp = (reg_t *) src; - - int i; - warning("Performing reg_t to raw conversion for AvoidPath"); - for (i = 0; i < -length / 2; i++) { - dest[2 * i] = srcp->offset & 0xff; - dest[2 * i + 1] = srcp->offset >> 8; - srcp++; - } - } else - memcpy(dest, src, -length); - } + s->segMan->strncpy(argv[0], argv[1], length); + else + s->segMan->memcpy(argv[0], argv[1], -length); } else - strcpy(dest, src); + s->segMan->strcpy(argv[0], argv[1]); return argv[0]; } -/* Simple heuristic to work around array handling peculiarity in SQ4: -It uses StrAt() to read the individual elements, so we must determine -whether a string is really a string or an array. */ -static int is_print_str(const char *str) { - int printable = 0; - int len = strlen(str); - - if (len == 0) return 1; - - while (*str) { - // The parameter passed to isprint() needs to be in the range - // 0 to 0xFF or EOF, according to MSDN, therefore we cast it - // to an unsigned char. Values outside this range (in this - // case, negative values) yield unpredictable results. Refer to: - // http://msdn.microsoft.com/en-us/library/ewx8s4kw.aspx - if (isprint((byte)*str)) - printable++; - str++; - } - - return ((float)printable / (float)len >= 0.5); -} - reg_t kStrAt(EngineState *s, int, int argc, reg_t *argv) { - byte *dest = (byte*)s->segMan->derefString(argv[0]); - reg_t *dest2; - - if (!dest) { + SegmentRef dest_r = s->segMan->dereference(argv[0]); + if (!dest_r.raw) { warning("Attempt to StrAt at invalid pointer %04x:%04x", PRINT_REG(argv[0])); return NULL_REG; } - bool lsl5PasswordWorkaround = false; - // LSL5 stores the password at the beginning in memory.drv, using XOR encryption, - // which means that is_print_str() will fail. Therefore, do not use the heuristic to determine - // if we're handling a string or an array for LSL5's password screen (room 155) - if (s->_gameName.equalsIgnoreCase("lsl5") && s->currentRoomNumber() == 155) - lsl5PasswordWorkaround = true; - - if ((argc == 2) && - /* Our pathfinder already works around the issue we're trying to fix */ - (strcmp(s->segMan->getDescription(argv[0]), AVOIDPATH_DYNMEM_STRING) != 0) && - ((strlen((const char*)dest) < 2) || - (!lsl5PasswordWorkaround && !is_print_str((const char*)dest)))) { - // SQ4 array handling detected + byte* dest; + + if (dest_r.isRaw) { + dest = (byte*)dest_r.raw + argv[1].toUint16(); + } else { #ifndef SCUMM_BIG_ENDIAN int odd = argv[1].toUint16() & 1; #else int odd = !(argv[1].toUint16() & 1); #endif - dest2 = ((reg_t *) dest) + (argv[1].toUint16() / 2); - dest = ((byte *)(&dest2->offset)) + odd; - } else - dest += argv[1].toUint16(); + reg_t *tmp = dest_r.reg + (argv[1].toUint16() / 2); + dest = ((byte *)(&tmp->offset)) + odd; + } s->r_acc = make_reg(0, *dest); @@ -393,7 +329,8 @@ reg_t kStrAt(EngineState *s, int, int argc, reg_t *argv) { reg_t kReadNumber(EngineState *s, int, int argc, reg_t *argv) { - char *source = s->segMan->derefString(argv[0]); + Common::String source_str = s->segMan->getString(argv[0]); + const char *source = source_str.c_str(); while (isspace(*source)) source++; /* Skip whitespace */ @@ -419,10 +356,10 @@ reg_t kReadNumber(EngineState *s, int, int argc, reg_t *argv) { reg_t kFormat(EngineState *s, int, int argc, reg_t *argv) { int *arguments; reg_t dest = argv[0]; - char *target = s->segMan->derefString(dest); + char targetbuf[512]; + char *target = targetbuf; reg_t position = argv[1]; /* source */ int index = argv[2].toUint16(); - char *source; char *str_base = target; int mode = 0; int paramindex = 0; /* Next parameter to evaluate */ @@ -439,7 +376,8 @@ reg_t kFormat(EngineState *s, int, int argc, reg_t *argv) { else startarg = 3; /* First parameter to use for formatting */ - source = kernel_lookup_text(s, position, index); + Common::String source_str = kernel_lookup_text(s, position, index); + const char* source = source_str.c_str(); debugC(2, kDebugLevelStrings, "Formatting \"%s\"\n", source); @@ -505,9 +443,9 @@ reg_t kFormat(EngineState *s, int, int argc, reg_t *argv) { switch (xfer) { case 's': { /* Copy string */ reg_t reg = argv[startarg + paramindex]; - char *tempsource = kernel_lookup_text(s, reg, - arguments[paramindex + 1]); - int slen = strlen(tempsource); + Common::String tempsource = kernel_lookup_text(s, reg, + arguments[paramindex + 1]); + int slen = strlen(tempsource.c_str()); int extralen = str_leng - slen; CHECK_OVERFLOW1(target, extralen, NULL_REG); if (extralen < 0) @@ -538,7 +476,7 @@ reg_t kFormat(EngineState *s, int, int argc, reg_t *argv) { } - strcpy(target, tempsource); + strcpy(target, tempsource.c_str()); target += slen; switch (align) { @@ -627,19 +565,15 @@ reg_t kFormat(EngineState *s, int, int argc, reg_t *argv) { free(arguments); *target = 0; /* Terminate string */ + + s->segMan->strcpy(dest, targetbuf); + return dest; /* Return target addr */ } reg_t kStrLen(EngineState *s, int, int argc, reg_t *argv) { - char *str = s->segMan->derefString(argv[0]); - - if (!str) { - warning("StrLen: invalid pointer %04x:%04x", PRINT_REG(argv[0])); - return NULL_REG; - } - - return make_reg(0, strlen(str)); + return make_reg(0, s->segMan->strlen(argv[0])); } @@ -664,7 +598,7 @@ reg_t kGetFarText(EngineState *s, int, int argc, reg_t *argv) { ** resource. */ - strcpy(s->segMan->derefString(argv[2]), seeker); /* Copy the string and get return value */ + s->segMan->strcpy(argv[2], seeker); /* Copy the string and get return value */ return argv[2]; } @@ -710,7 +644,6 @@ reg_t kMessage(EngineState *s, int, int argc, reg_t *argv) { case K_MESSAGE_GET: case K_MESSAGE_NEXT: { reg_t bufferReg; - char *buffer = NULL; Common::String str; reg_t retval; @@ -739,18 +672,15 @@ reg_t kMessage(EngineState *s, int, int argc, reg_t *argv) { if (!bufferReg.isNull()) { int len = str.size() + 1; - buffer = s->segMan->derefString(bufferReg, len); - - if (buffer) { - strcpy(buffer, str.c_str()); - } else { + SegmentRef buffer_r = s->segMan->dereference(bufferReg); + if (buffer_r.maxSize < len) { warning("Message: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(bufferReg), len, str.c_str()); // Set buffer to empty string if possible - buffer = s->segMan->derefString(bufferReg, 1); - if (buffer) - *buffer = 0; - } + if (buffer_r.maxSize > 0) + s->segMan->strcpy(bufferReg, ""); + } else + s->segMan->strcpy(bufferReg, str.c_str()); s->_msgState.gotoNext(); } @@ -792,6 +722,8 @@ reg_t kMessage(EngineState *s, int, int argc, reg_t *argv) { if (buffer) { // FIXME: Is this correct? I.e., do we really write into a "raw" segment // here? Or maybe we want to write 4 reg_t instead? + assert(s->segMan->dereference(argv[1]).isRaw); + WRITE_LE_UINT16(buffer, module); WRITE_LE_UINT16(buffer + 2, msg.noun); WRITE_LE_UINT16(buffer + 4, msg.verb); @@ -811,26 +743,29 @@ reg_t kMessage(EngineState *s, int, int argc, reg_t *argv) { } reg_t kSetQuitStr(EngineState *s, int, int argc, reg_t *argv) { - char *quitStr = s->segMan->derefString(argv[0]); - debug("Setting quit string to '%s'", quitStr); - return s->r_acc; + Common::String quitStr = s->segMan->getString(argv[0]); + debug("Setting quit string to '%s'", quitStr.c_str()); + return s->r_acc; } reg_t kStrSplit(EngineState *s, int, int argc, reg_t *argv) { - const char *format = s->segMan->derefString(argv[1]); - const char *sep = !argv[2].isNull() ? s->segMan->derefString(argv[2]) : NULL; - Common::String str = s->strSplit(format, sep); + Common::String format = s->segMan->getString(argv[1]); + Common::String sep_str; + const char *sep = NULL; + if (!argv[2].isNull()) { + sep_str = s->segMan->getString(argv[2]); + sep = sep_str.c_str(); + } + Common::String str = s->strSplit(format.c_str(), sep); // Make sure target buffer is large enough - char *buf = s->segMan->derefString(argv[0], str.size() + 1); - - if (buf) { - strcpy(buf, str.c_str()); - return argv[0]; - } else { + SegmentRef buf_r = s->segMan->dereference(argv[0]); + if (!buf_r.isValid() || buf_r.maxSize < (int)str.size() + 1) { warning("StrSplit: buffer %04x:%04x invalid or too small to hold the following text of %i bytes: '%s'", PRINT_REG(argv[0]), str.size() + 1, str.c_str()); return NULL_REG; } + s->segMan->strcpy(argv[0], str.c_str()); + return argv[0]; } } // End of namespace Sci diff --git a/engines/sci/gfx/gfx_gui.cpp b/engines/sci/gfx/gfx_gui.cpp index b4479b7341..6deb01b1c2 100644 --- a/engines/sci/gfx/gfx_gui.cpp +++ b/engines/sci/gfx/gfx_gui.cpp @@ -175,7 +175,7 @@ GfxPort *sciw_new_window(EngineState *s, win = new GfxPort(visual, area, color, bgcolor); win->_font = font; - win->title_text = title; + win->_title_text = title; win->port_flags = flags; win->_flags |= GFXW_FLAG_IMMUNE_TO_SNAPSHOTS; diff --git a/engines/sci/gfx/gfx_state_internal.h b/engines/sci/gfx/gfx_state_internal.h index 1bff83e713..aeb3b05cd4 100644 --- a/engines/sci/gfx/gfx_state_internal.h +++ b/engines/sci/gfx/gfx_state_internal.h @@ -382,7 +382,7 @@ struct GfxPort : public GfxContainer { gfxw_snapshot_t *restore_snap; /**< Snapshot to be restored automagically, experimental feature used in the PQ3 interpreter */ int port_flags; /**< interpreter-dependant flags */ - const char *title_text; + Common::String _title_text; byte gray_text; /**< Whether text is 'grayed out' (dithered) */ public: diff --git a/engines/sci/gfx/gfx_widgets.cpp b/engines/sci/gfx/gfx_widgets.cpp index 3d94bf1342..1d737e9eb2 100644 --- a/engines/sci/gfx/gfx_widgets.cpp +++ b/engines/sci/gfx/gfx_widgets.cpp @@ -1581,7 +1581,6 @@ GfxPort::GfxPort(GfxVisual *visual_, rect_t area, gfx_color_t fgcolor, gfx_color port_bg = NULL; _parent = NULL; _decorations = NULL; - title_text = NULL; draw_pos = Common::Point(0, 0); gray_text = 0; _color = fgcolor; diff --git a/engines/sci/gfx/menubar.cpp b/engines/sci/gfx/menubar.cpp index 1c04486d4d..283b233477 100644 --- a/engines/sci/gfx/menubar.cpp +++ b/engines/sci/gfx/menubar.cpp @@ -122,7 +122,7 @@ int Menu::addMenuItem(GfxState *state, MenuType type, const char *left, const ch return total_left_size + width; } -void Menubar::addMenu(GfxState *state, const char *title, const char *entries, int font, reg_t entries_base) { +void Menubar::addMenu(GfxState *state, const Common::String &title, const Common::String &entries, int font, reg_t entries_base) { char tracker; char *left = NULL; reg_t left_origin = entries_base; @@ -134,17 +134,19 @@ void Menubar::addMenu(GfxState *state, const char *title, const char *entries, i menu._title = title; - gfxop_get_text_params(state, font, title, SIZE_INF, &(menu._titleWidth), &height, 0, NULL, NULL, NULL); + gfxop_get_text_params(state, font, title.c_str(), SIZE_INF, &(menu._titleWidth), &height, 0, NULL, NULL, NULL); + + const char *entries_p = entries.c_str(); do { - tracker = *entries++; + tracker = *entries_p++; entries_base.offset++; if (!left) { // Left string not finished? if (tracker == '=') { // Hit early-SCI tag assignment? - left = sci_strndup(entries - string_len - 1, string_len); - tag = atoi(entries++); - tracker = *entries++; + left = sci_strndup(entries_p - string_len - 1, string_len); + tag = atoi(entries_p++); + tracker = *entries_p++; } if ((tracker == 0 && string_len > 0) || (tracker == '=') || (tracker == ':')) { // End of entry MenuType entrytype = MENU_TYPE_NORMAL; @@ -152,7 +154,7 @@ void Menubar::addMenu(GfxState *state, const char *title, const char *entries, i reg_t beginning; if (!left) - left = sci_strndup(entries - string_len - 1, string_len); + left = sci_strndup(entries_p - string_len - 1, string_len); inleft = left; while (isspace(*inleft)) @@ -179,7 +181,7 @@ void Menubar::addMenu(GfxState *state, const char *title, const char *entries, i if (!left) { left_origin = entries_base; left_origin.offset -= string_len + 1; - left = sci_strndup(entries - string_len - 1, string_len); + left = sci_strndup(entries_p - string_len - 1, string_len); } string_len = 0; // Continue with the right string } else @@ -189,7 +191,7 @@ void Menubar::addMenu(GfxState *state, const char *title, const char *entries, i if ((tracker == ':') || (tracker == 0)) { // End of entry int key, modifiers = 0; - char *right = sci_strndup(entries - string_len - 1, string_len); + char *right = sci_strndup(entries_p - string_len - 1, string_len); if (right[0] == '#') { right[0] = SCI_SPECIAL_CHAR_FUNCTION; // Function key @@ -294,7 +296,7 @@ int Menubar::setAttribute(EngineState *s, int menu_nr, int item_nr, int attribut case MENU_ATTRIBUTE_SAID: if (value.segment) { item->_saidPos = value; - memcpy(item->_said, s->segMan->derefBulkPtr(value, 0), MENU_SAID_SPEC_SIZE); // Copy Said spec + s->segMan->memcpy(item->_said, value, MENU_SAID_SPEC_SIZE); // Copy Said spec item->_flags |= MENU_ATTRIBUTE_FLAGS_SAID; } else @@ -304,7 +306,7 @@ int Menubar::setAttribute(EngineState *s, int menu_nr, int item_nr, int attribut case MENU_ATTRIBUTE_TEXT: assert(value.segment); - item->_text = s->segMan->derefString(value); + item->_text = s->segMan->getString(value); item->_textPos = value; break; diff --git a/engines/sci/gfx/menubar.h b/engines/sci/gfx/menubar.h index 44ecd8f1bb..029af5923b 100644 --- a/engines/sci/gfx/menubar.h +++ b/engines/sci/gfx/menubar.h @@ -171,7 +171,7 @@ public: * @param[in] font The font which is to be used for drawing * @param[in] entries_base Segmented VM address of the entries string */ - void addMenu(GfxState *state, const char *title, const char *entries, int font, reg_t entries_base); + void addMenu(GfxState *state, const Common::String &title, const Common::String &entries, int font, reg_t entries_base); /** -- cgit v1.2.3