[Scummvm-cvs-logs] scummvm master -> dec2b0857ca54ecf61e95c731d10ebfb85b5b375

dreammaster dreammaster at scummvm.org
Sun Jun 21 15:49:09 CEST 2015


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
dec2b0857c SHERLOCK: Split up Journal class for each game


Commit: dec2b0857ca54ecf61e95c731d10ebfb85b5b375
    https://github.com/scummvm/scummvm/commit/dec2b0857ca54ecf61e95c731d10ebfb85b5b375
Author: Paul Gilbert (dreammaster at scummvm.org)
Date: 2015-06-21T09:47:59-04:00

Commit Message:
SHERLOCK: Split up Journal class for each game

Changed paths:
  A engines/sherlock/scalpel/scalpel_journal.cpp
  A engines/sherlock/scalpel/scalpel_journal.h
  A engines/sherlock/tattoo/tattoo_journal.cpp
  A engines/sherlock/tattoo/tattoo_journal.h
    engines/sherlock/journal.cpp
    engines/sherlock/journal.h
    engines/sherlock/module.mk
    engines/sherlock/scalpel/scalpel_user_interface.cpp
    engines/sherlock/sherlock.cpp
    engines/sherlock/tattoo/tattoo_user_interface.cpp
    engines/sherlock/tattoo/widget_inventory.cpp



diff --git a/engines/sherlock/journal.cpp b/engines/sherlock/journal.cpp
index 0fd0947..c7c6569 100644
--- a/engines/sherlock/journal.cpp
+++ b/engines/sherlock/journal.cpp
@@ -22,1338 +22,19 @@
 
 #include "sherlock/journal.h"
 #include "sherlock/sherlock.h"
+#include "sherlock/scalpel/scalpel_journal.h"
+#include "sherlock/tattoo/tattoo_journal.h"
 
 namespace Sherlock {
 
-#define JOURNAL_BUTTONS_Y 178
-#define LINES_PER_PAGE 11
-#define JOURNAL_SEARCH_LEFT 15
-#define JOURNAL_SEARCH_TOP 186
-#define JOURNAL_SEARCH_RIGHT 296
-#define JOURNAL_SEACRH_MAX_CHARS 50
-
-// Positioning of buttons in the journal view
-static const int JOURNAL_POINTS[9][3] = {
-	{ 6, 68, 37 },
-	{ 69, 131, 100 },
-	{ 132, 192, 162 },
-	{ 193, 250, 221 },
-	{ 251, 313, 281 },
-	{ 6, 82, 44 },
-	{ 83, 159, 121 },
-	{ 160, 236, 198 },
-	{ 237, 313, 275 }
-};
-
-static const int SEARCH_POINTS[3][3] = {
-	{ 51, 123, 86 },
-	{ 124, 196, 159 },
-	{ 197, 269, 232 }
-};
-
-/*----------------------------------------------------------------*/
-
-Journal::Journal(SherlockEngine *vm) : _vm(vm) {
-	// Initialize fields
-	_maxPage = 0;
-	_index = 0;
-	_sub = 0;
-	_up = _down = false;
-	_page = 1;
-
-	if (_vm->_interactiveFl) {
-		// Load the journal directory and location names
-		loadJournalLocations();
-	}
-}
-
-void Journal::record(int converseNum, int statementNum, bool replyOnly) {
-	int saveIndex = _index;
-	int saveSub = _sub;
-
-	if (IS_3DO) {
-		// there seems to be no journal in the 3DO version
-		return;
-	}
-
-	// Record the entry into the list
-	_journal.push_back(JournalEntry(converseNum, statementNum, replyOnly));
-	_index = _journal.size() - 1;
-
-	// Load the text for the new entry to get the number of lines it will have
-	loadJournalFile(true);
-
-	// Restore old state
-	_index = saveIndex;
-	_sub = saveSub;
-
-	// If new lines were added to the ournal, update the total number of lines
-	// the journal continues
-	if (!_lines.empty()) {
-		_maxPage += _lines.size();
-	} else {
-		// No lines in entry, so remove the new entry from the journal
-		_journal.remove_at(_journal.size() - 1);
-	}
-}
-
-void Journal::loadJournalLocations() {
-	Resources &res = *_vm->_res;
-
-	_directory.clear();
-
-	Common::SeekableReadStream *dir = res.load("talk.lib");
-	dir->skip(4);		// Skip header
-
-	// Get the numer of entries
-	_directory.resize(dir->readUint16LE());
-	if (IS_ROSE_TATTOO)
-		dir->seek((_directory.size() + 1) * 8, SEEK_CUR);
-
-	// Read in each entry
-	char buffer[17];
-	for (uint idx = 0; idx < _directory.size(); ++idx) {
-		dir->read(buffer, 17);
-		buffer[16] = '\0';
-
-		_directory[idx] = Common::String(buffer);
-	}
-
-	delete dir;
-
-	_locations.clear();
-
-	if (IS_3DO) {
-		// 3DO: storage of locations is currently unknown TODO
-		return;
-	}
-
-	// Load in the locations stored in journal.txt
-	Common::SeekableReadStream *loc = res.load("journal.txt");
-
-	if (IS_SERRATED_SCALPEL) {
-		while (loc->pos() < loc->size()) {
-			Common::String line;
-			char c;
-			while ((c = loc->readByte()) != 0)
-				line += c;
-
-			_locations.push_back(line);
-		}
-	} else {
-		// Initialize locations
-		_locations.resize(100);
-		for (int i = 0; i < 100; i++)
-			_locations[i] = "No Description";
-
-		while (loc->pos() < loc->size()) {
-			// In Rose Tattoo, each location line starts with the location
-			// number, followed by a dot, some spaces and its description
-			// in quotes
-			Common::String line = loc->readLine();
-			Common::String locNumStr;
-			int locNum = 0;
-			int i = 0;
-			Common::String locDesc;
-
-			// Get the location
-			while (Common::isDigit(line[i])) {
-				locNumStr += line[i];
-				i++;
-			}
-			locNum = atoi(locNumStr.c_str());
-
-			// Skip the dot, spaces and initial quotation mark
-			while (line[i] == ' ' || line[i] == '.' || line[i] == '\"')
-				i++;
-
-			do {
-				locDesc += line[i];
-				i++;
-			} while (line[i] != '\"');
-
-			_locations[locNum] = locDesc;
-		}
-	}
-
-	delete loc;
-}
-
-void Journal::loadJournalFile(bool alreadyLoaded) {
-	People &people = *_vm->_people;
-	Screen &screen = *_vm->_screen;
-	Talk &talk = *_vm->_talk;
-	JournalEntry &journalEntry = _journal[_index];
-	const byte *opcodes = talk._opcodes;
-
-	Common::String dirFilename = _directory[journalEntry._converseNum];
-	bool replyOnly = journalEntry._replyOnly;
-
-	// Get the location number from within the filename
-	Common::String locStr(dirFilename.c_str() + 4, dirFilename.c_str() + 6);
-	int newLocation = atoi(locStr.c_str());
-
-	// If not flagged as already loaded, load the conversation into script variables
-	if (!alreadyLoaded) {
-		// See if the file to be used is already loaded
-		if (journalEntry._converseNum != talk._converseNum) {
-			// Nope. Free any previously loaded talk
-			talk.freeTalkVars();
-
-			// Find the person being referred to
-			talk._talkTo = -1;
-			for (int idx = 0; idx < (int)people._characters.size(); ++idx) {
-				Common::String portrait = people._characters[idx]._portrait;
-				Common::String numStr(portrait.c_str(), portrait.c_str() + 4);
-
-				if (locStr == numStr) {
-					talk._talkTo = idx;
-					break;
-				}
-			}
-
-			// Load their talk file
-			talk.loadTalkFile(dirFilename);
-		}
-	}
-
-	if (talk[0]._statement.hasPrefix("*") || talk[0]._statement.hasPrefix("^"))
-		replyOnly = true;
-
-	// If this isn't the first journal entry, see if the previous journal entry
-	// was in the same scene to see if we need to include the scene header
-	int oldLocation = -1;
-	if (_index != 0) {
-		// Get the scene number of the prior journal entry
-		Common::String priorEntry = _directory[_journal[_index - 1]._converseNum];
-		oldLocation = atoi(Common::String(priorEntry.c_str() + 4, priorEntry.c_str() + 6).c_str());
-	}
-
-	// Start building journal string
-	Statement &statement = talk[journalEntry._statementNum];
-	Common::String journalString;
-
-	if (newLocation != oldLocation) {
-		// Add in scene title
-		journalString = "@";
-		if (IS_SERRATED_SCALPEL || newLocation - 1 < 100)
-			journalString += _locations[newLocation - 1];
-		journalString += ":";
-
-		// See if title can fit into a single line, or requires splitting on 2 lines
-		int width = screen.stringWidth(journalString.c_str() + 1);
-		if (width > JOURNAL_MAX_WIDTH) {
-			// Scan backwards from end of title to find a space between a word
-			// where the width is less than the maximum allowed for the line
-			const char *lineP = journalString.c_str() + journalString.size() - 1;
-			while (width > JOURNAL_MAX_WIDTH || *lineP != ' ')
-				width -= screen.charWidth(*lineP--);
-
-			// Split the header into two lines, and add a '@' prefix
-			// to the second line as well
-			journalString = Common::String(journalString.c_str(), lineP) + "\n@" +
-				Common::String(lineP + 1);
-		}
-
-		// Add a newline at the end of the title
-		journalString += '\n';
-	}
-
-	// If Holmes has something to say first, then take care of it
-	if (!replyOnly) {
-		// Handle the grammar
-		journalString += "Holmes ";
-		if (talk[journalEntry._statementNum]._statement.hasSuffix("?"))
-			journalString += "asked ";
-		else
-			journalString += "said to ";
-
-		switch (talk._talkTo) {
-		case 1:
-			journalString += "me";
-			break;
-		case 2:
-			journalString += "the Inspector";
-			break;
-		default:
-			journalString += people._characters[talk._talkTo]._name;
-			break;
-		}
-		journalString += ", \"";
-
-		// Add the statement
-		journalString += statement._statement;
-	}
-
-	// Handle including the reply
-	bool startOfReply = true;
-	bool ctrlSpace = false;
-	bool commentFlag = false;
-	bool commentJustPrinted = false;
-	const byte *replyP = (const byte *)statement._reply.c_str();
-	const int inspectorId = (IS_SERRATED_SCALPEL) ? 2 : 18;
-
-	while (*replyP) {
-		byte c = *replyP++;
-
-		if (IS_ROSE_TATTOO) {
-			// Ignore commented out data
-			if (c == '/' && *(replyP + 1) == '*') {
-				replyP++;	// skip *
-				while (*replyP++ != '*') {}	// empty loop on purpose
-				replyP++;	// skip /
-				c = *replyP;
-			}
-		}
-
-		// Is it a control character?
-		if (c < opcodes[0]) {
-			// Nope. Set flag for allowing control codes to insert spaces
-			ctrlSpace = true;
-			assert(c >= ' ');
-
-			// Check for embedded comments
-			if (c == '{' || c == '}') {
-
-				// TODO: Rose Tattoo checks if no text was added for the last
-				// comment here. In such a case, the last "XXX said" string is
-				// removed here.
-
-				// Comment characters. If we're starting a comment and there's
-				// already text displayed, add a closing quote
-				if (c == '{' && !startOfReply && !commentJustPrinted)
-					journalString += '"';
-
-				// If a reply isn't just being started, and we didn't just end
-				// a comment (which would have added a line), add a carriage return
-				if (!startOfReply && ((!commentJustPrinted && c == '{') || c == '}'))
-					journalString += '\n';
-				startOfReply = false;
-
-				// Handle setting or clearing comment state
-				if (c == '{') {
-					commentFlag = true;
-					commentJustPrinted = false;
-				} else {
-					commentFlag = false;
-					commentJustPrinted = true;
-				}
-			} else {
-				if (startOfReply) {
-					if (!replyOnly) {
-						journalString += "\"\n";
-
-						if (talk._talkTo == 1)
-							journalString += "I replied, \"";
-						else
-							journalString += "The reply was, \"";
-					} else {
-						if (talk._talkTo == 1)
-							journalString += "I";
-						else if (talk._talkTo == inspectorId)
-							journalString += "The Inspector";
-						else
-							journalString += people._characters[talk._talkTo]._name;
-
-						const byte *strP = replyP + 1;
-						byte v;
-						do {
-							v = *strP++;
-						} while (v && (v < opcodes[0]) && (v != '.') && (v != '!') && (v != '?'));
-
-						if (v == '?')
-							journalString += " asked, \"";
-						else
-							journalString += " said, \"";
-					}
-
-					startOfReply = false;
-				}
-
-				// Copy text from the place until either the reply ends, a comment
-				// {} block is started, or a control character is encountered
-				journalString += c;
-				do {
-					journalString += *replyP++;
-				} while (*replyP && *replyP < opcodes[0] && *replyP != '{' && *replyP != '}');
-
-				commentJustPrinted = false;
-			}
-		} else if (c == opcodes[OP_SWITCH_SPEAKER]) {
-			if (!startOfReply) {
-				if (!commentFlag && !commentJustPrinted)
-					journalString += "\"\n";
-
-				journalString += "Then ";
-				commentFlag = false;
-			} else if (!replyOnly) {
-				journalString += "\"\n";
-			}
-
-			startOfReply = false;
-			c = *replyP++ - 1;
-			if (IS_ROSE_TATTOO)
-				replyP++;
-
-			if (c == 0)
-				journalString += "Holmes";
-			else if (c == 1)
-				journalString += "I";
-			else if (c == inspectorId)
-				journalString += "the Inspector";
-			else
-				journalString += people._characters[c]._name;
-
-			const byte *strP = replyP;
-			byte v;
-			do {
-				v = *strP++;
-			} while (v && v < opcodes[0] && v != '.' && v != '!' && v != '?');
-
-			if (v == '?')
-				journalString += " asked, \"";
-			else
-				journalString += " said, \"";
-		} else {
-			if (IS_SERRATED_SCALPEL) {
-				// Control code, so move past it and any parameters
-				if (c == opcodes[OP_RUN_CANIMATION] ||
-					c == opcodes[OP_ASSIGN_PORTRAIT_LOCATION] ||
-					c == opcodes[OP_PAUSE] ||
-					c == opcodes[OP_PAUSE_WITHOUT_CONTROL] ||
-					c == opcodes[OP_WALK_TO_CANIMATION]) {
-					// These commands have a single parameter
-					++replyP;
-				} else if (c == opcodes[OP_ADJUST_OBJ_SEQUENCE]) {
-					replyP += (replyP[0] & 127) + replyP[1] + 2;
-				} else if (c == opcodes[OP_WALK_TO_COORDS] || c == opcodes[OP_MOVE_MOUSE]) {
-					replyP += 4;
-				} else if (c == opcodes[OP_SET_FLAG] || c == opcodes[OP_IF_STATEMENT]) {
-					replyP += 2;
-				} else if (c == opcodes[OP_SFX_COMMAND] || c == opcodes[OP_PLAY_PROLOGUE] ||
-					c == opcodes[OP_CALL_TALK_FILE]) {
-					replyP += 8;
-					break;
-				} else if (
-					c == opcodes[OP_TOGGLE_OBJECT] ||
-					c == opcodes[OP_ADD_ITEM_TO_INVENTORY] ||
-					c == opcodes[OP_SET_OBJECT] ||
-					c == opcodes[OP_DISPLAY_INFO_LINE] ||
-					c == opcodes[OP_REMOVE_ITEM_FROM_INVENTORY]) {
-					replyP += (*replyP & 127) + 1;
-				} else if (c == opcodes[OP_GOTO_SCENE]) {
-					replyP += 5;
-				} else if (c == opcodes[OP_CARRIAGE_RETURN]) {
-					journalString += "\n";
-				}
-			} else {
-				if (c == opcodes[OP_RUN_CANIMATION] ||
-					c == opcodes[OP_PAUSE] ||
-					c == opcodes[OP_MOUSE_OFF_ON] ||
-					c == opcodes[OP_SET_WALK_CONTROL] ||
-					c == opcodes[OP_PAUSE_WITHOUT_CONTROL] ||
-					c == opcodes[OP_WALK_TO_CANIMATION] ||
-					c == opcodes[OP_TURN_NPC_OFF] ||
-					c == opcodes[OP_TURN_NPC_ON] ||
-					c == opcodes[OP_RESTORE_PEOPLE_SEQUENCE])
-					++replyP;
-				else if (
-					c == opcodes[OP_SET_TALK_SEQUENCE] ||
-					c == opcodes[OP_SET_FLAG] ||
-					c == opcodes[OP_WALK_NPC_TO_CANIM] ||
-					c == opcodes[OP_WALK_HOLMES_AND_NPC_TO_CANIM] ||
-					c == opcodes[OP_NPC_PATH_LABEL] ||
-					c == opcodes[OP_PATH_GOTO_LABEL])
-					replyP += 2;
-				else if (
-					c == opcodes[OP_SET_NPC_PATH_PAUSE] ||
-					c == opcodes[OP_NPC_PATH_PAUSE_TAKING_NOTES] ||
-					c == opcodes[OP_NPC_PATH_PAUSE_LOOKING_HOLMES] ||
-					c == opcodes[OP_NPC_VERB_CANIM])
-					replyP += 3;
-				else if (
-					c == opcodes[OP_SET_SCENE_ENTRY_FLAG] ||
-					c == opcodes[OP_PATH_IF_FLAG_GOTO_LABEL])
-					replyP += 4;
-				else if (
-					c == opcodes[OP_WALK_TO_COORDS])
-					replyP += 5;
-				else if (
-					c == opcodes[OP_WALK_NPC_TO_COORDS] ||
-					c == opcodes[OP_GOTO_SCENE] ||
-					c == opcodes[OP_SET_NPC_PATH_DEST] ||
-					c == opcodes[OP_SET_NPC_POSITION])
-					replyP += 6;
-				else if (
-					c == opcodes[OP_PLAY_SONG] ||
-					c == opcodes[OP_NEXT_SONG])
-					replyP += 8;
-				else if (
-					c == opcodes[OP_CALL_TALK_FILE] ||
-					c == opcodes[OP_SET_NPC_TALK_FILE] ||
-					c == opcodes[OP_NPC_WALK_GRAPHICS])
-					replyP += 9;
-				else if (
-					c == opcodes[OP_NPC_VERB_SCRIPT])
-					replyP += 10;
-				else if (
-					c == opcodes[OP_WALK_HOLMES_AND_NPC_TO_COORDS])
-					replyP += 11;
-				else if (
-					c == opcodes[OP_NPC_VERB] ||
-					c == opcodes[OP_NPC_VERB_TARGET])
-					replyP += 14;
-				else if (
-					c == opcodes[OP_ADJUST_OBJ_SEQUENCE])
-					replyP += (replyP[0] & 127) + replyP[1] + 2;
-				else if (
-					c == opcodes[OP_TOGGLE_OBJECT] ||
-					c == opcodes[OP_ADD_ITEM_TO_INVENTORY] ||
-					c == opcodes[OP_SET_OBJECT] ||
-					c == opcodes[OP_REMOVE_ITEM_FROM_INVENTORY])
-					replyP += (*replyP & 127) + 1;
-				else if (
-					c == opcodes[OP_END_TEXT_WINDOW]) {
-					journalString += '\n';
-				} else if (
-					c == opcodes[OP_NPC_DESC_ON_OFF]) {
-					replyP++;
-					while (replyP[0] && replyP[0] != opcodes[OP_NPC_DESC_ON_OFF])
-						replyP++;
-					replyP++;
-				} else if (
-					c == opcodes[OP_SET_NPC_INFO_LINE])
-					replyP += replyP[1] + 2;
-			}
-
-			// Put a space in the output for a control character, unless it's
-			// immediately coming after another control character
-			if (ctrlSpace && c != opcodes[OP_ASSIGN_PORTRAIT_LOCATION] && c != opcodes[OP_CARRIAGE_RETURN] && 
-					!commentJustPrinted) {
-				journalString += " ";
-				ctrlSpace = false;
-			}
-		}
-	}
-
-	if (!startOfReply && !commentJustPrinted)
-		journalString += '"';
-
-	// Finally finished building the journal text. Need to process the text to
-	// word wrap it to fit on-screen. The resulting lines are stored in the
-	// _lines array
-	_lines.clear();
-
-	while (!journalString.empty()) {
-		const char *startP = journalString.c_str();
-
-		// If the first character is a '@' flagging a title line, then move
-		// past it, so the @ won't be included in the line width calculation
-		if (*startP == '@')
-			++startP;
-
-		// Build up chacters until a full line is found
-		int width = 0;
-		const char *endP = startP;
-		while (width < JOURNAL_MAX_WIDTH && *endP && *endP != '\n' && (endP - startP) < (JOURNAL_MAX_CHARS - 1))
-			width += screen.charWidth(*endP++);
-
-		// If word wrapping, move back to end of prior word
-		if (width >= JOURNAL_MAX_WIDTH || (endP - startP) >= (JOURNAL_MAX_CHARS - 1)) {
-			while (*--endP != ' ')
-				;
-		}
-
-		// Add in the line
-		_lines.push_back(Common::String(journalString.c_str(), endP));
-
-		// Strip line off from string being processed
-		journalString = *endP ? Common::String(endP + 1) : "";
-	}
-
-	// Add a blank line at the end of the text as long as text was present
-	if (!startOfReply) {
-		_lines.push_back("");
-	} else {
-		_lines.clear();
-	}
-}
-
-void Journal::drawJournalFrame() {
-	FixedText &fixedText = *_vm->_fixedText;
-	Resources &res = *_vm->_res;
-	Screen &screen = *_vm->_screen;
-	byte palette[PALETTE_SIZE];
-
-	// Load in the journal background
-	Common::SeekableReadStream *bg = res.load("journal.lbv");
-	bg->read(screen._backBuffer1.getPixels(), SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCREEN_HEIGHT);
-	bg->read(palette, PALETTE_SIZE);
-	delete bg;
-
-	// Translate the palette for display
-	for (int idx = 0; idx < PALETTE_SIZE; ++idx)
-		palette[idx] = VGA_COLOR_TRANS(palette[idx]);
-
-	Common::String fixedText_WatsonsJournal = fixedText.getText(kFixedText_Journal_WatsonsJournal);
-	Common::String fixedText_Exit           = fixedText.getText(kFixedText_Journal_Exit);
-	Common::String fixedText_Back10         = fixedText.getText(kFixedText_Journal_Back10);
-	Common::String fixedText_Up             = fixedText.getText(kFixedText_Journal_Up);
-	Common::String fixedText_Down           = fixedText.getText(kFixedText_Journal_Down);
-	Common::String fixedText_Ahead10        = fixedText.getText(kFixedText_Journal_Ahead10);
-	Common::String fixedText_Search         = fixedText.getText(kFixedText_Journal_Search);
-	Common::String fixedText_FirstPage      = fixedText.getText(kFixedText_Journal_FirstPage);
-	Common::String fixedText_LastPage       = fixedText.getText(kFixedText_Journal_LastPage);
-	Common::String fixedText_PrintText      = fixedText.getText(kFixedText_Journal_PrintText);
-
-	// Set the palette and print the title
-	screen.setPalette(palette);
-	screen.gPrint(Common::Point(111, 18), BUTTON_BOTTOM, "%s", fixedText_WatsonsJournal.c_str());
-	screen.gPrint(Common::Point(110, 17), INV_FOREGROUND, "%s", fixedText_WatsonsJournal.c_str());
-
-	// Draw the buttons
-	screen.makeButton(Common::Rect(JOURNAL_POINTS[0][0], JOURNAL_BUTTONS_Y,
-		JOURNAL_POINTS[0][1], JOURNAL_BUTTONS_Y + 10),
-		JOURNAL_POINTS[0][2] - screen.stringWidth(fixedText_Exit) / 2, fixedText_Exit);
-	screen.makeButton(Common::Rect(JOURNAL_POINTS[1][0], JOURNAL_BUTTONS_Y,
-		JOURNAL_POINTS[1][1], JOURNAL_BUTTONS_Y + 10),
-		JOURNAL_POINTS[1][2] - screen.stringWidth(fixedText_Back10) / 2, fixedText_Back10);
-	screen.makeButton(Common::Rect(JOURNAL_POINTS[2][0], JOURNAL_BUTTONS_Y,
-		JOURNAL_POINTS[2][1], JOURNAL_BUTTONS_Y + 10),
-		JOURNAL_POINTS[2][2] - screen.stringWidth(fixedText_Up) / 2, fixedText_Up);
-	screen.makeButton(Common::Rect(JOURNAL_POINTS[3][0], JOURNAL_BUTTONS_Y,
-		JOURNAL_POINTS[3][1], JOURNAL_BUTTONS_Y + 10),
-		JOURNAL_POINTS[3][2] - screen.stringWidth(fixedText_Down) / 2, fixedText_Down);
-	screen.makeButton(Common::Rect(JOURNAL_POINTS[4][0], JOURNAL_BUTTONS_Y,
-		JOURNAL_POINTS[4][1], JOURNAL_BUTTONS_Y + 10),
-		JOURNAL_POINTS[4][2] - screen.stringWidth(fixedText_Ahead10) / 2, fixedText_Ahead10);
-	screen.makeButton(Common::Rect(JOURNAL_POINTS[5][0], JOURNAL_BUTTONS_Y + 11,
-		JOURNAL_POINTS[5][1], JOURNAL_BUTTONS_Y + 21),
-		JOURNAL_POINTS[5][2] - screen.stringWidth(fixedText_Search) / 2, fixedText_Search);
-	screen.makeButton(Common::Rect(JOURNAL_POINTS[6][0], JOURNAL_BUTTONS_Y + 11,
-		JOURNAL_POINTS[6][1], JOURNAL_BUTTONS_Y + 21),
-		JOURNAL_POINTS[6][2] - screen.stringWidth(fixedText_FirstPage) / 2, fixedText_FirstPage);
-	screen.makeButton(Common::Rect(JOURNAL_POINTS[7][0], JOURNAL_BUTTONS_Y + 11,
-		JOURNAL_POINTS[7][1], JOURNAL_BUTTONS_Y + 21),
-		JOURNAL_POINTS[7][2] - screen.stringWidth(fixedText_LastPage) / 2, fixedText_LastPage);
-
-	// WORKAROUND: Draw Print Text button as disabled, since we don't support it in ScummVM
-	screen.makeButton(Common::Rect(JOURNAL_POINTS[8][0], JOURNAL_BUTTONS_Y + 11,
-		JOURNAL_POINTS[8][1], JOURNAL_BUTTONS_Y + 21),
-		JOURNAL_POINTS[8][2] - screen.stringWidth(fixedText_PrintText) / 2, fixedText_PrintText);
-	screen.buttonPrint(Common::Point(JOURNAL_POINTS[8][2], JOURNAL_BUTTONS_Y + 11),
-		COMMAND_NULL, false, fixedText_PrintText);
-}
-
-void Journal::drawInterface() {
-	Screen &screen = *_vm->_screen;
-
-	drawJournalFrame();
-
-	if (_journal.empty()) {
-		_up = _down = 0;
-	} else {
-		drawJournal(0, 0);
-	}
-
-	doArrows();
-
-	// Show the entire screen
-	screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
-}
-
-void Journal::doArrows() {
-	FixedText &fixedText = *_vm->_fixedText;
-	Screen &screen = *_vm->_screen;
-	byte color;
-
-	Common::String fixedText_Back10         = fixedText.getText(kFixedText_Journal_Back10);
-	Common::String fixedText_Up             = fixedText.getText(kFixedText_Journal_Up);
-	Common::String fixedText_Down           = fixedText.getText(kFixedText_Journal_Down);
-	Common::String fixedText_Ahead10        = fixedText.getText(kFixedText_Journal_Ahead10);
-	Common::String fixedText_Search         = fixedText.getText(kFixedText_Journal_Search);
-	Common::String fixedText_FirstPage      = fixedText.getText(kFixedText_Journal_FirstPage);
-	Common::String fixedText_LastPage       = fixedText.getText(kFixedText_Journal_LastPage);
-	Common::String fixedText_PrintText      = fixedText.getText(kFixedText_Journal_PrintText);
-
-	color = (_page > 1) ? COMMAND_FOREGROUND : COMMAND_NULL;
-	screen.buttonPrint(Common::Point(JOURNAL_POINTS[1][2], JOURNAL_BUTTONS_Y), color, false, fixedText_Back10);
-	screen.buttonPrint(Common::Point(JOURNAL_POINTS[2][2], JOURNAL_BUTTONS_Y), color, false, fixedText_Up);
-
-	color = _down ? COMMAND_FOREGROUND : COMMAND_NULL;
-	screen.buttonPrint(Common::Point(JOURNAL_POINTS[3][2], JOURNAL_BUTTONS_Y), color, false, fixedText_Down);
-	screen.buttonPrint(Common::Point(JOURNAL_POINTS[4][2], JOURNAL_BUTTONS_Y), color, false, fixedText_Ahead10);
-	screen.buttonPrint(Common::Point(JOURNAL_POINTS[7][2], JOURNAL_BUTTONS_Y + 11), color, false, fixedText_LastPage);
-
-	color = _journal.size() > 0 ? COMMAND_FOREGROUND : COMMAND_NULL;
-	screen.buttonPrint(Common::Point(JOURNAL_POINTS[5][2], JOURNAL_BUTTONS_Y + 11), color, false, fixedText_Search);
-	screen.buttonPrint(Common::Point(JOURNAL_POINTS[8][2], JOURNAL_BUTTONS_Y + 11), COMMAND_NULL, false, fixedText_PrintText);
-
-	color = _page > 1 ? COMMAND_FOREGROUND : COMMAND_NULL;
-	screen.buttonPrint(Common::Point(JOURNAL_POINTS[6][2], JOURNAL_BUTTONS_Y + 11), color, false, fixedText_FirstPage);
-}
-
-bool Journal::drawJournal(int direction, int howFar) {
-	Events &events = *_vm->_events;
-	FixedText &fixedText = *_vm->_fixedText;
-	Screen &screen = *_vm->_screen;
-	Talk &talk = *_vm->_talk;
-	int yp = 37;
-	int startPage = _page;
-	bool endJournal = false;
-	bool firstOccurance = true;
-	bool searchSuccessful = false;
-	bool endFlag = false;
-	int lineNum = 0;
-	int savedIndex;
-	int temp;
-	const char *matchP;
-	int width;
-
-	talk._converseNum = -1;
-	_down = true;
-
-	do {
-		// Get the number of lines for the current journal entry
-		loadJournalFile(false);
-		if (_lines.empty()) {
-			// Entry has no text, so it must be a stealth eny. Move onto further journal entries
-			// until an entry with text is found
-			if (++_index == (int)_journal.size()) {
-				endJournal = true;
-			} else {
-				_sub = 0;
-				loadJournalFile(false);
-			}
-		}
-	} while (!endJournal && _lines.empty());
-
-	// Check if there no further pages with text until the end of the journal
-	if (endJournal) {
-		// If moving forward or backwards, clear the page before printing
-		if (direction)
-			drawJournalFrame();
-
-		screen.gPrint(Common::Point(235, 21), PEN_COLOR, "Page %d", _page);
-		return false;
-	}
-
-	// If the journal page is being changed, set the wait cursor
-	if (direction)
-		events.setCursor(WAIT);
-
-	switch (direction) {
-	case 1:
-	case 4:
-		// Move backwards howFar number of lines unless either the start of the journal is reached,
-		// or a searched for keyword is found
-		do {
-			// Move backwards through the journal file a line at a time
-			if (--_sub < 0) {
-				do {
-					if (--_index < 0) {
-						_index = 0;
-						_sub = 0;
-						endJournal = true;
-					}
-					else {
-						loadJournalFile(false);
-						_sub = _lines.size() - 1;
-					}
-				} while (!endJournal && _lines.empty());
-			}
-
-			// If it's search mode, check each line for the given keyword
-			if (direction >= 3 && !_lines.empty() && !endJournal && !searchSuccessful) {
-				Common::String line = _lines[_sub];
-				line.toUppercase();
-				if (strstr(line.c_str(), _find.c_str()) != nullptr) {
-					// Found a match. Reset howFar so that the start of page that the match
-					// was found on will be displayed
-					searchSuccessful = true;
-					howFar = ((lineNum / LINES_PER_PAGE) + 1) * LINES_PER_PAGE;
-				}
-			}
-
-			++lineNum;
-		} while (lineNum < howFar && !endJournal);
-
-		if (!_index && !_sub)
-			_page = 1;
-		else
-			_page -= howFar / LINES_PER_PAGE;
-		break;
-
-	case 2:
-	case 3:
-		// Move howFar lines ahead unless the end of the journal is reached,
-		// or a searched for keyword is found
-		for (temp = 0; (temp < (howFar / LINES_PER_PAGE)) && !endJournal && !searchSuccessful; ++temp) {
-			// Handle animating mouse cursor
-			int cursorNum = (int)events.getCursor() + 1;
-			if (cursorNum >(WAIT + 2))
-				cursorNum = WAIT;
-			events.setCursor((CursorId)cursorNum);
-
-			lineNum = 0;
-			savedIndex = _index;
-			int savedSub = _sub;
-
-			// Move a single page ahead
-			do {
-				// If in search mode, check for keyword
-				if (direction >= 3 && _page != startPage) {
-					Common::String line = _lines[_sub];
-					line.toUppercase();
-					if (strstr(line.c_str(), _find.c_str()) != nullptr)
-						searchSuccessful = true;
-				}
-
-				// Move forwards a line at a time, unless search word was found
-				if (!searchSuccessful) {
-					if (++_sub == (int)_lines.size()) {
-						// Reached end of page
-						do {
-							if (++_index == (int)_journal.size()) {
-								_index = savedIndex;
-								_sub = savedSub;
-								loadJournalFile(false);
-								endJournal = true;
-							} else {
-								_sub = 0;
-								loadJournalFile(false);
-							}
-						} while (!endJournal && _lines.empty());
-					}
-
-					++lineNum;
-				}
-			} while ((lineNum < LINES_PER_PAGE) && !endJournal && !searchSuccessful);
-
-			if (!endJournal && !searchSuccessful)
-				// Move to next page
-				++_page;
-
-			if (searchSuccessful) {
-				// Search found, so show top of the page it was found on
-				_index = savedIndex;
-				_sub = savedSub;
-				loadJournalFile(false);
-			}
-		}
-		break;
-
-	default:
-		break;
-	}
-
-	if (direction) {
-		events.setCursor(ARROW);
-		drawJournalFrame();
-	}
-
-	Common::String fixedText_Page = fixedText.getText(kFixedText_Journal_Page);
-
-	screen.gPrint(Common::Point(235, 21), PEN_COLOR, fixedText_Page.c_str(), _page);
-
-	temp = _sub;
-	savedIndex = _index;
-	lineNum = 0;
-
-	do {
-		bool inc = true;
-
-		// If there wasn't any line to print at the top of the page, we won't need to
-		// increment the y position
-		if (_lines[temp].empty() && yp == 37)
-			inc = false;
-
-		// If there's a searched for keyword in the line, it will need to be highlighted
-		if (searchSuccessful && firstOccurance) {
-			// Check if line has the keyword
-			Common::String line = _lines[temp];
-			line.toUppercase();
-			if ((matchP = strstr(line.c_str(), _find.c_str())) != nullptr) {
-				matchP = _lines[temp].c_str() + (matchP - line.c_str());
-				firstOccurance = false;
-
-				// Print out the start of the line before the matching keyword
-				Common::String lineStart(_lines[temp].c_str(), matchP);
-				if (lineStart.hasPrefix("@")) {
-					width = screen.stringWidth(lineStart.c_str() + 1);
-					screen.gPrint(Common::Point(53, yp), 15, "%s", lineStart.c_str() + 1);
-				} else {
-					width = screen.stringWidth(lineStart.c_str());
-					screen.gPrint(Common::Point(53, yp), PEN_COLOR, "%s", lineStart.c_str());
-				 }
-
-				// Print out the found keyword
-				Common::String lineMatch(matchP, matchP + _find.size());
-				screen.gPrint(Common::Point(53 + width, yp), INV_FOREGROUND, "%s", lineMatch.c_str());
-				width += screen.stringWidth(lineMatch.c_str());
-
-				// Print remainder of line
-				screen.gPrint(Common::Point(53 + width, yp), PEN_COLOR, "%s", matchP + _find.size());
-			} else if (_lines[temp].hasPrefix("@")) {
-				screen.gPrint(Common::Point(53, yp), 15, "%s", _lines[temp].c_str() + 1);
-			} else {
-				screen.gPrint(Common::Point(53, yp), PEN_COLOR, "%s", _lines[temp].c_str());
-			}
-		} else {
-			if (_lines[temp].hasPrefix("@")) {
-				screen.gPrint(Common::Point(53, yp), 15, "%s", _lines[temp].c_str() + 1);
-			} else {
-				screen.gPrint(Common::Point(53, yp), PEN_COLOR, "%s", _lines[temp].c_str());
-			}
-		}
-
-		if (++temp == (int)_lines.size()) {
-			// Move to next page
-			do {
-				if (_index < ((int)_journal.size() - 1) && lineNum < (LINES_PER_PAGE - 1)) {
-					++_index;
-					loadJournalFile(false);
-					temp = 0;
-				} else {
-					if (_index == ((int)_journal.size() - 1))
-						_down = false;
-					endFlag = true;
-				}
-			} while (!endFlag && _lines.empty());
-		}
-
-		if (inc) {
-			// Move to next line
-			++lineNum;
-			yp += 13;
-		}
-	} while (lineNum < LINES_PER_PAGE && !endFlag);
-
-	_index = savedIndex;
-	_up = _index || _sub;
-
-	return direction >= 3 && searchSuccessful;
+Journal *Journal::init(SherlockEngine *vm) {
+	if (vm->getGameID() == GType_SerratedScalpel)
+		return new Scalpel::ScalpelJournal(vm);
+	else
+		return new Tattoo::TattooJournal(vm);
 }
 
-JournalButton Journal::getHighlightedButton(const Common::Point &pt) {
-	if (pt.x > JOURNAL_POINTS[0][0] && pt.x < JOURNAL_POINTS[0][1] && pt.y >= JOURNAL_BUTTONS_Y &&
-			pt.y < (JOURNAL_BUTTONS_Y + 10))
-		return BTN_EXIT;
-
-	if (pt.x > JOURNAL_POINTS[1][0] && pt.x < JOURNAL_POINTS[1][1] && pt.y >= JOURNAL_BUTTONS_Y &&
-			pt.y < (JOURNAL_BUTTONS_Y + 10) && _page > 1)
-		return BTN_BACK10;
-
-	if (pt.x > JOURNAL_POINTS[2][0] && pt.x < JOURNAL_POINTS[2][1] && pt.y >= JOURNAL_BUTTONS_Y &&
-			pt.y < (JOURNAL_BUTTONS_Y + 10) && _up)
-		return BTN_UP;
-
-	if (pt.x > JOURNAL_POINTS[3][0] && pt.x < JOURNAL_POINTS[3][1] && pt.y >= JOURNAL_BUTTONS_Y &&
-			pt.y < (JOURNAL_BUTTONS_Y + 10) && _down)
-		return BTN_DOWN;
-
-	if (pt.x > JOURNAL_POINTS[4][0] && pt.x < JOURNAL_POINTS[4][1] && pt.y >= JOURNAL_BUTTONS_Y &&
-			pt.y < (JOURNAL_BUTTONS_Y + 10) && _down)
-		return BTN_AHEAD110;
-
-	if (pt.x > JOURNAL_POINTS[5][0] && pt.x < JOURNAL_POINTS[5][1] && pt.y >= (JOURNAL_BUTTONS_Y + 11) &&
-			pt.y < (JOURNAL_BUTTONS_Y + 20) && !_journal.empty())
-		return BTN_SEARCH;
-
-	if (pt.x > JOURNAL_POINTS[6][0] && pt.x < JOURNAL_POINTS[6][1] && pt.y >= (JOURNAL_BUTTONS_Y + 11) &&
-			pt.y < (JOURNAL_BUTTONS_Y + 20) && _up)
-		return BTN_FIRST_PAGE;
-
-	if (pt.x > JOURNAL_POINTS[7][0] && pt.x < JOURNAL_POINTS[7][1] && pt.y >= (JOURNAL_BUTTONS_Y + 11) &&
-			pt.y < (JOURNAL_BUTTONS_Y + 20) && _down)
-		return BTN_LAST_PAGE;
-
-	if (pt.x > JOURNAL_POINTS[8][0] && pt.x < JOURNAL_POINTS[8][1] && pt.y >= (JOURNAL_BUTTONS_Y + 11) &&
-			pt.y < (JOURNAL_BUTTONS_Y + 20) && !_journal.empty())
-		return BTN_PRINT_TEXT;
-
-	return BTN_NONE;
-}
-
-bool Journal::handleEvents(int key) {
-	Events    &events    = *_vm->_events;
-	FixedText &fixedText = *_vm->_fixedText;
-	Screen    &screen    = *_vm->_screen;
-	bool doneFlag = false;
-
-	Common::Point pt = events.mousePos();
-	JournalButton btn = getHighlightedButton(pt);
-	byte color;
-
-	if (events._pressed || events._released) {
-		Common::String fixedText_Exit           = fixedText.getText(kFixedText_Journal_Exit);
-		Common::String fixedText_Back10         = fixedText.getText(kFixedText_Journal_Back10);
-		Common::String fixedText_Up             = fixedText.getText(kFixedText_Journal_Up);
-		Common::String fixedText_Down           = fixedText.getText(kFixedText_Journal_Down);
-		Common::String fixedText_Ahead10        = fixedText.getText(kFixedText_Journal_Ahead10);
-		Common::String fixedText_Search         = fixedText.getText(kFixedText_Journal_Search);
-		Common::String fixedText_FirstPage      = fixedText.getText(kFixedText_Journal_FirstPage);
-		Common::String fixedText_LastPage       = fixedText.getText(kFixedText_Journal_LastPage);
-		Common::String fixedText_PrintText      = fixedText.getText(kFixedText_Journal_PrintText);
-
-		// Exit button
-		color = (btn == BTN_EXIT) ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND;
-		screen.buttonPrint(Common::Point(JOURNAL_POINTS[0][2], JOURNAL_BUTTONS_Y), color, true, fixedText_Exit);
-
-		// Back 10 button
-		if (btn == BTN_BACK10) {
-			screen.buttonPrint(Common::Point(JOURNAL_POINTS[1][2], JOURNAL_BUTTONS_Y), COMMAND_HIGHLIGHTED, true, fixedText_Back10);
-		} else if (_page > 1) {
-			screen.buttonPrint(Common::Point(JOURNAL_POINTS[1][2], JOURNAL_BUTTONS_Y), COMMAND_FOREGROUND, true, fixedText_Back10);
-		}
-
-		// Up button
-		if (btn == BTN_UP) {
-			screen.buttonPrint(Common::Point(JOURNAL_POINTS[2][2], JOURNAL_BUTTONS_Y), COMMAND_HIGHLIGHTED, true, fixedText_Up);
-		} else if (_up) {
-			screen.buttonPrint(Common::Point(JOURNAL_POINTS[2][2], JOURNAL_BUTTONS_Y), COMMAND_FOREGROUND, true, fixedText_Up);
-		}
-
-		// Down button
-		if (btn == BTN_DOWN) {
-			screen.buttonPrint(Common::Point(JOURNAL_POINTS[3][2], JOURNAL_BUTTONS_Y), COMMAND_HIGHLIGHTED, true, fixedText_Down);
-		} else if (_down) {
-			screen.buttonPrint(Common::Point(JOURNAL_POINTS[3][2], JOURNAL_BUTTONS_Y), COMMAND_FOREGROUND, true, fixedText_Down);
-		}
-
-		// Ahead 10 button
-		if (btn == BTN_AHEAD110) {
-			screen.buttonPrint(Common::Point(JOURNAL_POINTS[4][2], JOURNAL_BUTTONS_Y), COMMAND_HIGHLIGHTED, true, fixedText_Ahead10);
-		} else if (_down) {
-			screen.buttonPrint(Common::Point(JOURNAL_POINTS[4][2], JOURNAL_BUTTONS_Y), COMMAND_FOREGROUND, true, fixedText_Ahead10);
-		}
-
-		// Search button
-		if (btn == BTN_SEARCH) {
-			color = COMMAND_HIGHLIGHTED;
-		} else if (_journal.empty()) {
-			color = COMMAND_NULL;
-		} else {
-			color = COMMAND_FOREGROUND;
-		}
-		screen.buttonPrint(Common::Point(JOURNAL_POINTS[5][2], JOURNAL_BUTTONS_Y + 11), color, true, fixedText_Search);
-
-		// First Page button
-		if (btn == BTN_FIRST_PAGE) {
-			color = COMMAND_HIGHLIGHTED;
-		} else if (_up) {
-			color = COMMAND_FOREGROUND;
-		} else {
-			color = COMMAND_NULL;
-		}
-		screen.buttonPrint(Common::Point(JOURNAL_POINTS[6][2], JOURNAL_BUTTONS_Y + 11), color, true, fixedText_FirstPage);
-
-		// Last Page button
-		if (btn == BTN_LAST_PAGE) {
-			color = COMMAND_HIGHLIGHTED;
-		} else if (_down) {
-			color = COMMAND_FOREGROUND;
-		} else {
-			color = COMMAND_NULL;
-		}
-		screen.buttonPrint(Common::Point(JOURNAL_POINTS[7][2], JOURNAL_BUTTONS_Y + 11), color, true, fixedText_LastPage);
-
-		// Print Text button
-		screen.buttonPrint(Common::Point(JOURNAL_POINTS[8][2], JOURNAL_BUTTONS_Y + 11), COMMAND_NULL, true, fixedText_PrintText);
-	}
-
-	if (btn == BTN_EXIT && events._released) {
-		// Exit button pressed
-		doneFlag = true;
-
-	} else if (((btn == BTN_BACK10 && events._released) || key == 'B') && (_page > 1)) {
-		// Scrolll up 10 pages
-		if (_page < 11)
-			drawJournal(1, (_page - 1) * LINES_PER_PAGE);
-		else
-			drawJournal(1, 10 * LINES_PER_PAGE);
-
-		doArrows();
-		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
-
-	} else if (((btn == BTN_UP && events._released) || key == 'U') && _up) {
-		// Scroll up
-		drawJournal(1, LINES_PER_PAGE);
-		doArrows();
-		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
-
-	} else if (((btn == BTN_DOWN && events._released) || key == 'D') && _down) {
-		// Scroll down
-		drawJournal(2, LINES_PER_PAGE);
-		doArrows();
-		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
-
-	} else if (((btn == BTN_AHEAD110 && events._released) || key == 'A') && _down) {
-		// Scroll down 10 pages
-		if ((_page + 10) > _maxPage)
-			drawJournal(2, (_maxPage - _page) * LINES_PER_PAGE);
-		else
-			drawJournal(2, 10 * LINES_PER_PAGE);
-
-		doArrows();
-		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
-
-	} else if (((btn == BTN_SEARCH && events._released) || key == 'S') && !_journal.empty()) {
-		screen.buttonPrint(Common::Point(JOURNAL_POINTS[5][2], JOURNAL_BUTTONS_Y + 11), COMMAND_FOREGROUND, true, "Search");
-		bool notFound = false;
-
-		do {
-			int dir;
-			if ((dir = getSearchString(notFound)) != 0) {
-				int savedIndex = _index;
-				int savedSub = _sub;
-				int savedPage = _page;
-
-				if (drawJournal(dir + 2, 1000 * LINES_PER_PAGE) == 0) {
-					_index = savedIndex;
-					_sub = savedSub;
-					_page = savedPage;
-
-					drawJournalFrame();
-					drawJournal(0, 0);
-					notFound = true;
-				} else {
-					doneFlag = true;
-				}
-
-				doArrows();
-				screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
-			} else {
-				doneFlag = true;
-			}
-		} while (!doneFlag);
-		doneFlag = false;
-
-	} else if (((btn == BTN_FIRST_PAGE && events._released) || key == 'F') && _up) {
-		// First page
-		_index = _sub = 0;
-		_up = _down = false;
-		_page = 1;
-
-		drawJournalFrame();
-		drawJournal(0, 0);
-		doArrows();
-		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
-
-	} else if (((btn == BTN_LAST_PAGE && events._released) || key == 'L') && _down) {
-		// Last page
-		if ((_page + 10) > _maxPage)
-			drawJournal(2, (_maxPage - _page) * LINES_PER_PAGE);
-		else
-			drawJournal(2, 1000 * LINES_PER_PAGE);
-
-		doArrows();
-		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
-	}
-
-	events.wait(2);
-
-	return doneFlag;
-}
-
-int Journal::getSearchString(bool printError) {
-	enum Button { BTN_NONE, BTN_EXIT, BTN_BACKWARD, BTN_FORWARD };
-
-	Events    &events    = *_vm->_events;
-	FixedText &fixedText = *_vm->_fixedText;
-	Screen    &screen    = *_vm->_screen;
-	Talk &talk = *_vm->_talk;
-	int xp;
-	int yp = 174;
-	bool flag = false;
-	Common::String name;
-	int done = 0;
-	byte color;
-
-	Common::String fixedText_Exit           = fixedText.getText(kFixedText_JournalSearch_Exit);
-	Common::String fixedText_Backward       = fixedText.getText(kFixedText_JournalSearch_Backward);
-	Common::String fixedText_Forward        = fixedText.getText(kFixedText_JournalSearch_Forward);
-	Common::String fixedText_NotFound       = fixedText.getText(kFixedText_JournalSearch_NotFound);
-
-	// Draw search panel
-	screen.makePanel(Common::Rect(6, 171, 313, 199));
-	screen.makeButton(Common::Rect(SEARCH_POINTS[0][0], yp, SEARCH_POINTS[0][1], yp + 10),
-		SEARCH_POINTS[0][2] - screen.stringWidth(fixedText_Exit) / 2, fixedText_Exit);
-	screen.makeButton(Common::Rect(SEARCH_POINTS[1][0], yp, SEARCH_POINTS[1][1], yp + 10),
-		SEARCH_POINTS[1][2] - screen.stringWidth(fixedText_Backward) / 2, fixedText_Backward);
-	screen.makeButton(Common::Rect(SEARCH_POINTS[2][0], yp, SEARCH_POINTS[2][1], yp + 10),
-		SEARCH_POINTS[2][2] - screen.stringWidth(fixedText_Forward) / 2, fixedText_Forward);
-
-	screen.gPrint(Common::Point(SEARCH_POINTS[0][2] - screen.stringWidth(fixedText_Exit) / 2, yp),
-		COMMAND_HIGHLIGHTED, "%c", fixedText_Exit[0]);
-	screen.gPrint(Common::Point(SEARCH_POINTS[1][2] - screen.stringWidth(fixedText_Backward) / 2, yp),
-		COMMAND_HIGHLIGHTED, "%c", fixedText_Backward[0]);
-	screen.gPrint(Common::Point(SEARCH_POINTS[2][2] - screen.stringWidth(fixedText_Forward) / 2, yp),
-		COMMAND_HIGHLIGHTED, "%c", fixedText_Forward[0]);
-
-	screen.makeField(Common::Rect(12, 185, 307, 196));
-
-	screen.fillRect(Common::Rect(12, 185, 307, 186), BUTTON_BOTTOM);
-	screen.vLine(12, 185, 195, BUTTON_BOTTOM);
-	screen.hLine(13, 195, 306, BUTTON_TOP);
-	screen.hLine(306, 186, 195, BUTTON_TOP);
-
-	if (printError) {
-		screen.gPrint(Common::Point((SHERLOCK_SCREEN_WIDTH - screen.stringWidth(fixedText_NotFound)) / 2, 185),
-			INV_FOREGROUND, "%s", fixedText_NotFound.c_str());
-	} else if (!_find.empty()) {
-		// There's already a search term, display it already
-		screen.gPrint(Common::Point(15, 185), TALK_FOREGROUND, "%s", _find.c_str());
-		name = _find;
-	}
-
-	screen.slamArea(6, 171, 307, 28);
-
-	if (printError) {
-		// Give time for user to see the message
-		events.setButtonState();
-		for (int idx = 0; idx < 40 && !_vm->shouldQuit() && !events.kbHit() && !events._released; ++idx) {
-			events.pollEvents();
-			events.setButtonState();
-			events.wait(2);
-		}
-
-		events.clearKeyboard();
-		screen._backBuffer1.fillRect(Common::Rect(13, 186, 306, 195), BUTTON_MIDDLE);
-
-		if (!_find.empty()) {
-			screen.gPrint(Common::Point(15, 185), TALK_FOREGROUND, "%s", _find.c_str());
-			name = _find;
-		}
-
-		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
-	}
-
-	xp = JOURNAL_SEARCH_LEFT + screen.stringWidth(name);
-	yp = JOURNAL_SEARCH_TOP;
-
-	do {
-		events._released = false;
-		Button found = BTN_NONE;
-
-		while (!_vm->shouldQuit() && !events.kbHit() && !events._released) {
-			found = BTN_NONE;
-			if (talk._talkToAbort)
-				return 0;
-
-			// Check if key or mouse button press has occurred
-			events.setButtonState();
-			Common::Point pt = events.mousePos();
-
-			flag = !flag;
-			screen.vgaBar(Common::Rect(xp, yp, xp + 8, yp + 9), flag ? INV_FOREGROUND : BUTTON_MIDDLE);
-
-			if (events._pressed || events._released) {
-				if (pt.x > SEARCH_POINTS[0][0] && pt.x < SEARCH_POINTS[0][1] && pt.y > 174 && pt.y < 183) {
-					found = BTN_EXIT;
-					color = COMMAND_HIGHLIGHTED;
-				} else {
-					color = COMMAND_FOREGROUND;
-				}
-				screen.print(Common::Point(SEARCH_POINTS[0][2] - screen.stringWidth(fixedText_Exit) / 2, 175), color, "%s", fixedText_Exit.c_str());
-
-				if (pt.x > SEARCH_POINTS[1][0] && pt.x < SEARCH_POINTS[1][1] && pt.y > 174 && pt.y < 183) {
-					found = BTN_BACKWARD;
-					color = COMMAND_HIGHLIGHTED;
-				} else {
-					color = COMMAND_FOREGROUND;
-				}
-				screen.print(Common::Point(SEARCH_POINTS[1][2] - screen.stringWidth(fixedText_Backward) / 2, 175), color, "%s", fixedText_Backward.c_str());
-
-				if (pt.x > SEARCH_POINTS[2][0] && pt.x < SEARCH_POINTS[2][1] && pt.y > 174 && pt.y < 183) {
-					found = BTN_FORWARD;
-					color = COMMAND_HIGHLIGHTED;
-				} else {
-					color = COMMAND_FOREGROUND;
-				}
-				screen.print(Common::Point(SEARCH_POINTS[2][2] - screen.stringWidth(fixedText_Forward) / 2, 175), color, "%s", fixedText_Forward.c_str());
-			}
-
-			events.wait(2);
-		}
-
-		if (events.kbHit()) {
-			Common::KeyState keyState = events.getKey();
-
-			if ((keyState.keycode == Common::KEYCODE_BACKSPACE) && (name.size() > 0)) {
-				screen.vgaBar(Common::Rect(xp - screen.charWidth(name.lastChar()), yp, xp + 8, yp + 9), BUTTON_MIDDLE);
-				xp -= screen.charWidth(name.lastChar());
-				screen.vgaBar(Common::Rect(xp, yp, xp + 8, yp + 9), INV_FOREGROUND);
-				name.deleteLastChar();
-
-			} else  if (keyState.keycode == Common::KEYCODE_RETURN) {
-				done = 1;
-
-			}  else if (keyState.keycode == Common::KEYCODE_ESCAPE) {
-				screen.vgaBar(Common::Rect(xp, yp, xp + 8, yp + 9), BUTTON_MIDDLE);
-				done = -1;
-
-			} else if (keyState.ascii >= ' ' && keyState.ascii <= 'z' && keyState.keycode != Common::KEYCODE_AT &&
-				name.size() < JOURNAL_SEACRH_MAX_CHARS && (xp + screen.charWidth(keyState.ascii)) < JOURNAL_SEARCH_RIGHT) {
-				char ch = toupper(keyState.ascii);
-				screen.vgaBar(Common::Rect(xp, yp, xp + 8, yp + 9), BUTTON_MIDDLE);
-				screen.print(Common::Point(xp, yp), TALK_FOREGROUND, "%c", ch);
-				xp += screen.charWidth(ch);
-				name += ch;
-			}
-		}
-
-		if (events._released) {
-			switch (found) {
-			case BTN_EXIT:
-				done = -1; break;
-			case BTN_BACKWARD:
-				done = 2; break;
-			case BTN_FORWARD:
-				done = 1; break;
-			default:
-				break;
-			}
-		}
-	} while (!done && !_vm->shouldQuit());
-
-	if (done != -1) {
-		_find = name;
-	} else {
-		done = 0;
-	}
-
-	// Redisplay the journal screen
-	drawJournalFrame();
-	drawJournal(0, 0);
-	screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
-
-	return done;
-}
-
-void Journal::resetPosition() {
-	_index = _sub = _up = _down = 0;
-	_page = 1;
-}
-
-void Journal::synchronize(Serializer &s) {
-	s.syncAsSint16LE(_index);
-	s.syncAsSint16LE(_sub);
-	s.syncAsSint16LE(_page);
-	s.syncAsSint16LE(_maxPage);
-
-	int journalCount = _journal.size();
-	s.syncAsUint16LE(journalCount);
-	if (s.isLoading())
-		_journal.resize(journalCount);
-
-	for (uint idx = 0; idx < _journal.size(); ++idx) {
-		JournalEntry &je = _journal[idx];
-
-		s.syncAsSint16LE(je._converseNum);
-		s.syncAsByte(je._replyOnly);
-		s.syncAsSint16LE(je._statementNum);
-	}
+Journal::Journal(SherlockEngine *vm) : _vm(vm) {
 }
 
 } // End of namespace Sherlock
diff --git a/engines/sherlock/journal.h b/engines/sherlock/journal.h
index f164562..de07368 100644
--- a/engines/sherlock/journal.h
+++ b/engines/sherlock/journal.h
@@ -32,106 +32,32 @@
 
 namespace Sherlock {
 
-#define JOURNAL_MAX_WIDTH 230
-#define JOURNAL_MAX_CHARS 80
-
-enum JournalButton {
-	BTN_NONE, BTN_EXIT, BTN_BACK10, BTN_UP, BTN_DOWN, BTN_AHEAD110, BTN_SEARCH,
-	BTN_FIRST_PAGE, BTN_LAST_PAGE, BTN_PRINT_TEXT
-};
-
-
-struct JournalEntry {
-	int _converseNum;
-	bool _replyOnly;
-	int _statementNum;
-
-	JournalEntry() : _converseNum(0), _replyOnly(false), _statementNum(0) {}
-	JournalEntry(int converseNum, int statementNum, bool replyOnly = false) :
-		_converseNum(converseNum), _statementNum(statementNum), _replyOnly(replyOnly) {}
-};
-
 class SherlockEngine;
 
 class Journal {
-private:
+protected:
 	SherlockEngine *_vm;
-	Common::Array<JournalEntry> _journal;
-	Common::StringArray _directory;
-	Common::StringArray _locations;
-	Common::StringArray _lines;
-	int _maxPage;
-	int _index;
-	int _sub;
-	bool _up, _down;
-	int _page;
-	Common::String _find;
-
-	/**
-	 * Load the list of location names that the journal will make reference to
-	 */
-	void loadJournalLocations();
 
-	/**
-	 * Loads the description for the current display index in the journal, and then
-	 * word wraps the result to prepare it for being displayed
-	 * @param alreadyLoaded		Indicates whether the journal file is being loaded for the
-	 *		first time, or being reloaded
-	 */
-	void loadJournalFile(bool alreadyLoaded);
-
-	/**
-	 * Display the arrows that can be used to scroll up and down pages
-	 */
-	void doArrows();
-
-	/**
-	 * Displays a page of the journal at the current index
-	 */
-	bool drawJournal(int direction, int howFar);
-
-	/**
-	 * Show the search submenu and allow the player to enter a search string
-	 */
-	int getSearchString(bool printError);
-
-	/**
-	 * Draw the journal background, frame, and interface buttons
-	 */
-	void drawJournalFrame();
-
-	/**
-	 * Returns the button, if any, that is under the specified position
-	 */
-	JournalButton getHighlightedButton(const Common::Point &pt);
-public:
 	Journal(SherlockEngine *vm);
+public:
+	static Journal *init(SherlockEngine *vm);
+	virtual ~Journal() {}
 
 	/**
 	 * Records statements that are said, in the order which they are said. The player
 	 * can then read the journal to review them
 	 */
-	void record(int converseNum, int statementNum, bool replyOnly = false);
-
-	/**
-	 * Display the journal
-	 */
-	void drawInterface();
-
-	/**
-	 * Handle events whilst the journal is being displayed
-	 */
-	bool handleEvents(int key);
+	virtual void record(int converseNum, int statementNum, bool replyOnly = false) {}
 
 	/**
 	 * Reset viewing position to the start of the journal
 	 */
-	void resetPosition();
+	virtual void resetPosition() {}
 
 	/**
 	 * Synchronize the data for a savegame
 	 */
-	void synchronize(Serializer &s);
+	virtual void synchronize(Serializer &s) = 0;
 };
 
 } // End of namespace Sherlock
diff --git a/engines/sherlock/module.mk b/engines/sherlock/module.mk
index 663fc51..55d3840 100644
--- a/engines/sherlock/module.mk
+++ b/engines/sherlock/module.mk
@@ -8,6 +8,7 @@ MODULE_OBJS = \
 	scalpel/drivers/mt32.o \
 	scalpel/tsage/logo.o \
 	scalpel/tsage/resources.o \
+	scalpel/scalpel_journal.o \
 	scalpel/scalpel_map.o \
 	scalpel/scalpel_people.o \
 	scalpel/scalpel_scene.o \
@@ -15,6 +16,7 @@ MODULE_OBJS = \
 	scalpel/scalpel_user_interface.o \
 	scalpel/settings.o \
 	tattoo/tattoo.o \
+	tattoo/tattoo_journal.o \
 	tattoo/tattoo_map.o \
 	tattoo/tattoo_people.o \
 	tattoo/tattoo_resources.o \
diff --git a/engines/sherlock/scalpel/scalpel_journal.cpp b/engines/sherlock/scalpel/scalpel_journal.cpp
new file mode 100644
index 0000000..f7ef1e2
--- /dev/null
+++ b/engines/sherlock/scalpel/scalpel_journal.cpp
@@ -0,0 +1,1365 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "sherlock/journal.h"
+#include "sherlock/sherlock.h"
+#include "sherlock/scalpel/scalpel_journal.h"
+#include "sherlock/tattoo/tattoo_journal.h"
+
+namespace Sherlock {
+
+namespace Scalpel {
+
+#define JOURNAL_BUTTONS_Y 178
+#define LINES_PER_PAGE 11
+#define JOURNAL_SEARCH_LEFT 15
+#define JOURNAL_SEARCH_TOP 186
+#define JOURNAL_SEARCH_RIGHT 296
+#define JOURNAL_SEACRH_MAX_CHARS 50
+
+// Positioning of buttons in the journal view
+static const int JOURNAL_POINTS[9][3] = {
+	{ 6, 68, 37 },
+	{ 69, 131, 100 },
+	{ 132, 192, 162 },
+	{ 193, 250, 221 },
+	{ 251, 313, 281 },
+	{ 6, 82, 44 },
+	{ 83, 159, 121 },
+	{ 160, 236, 198 },
+	{ 237, 313, 275 }
+};
+
+static const int SEARCH_POINTS[3][3] = {
+	{ 51, 123, 86 },
+	{ 124, 196, 159 },
+	{ 197, 269, 232 }
+};
+
+/*----------------------------------------------------------------*/
+
+ScalpelJournal::ScalpelJournal(SherlockEngine *vm) : Journal(vm) {
+	// Initialize fields
+	_maxPage = 0;
+	_index = 0;
+	_sub = 0;
+	_up = _down = false;
+	_page = 1;
+
+	if (_vm->_interactiveFl) {
+		// Load the journal directory and location names
+		loadJournalLocations();
+	}
+}
+
+void ScalpelJournal::record(int converseNum, int statementNum, bool replyOnly) {
+	int saveIndex = _index;
+	int saveSub = _sub;
+
+	if (IS_3DO) {
+		// there seems to be no journal in the 3DO version
+		return;
+	}
+
+	// Record the entry into the list
+	_journal.push_back(JournalEntry(converseNum, statementNum, replyOnly));
+	_index = _journal.size() - 1;
+
+	// Load the text for the new entry to get the number of lines it will have
+	loadJournalFile(true);
+
+	// Restore old state
+	_index = saveIndex;
+	_sub = saveSub;
+
+	// If new lines were added to the ournal, update the total number of lines
+	// the journal continues
+	if (!_lines.empty()) {
+		_maxPage += _lines.size();
+	} else {
+		// No lines in entry, so remove the new entry from the journal
+		_journal.remove_at(_journal.size() - 1);
+	}
+}
+
+void ScalpelJournal::loadJournalLocations() {
+	Resources &res = *_vm->_res;
+
+	_directory.clear();
+
+	Common::SeekableReadStream *dir = res.load("talk.lib");
+	dir->skip(4);		// Skip header
+
+	// Get the numer of entries
+	_directory.resize(dir->readUint16LE());
+	if (IS_ROSE_TATTOO)
+		dir->seek((_directory.size() + 1) * 8, SEEK_CUR);
+
+	// Read in each entry
+	char buffer[17];
+	for (uint idx = 0; idx < _directory.size(); ++idx) {
+		dir->read(buffer, 17);
+		buffer[16] = '\0';
+
+		_directory[idx] = Common::String(buffer);
+	}
+
+	delete dir;
+
+	_locations.clear();
+
+	if (IS_3DO) {
+		// 3DO: storage of locations is currently unknown TODO
+		return;
+	}
+
+	// Load in the locations stored in journal.txt
+	Common::SeekableReadStream *loc = res.load("journal.txt");
+
+	if (IS_SERRATED_SCALPEL) {
+		while (loc->pos() < loc->size()) {
+			Common::String line;
+			char c;
+			while ((c = loc->readByte()) != 0)
+				line += c;
+
+			_locations.push_back(line);
+		}
+	} else {
+		// Initialize locations
+		_locations.resize(100);
+		for (int i = 0; i < 100; i++)
+			_locations[i] = "No Description";
+
+		while (loc->pos() < loc->size()) {
+			// In Rose Tattoo, each location line starts with the location
+			// number, followed by a dot, some spaces and its description
+			// in quotes
+			Common::String line = loc->readLine();
+			Common::String locNumStr;
+			int locNum = 0;
+			int i = 0;
+			Common::String locDesc;
+
+			// Get the location
+			while (Common::isDigit(line[i])) {
+				locNumStr += line[i];
+				i++;
+			}
+			locNum = atoi(locNumStr.c_str());
+
+			// Skip the dot, spaces and initial quotation mark
+			while (line[i] == ' ' || line[i] == '.' || line[i] == '\"')
+				i++;
+
+			do {
+				locDesc += line[i];
+				i++;
+			} while (line[i] != '\"');
+
+			_locations[locNum] = locDesc;
+		}
+	}
+
+	delete loc;
+}
+
+void ScalpelJournal::loadJournalFile(bool alreadyLoaded) {
+	People &people = *_vm->_people;
+	Screen &screen = *_vm->_screen;
+	Talk &talk = *_vm->_talk;
+	JournalEntry &journalEntry = _journal[_index];
+	const byte *opcodes = talk._opcodes;
+
+	Common::String dirFilename = _directory[journalEntry._converseNum];
+	bool replyOnly = journalEntry._replyOnly;
+
+	// Get the location number from within the filename
+	Common::String locStr(dirFilename.c_str() + 4, dirFilename.c_str() + 6);
+	int newLocation = atoi(locStr.c_str());
+
+	// If not flagged as already loaded, load the conversation into script variables
+	if (!alreadyLoaded) {
+		// See if the file to be used is already loaded
+		if (journalEntry._converseNum != talk._converseNum) {
+			// Nope. Free any previously loaded talk
+			talk.freeTalkVars();
+
+			// Find the person being referred to
+			talk._talkTo = -1;
+			for (int idx = 0; idx < (int)people._characters.size(); ++idx) {
+				Common::String portrait = people._characters[idx]._portrait;
+				Common::String numStr(portrait.c_str(), portrait.c_str() + 4);
+
+				if (locStr == numStr) {
+					talk._talkTo = idx;
+					break;
+				}
+			}
+
+			// Load their talk file
+			talk.loadTalkFile(dirFilename);
+		}
+	}
+
+	if (talk[0]._statement.hasPrefix("*") || talk[0]._statement.hasPrefix("^"))
+		replyOnly = true;
+
+	// If this isn't the first journal entry, see if the previous journal entry
+	// was in the same scene to see if we need to include the scene header
+	int oldLocation = -1;
+	if (_index != 0) {
+		// Get the scene number of the prior journal entry
+		Common::String priorEntry = _directory[_journal[_index - 1]._converseNum];
+		oldLocation = atoi(Common::String(priorEntry.c_str() + 4, priorEntry.c_str() + 6).c_str());
+	}
+
+	// Start building journal string
+	Statement &statement = talk[journalEntry._statementNum];
+	Common::String journalString;
+
+	if (newLocation != oldLocation) {
+		// Add in scene title
+		journalString = "@";
+		if (IS_SERRATED_SCALPEL || newLocation - 1 < 100)
+			journalString += _locations[newLocation - 1];
+		journalString += ":";
+
+		// See if title can fit into a single line, or requires splitting on 2 lines
+		int width = screen.stringWidth(journalString.c_str() + 1);
+		if (width > JOURNAL_MAX_WIDTH) {
+			// Scan backwards from end of title to find a space between a word
+			// where the width is less than the maximum allowed for the line
+			const char *lineP = journalString.c_str() + journalString.size() - 1;
+			while (width > JOURNAL_MAX_WIDTH || *lineP != ' ')
+				width -= screen.charWidth(*lineP--);
+
+			// Split the header into two lines, and add a '@' prefix
+			// to the second line as well
+			journalString = Common::String(journalString.c_str(), lineP) + "\n@" +
+				Common::String(lineP + 1);
+		}
+
+		// Add a newline at the end of the title
+		journalString += '\n';
+	}
+
+	// If Holmes has something to say first, then take care of it
+	if (!replyOnly) {
+		// Handle the grammar
+		journalString += "Holmes ";
+		if (talk[journalEntry._statementNum]._statement.hasSuffix("?"))
+			journalString += "asked ";
+		else
+			journalString += "said to ";
+
+		switch (talk._talkTo) {
+		case 1:
+			journalString += "me";
+			break;
+		case 2:
+			journalString += "the Inspector";
+			break;
+		default:
+			journalString += people._characters[talk._talkTo]._name;
+			break;
+		}
+		journalString += ", \"";
+
+		// Add the statement
+		journalString += statement._statement;
+	}
+
+	// Handle including the reply
+	bool startOfReply = true;
+	bool ctrlSpace = false;
+	bool commentFlag = false;
+	bool commentJustPrinted = false;
+	const byte *replyP = (const byte *)statement._reply.c_str();
+	const int inspectorId = (IS_SERRATED_SCALPEL) ? 2 : 18;
+
+	while (*replyP) {
+		byte c = *replyP++;
+
+		if (IS_ROSE_TATTOO) {
+			// Ignore commented out data
+			if (c == '/' && *(replyP + 1) == '*') {
+				replyP++;	// skip *
+				while (*replyP++ != '*') {}	// empty loop on purpose
+				replyP++;	// skip /
+				c = *replyP;
+			}
+		}
+
+		// Is it a control character?
+		if (c < opcodes[0]) {
+			// Nope. Set flag for allowing control codes to insert spaces
+			ctrlSpace = true;
+			assert(c >= ' ');
+
+			// Check for embedded comments
+			if (c == '{' || c == '}') {
+
+				// TODO: Rose Tattoo checks if no text was added for the last
+				// comment here. In such a case, the last "XXX said" string is
+				// removed here.
+
+				// Comment characters. If we're starting a comment and there's
+				// already text displayed, add a closing quote
+				if (c == '{' && !startOfReply && !commentJustPrinted)
+					journalString += '"';
+
+				// If a reply isn't just being started, and we didn't just end
+				// a comment (which would have added a line), add a carriage return
+				if (!startOfReply && ((!commentJustPrinted && c == '{') || c == '}'))
+					journalString += '\n';
+				startOfReply = false;
+
+				// Handle setting or clearing comment state
+				if (c == '{') {
+					commentFlag = true;
+					commentJustPrinted = false;
+				} else {
+					commentFlag = false;
+					commentJustPrinted = true;
+				}
+			} else {
+				if (startOfReply) {
+					if (!replyOnly) {
+						journalString += "\"\n";
+
+						if (talk._talkTo == 1)
+							journalString += "I replied, \"";
+						else
+							journalString += "The reply was, \"";
+					} else {
+						if (talk._talkTo == 1)
+							journalString += "I";
+						else if (talk._talkTo == inspectorId)
+							journalString += "The Inspector";
+						else
+							journalString += people._characters[talk._talkTo]._name;
+
+						const byte *strP = replyP + 1;
+						byte v;
+						do {
+							v = *strP++;
+						} while (v && (v < opcodes[0]) && (v != '.') && (v != '!') && (v != '?'));
+
+						if (v == '?')
+							journalString += " asked, \"";
+						else
+							journalString += " said, \"";
+					}
+
+					startOfReply = false;
+				}
+
+				// Copy text from the place until either the reply ends, a comment
+				// {} block is started, or a control character is encountered
+				journalString += c;
+				do {
+					journalString += *replyP++;
+				} while (*replyP && *replyP < opcodes[0] && *replyP != '{' && *replyP != '}');
+
+				commentJustPrinted = false;
+			}
+		} else if (c == opcodes[OP_SWITCH_SPEAKER]) {
+			if (!startOfReply) {
+				if (!commentFlag && !commentJustPrinted)
+					journalString += "\"\n";
+
+				journalString += "Then ";
+				commentFlag = false;
+			} else if (!replyOnly) {
+				journalString += "\"\n";
+			}
+
+			startOfReply = false;
+			c = *replyP++ - 1;
+			if (IS_ROSE_TATTOO)
+				replyP++;
+
+			if (c == 0)
+				journalString += "Holmes";
+			else if (c == 1)
+				journalString += "I";
+			else if (c == inspectorId)
+				journalString += "the Inspector";
+			else
+				journalString += people._characters[c]._name;
+
+			const byte *strP = replyP;
+			byte v;
+			do {
+				v = *strP++;
+			} while (v && v < opcodes[0] && v != '.' && v != '!' && v != '?');
+
+			if (v == '?')
+				journalString += " asked, \"";
+			else
+				journalString += " said, \"";
+		} else {
+			if (IS_SERRATED_SCALPEL) {
+				// Control code, so move past it and any parameters
+				if (c == opcodes[OP_RUN_CANIMATION] ||
+					c == opcodes[OP_ASSIGN_PORTRAIT_LOCATION] ||
+					c == opcodes[OP_PAUSE] ||
+					c == opcodes[OP_PAUSE_WITHOUT_CONTROL] ||
+					c == opcodes[OP_WALK_TO_CANIMATION]) {
+					// These commands have a single parameter
+					++replyP;
+				} else if (c == opcodes[OP_ADJUST_OBJ_SEQUENCE]) {
+					replyP += (replyP[0] & 127) + replyP[1] + 2;
+				} else if (c == opcodes[OP_WALK_TO_COORDS] || c == opcodes[OP_MOVE_MOUSE]) {
+					replyP += 4;
+				} else if (c == opcodes[OP_SET_FLAG] || c == opcodes[OP_IF_STATEMENT]) {
+					replyP += 2;
+				} else if (c == opcodes[OP_SFX_COMMAND] || c == opcodes[OP_PLAY_PROLOGUE] ||
+					c == opcodes[OP_CALL_TALK_FILE]) {
+					replyP += 8;
+					break;
+				} else if (
+					c == opcodes[OP_TOGGLE_OBJECT] ||
+					c == opcodes[OP_ADD_ITEM_TO_INVENTORY] ||
+					c == opcodes[OP_SET_OBJECT] ||
+					c == opcodes[OP_DISPLAY_INFO_LINE] ||
+					c == opcodes[OP_REMOVE_ITEM_FROM_INVENTORY]) {
+					replyP += (*replyP & 127) + 1;
+				} else if (c == opcodes[OP_GOTO_SCENE]) {
+					replyP += 5;
+				} else if (c == opcodes[OP_CARRIAGE_RETURN]) {
+					journalString += "\n";
+				}
+			} else {
+				if (c == opcodes[OP_RUN_CANIMATION] ||
+					c == opcodes[OP_PAUSE] ||
+					c == opcodes[OP_MOUSE_OFF_ON] ||
+					c == opcodes[OP_SET_WALK_CONTROL] ||
+					c == opcodes[OP_PAUSE_WITHOUT_CONTROL] ||
+					c == opcodes[OP_WALK_TO_CANIMATION] ||
+					c == opcodes[OP_TURN_NPC_OFF] ||
+					c == opcodes[OP_TURN_NPC_ON] ||
+					c == opcodes[OP_RESTORE_PEOPLE_SEQUENCE])
+					++replyP;
+				else if (
+					c == opcodes[OP_SET_TALK_SEQUENCE] ||
+					c == opcodes[OP_SET_FLAG] ||
+					c == opcodes[OP_WALK_NPC_TO_CANIM] ||
+					c == opcodes[OP_WALK_HOLMES_AND_NPC_TO_CANIM] ||
+					c == opcodes[OP_NPC_PATH_LABEL] ||
+					c == opcodes[OP_PATH_GOTO_LABEL])
+					replyP += 2;
+				else if (
+					c == opcodes[OP_SET_NPC_PATH_PAUSE] ||
+					c == opcodes[OP_NPC_PATH_PAUSE_TAKING_NOTES] ||
+					c == opcodes[OP_NPC_PATH_PAUSE_LOOKING_HOLMES] ||
+					c == opcodes[OP_NPC_VERB_CANIM])
+					replyP += 3;
+				else if (
+					c == opcodes[OP_SET_SCENE_ENTRY_FLAG] ||
+					c == opcodes[OP_PATH_IF_FLAG_GOTO_LABEL])
+					replyP += 4;
+				else if (
+					c == opcodes[OP_WALK_TO_COORDS])
+					replyP += 5;
+				else if (
+					c == opcodes[OP_WALK_NPC_TO_COORDS] ||
+					c == opcodes[OP_GOTO_SCENE] ||
+					c == opcodes[OP_SET_NPC_PATH_DEST] ||
+					c == opcodes[OP_SET_NPC_POSITION])
+					replyP += 6;
+				else if (
+					c == opcodes[OP_PLAY_SONG] ||
+					c == opcodes[OP_NEXT_SONG])
+					replyP += 8;
+				else if (
+					c == opcodes[OP_CALL_TALK_FILE] ||
+					c == opcodes[OP_SET_NPC_TALK_FILE] ||
+					c == opcodes[OP_NPC_WALK_GRAPHICS])
+					replyP += 9;
+				else if (
+					c == opcodes[OP_NPC_VERB_SCRIPT])
+					replyP += 10;
+				else if (
+					c == opcodes[OP_WALK_HOLMES_AND_NPC_TO_COORDS])
+					replyP += 11;
+				else if (
+					c == opcodes[OP_NPC_VERB] ||
+					c == opcodes[OP_NPC_VERB_TARGET])
+					replyP += 14;
+				else if (
+					c == opcodes[OP_ADJUST_OBJ_SEQUENCE])
+					replyP += (replyP[0] & 127) + replyP[1] + 2;
+				else if (
+					c == opcodes[OP_TOGGLE_OBJECT] ||
+					c == opcodes[OP_ADD_ITEM_TO_INVENTORY] ||
+					c == opcodes[OP_SET_OBJECT] ||
+					c == opcodes[OP_REMOVE_ITEM_FROM_INVENTORY])
+					replyP += (*replyP & 127) + 1;
+				else if (
+					c == opcodes[OP_END_TEXT_WINDOW]) {
+					journalString += '\n';
+				} else if (
+					c == opcodes[OP_NPC_DESC_ON_OFF]) {
+					replyP++;
+					while (replyP[0] && replyP[0] != opcodes[OP_NPC_DESC_ON_OFF])
+						replyP++;
+					replyP++;
+				} else if (
+					c == opcodes[OP_SET_NPC_INFO_LINE])
+					replyP += replyP[1] + 2;
+			}
+
+			// Put a space in the output for a control character, unless it's
+			// immediately coming after another control character
+			if (ctrlSpace && c != opcodes[OP_ASSIGN_PORTRAIT_LOCATION] && c != opcodes[OP_CARRIAGE_RETURN] && 
+					!commentJustPrinted) {
+				journalString += " ";
+				ctrlSpace = false;
+			}
+		}
+	}
+
+	if (!startOfReply && !commentJustPrinted)
+		journalString += '"';
+
+	// Finally finished building the journal text. Need to process the text to
+	// word wrap it to fit on-screen. The resulting lines are stored in the
+	// _lines array
+	_lines.clear();
+
+	while (!journalString.empty()) {
+		const char *startP = journalString.c_str();
+
+		// If the first character is a '@' flagging a title line, then move
+		// past it, so the @ won't be included in the line width calculation
+		if (*startP == '@')
+			++startP;
+
+		// Build up chacters until a full line is found
+		int width = 0;
+		const char *endP = startP;
+		while (width < JOURNAL_MAX_WIDTH && *endP && *endP != '\n' && (endP - startP) < (JOURNAL_MAX_CHARS - 1))
+			width += screen.charWidth(*endP++);
+
+		// If word wrapping, move back to end of prior word
+		if (width >= JOURNAL_MAX_WIDTH || (endP - startP) >= (JOURNAL_MAX_CHARS - 1)) {
+			while (*--endP != ' ')
+				;
+		}
+
+		// Add in the line
+		_lines.push_back(Common::String(journalString.c_str(), endP));
+
+		// Strip line off from string being processed
+		journalString = *endP ? Common::String(endP + 1) : "";
+	}
+
+	// Add a blank line at the end of the text as long as text was present
+	if (!startOfReply) {
+		_lines.push_back("");
+	} else {
+		_lines.clear();
+	}
+}
+
+void ScalpelJournal::drawJournalFrame() {
+	FixedText &fixedText = *_vm->_fixedText;
+	Resources &res = *_vm->_res;
+	Screen &screen = *_vm->_screen;
+	byte palette[PALETTE_SIZE];
+
+	// Load in the journal background
+	Common::SeekableReadStream *bg = res.load("journal.lbv");
+	bg->read(screen._backBuffer1.getPixels(), SHERLOCK_SCREEN_WIDTH * SHERLOCK_SCREEN_HEIGHT);
+	bg->read(palette, PALETTE_SIZE);
+	delete bg;
+
+	// Translate the palette for display
+	for (int idx = 0; idx < PALETTE_SIZE; ++idx)
+		palette[idx] = VGA_COLOR_TRANS(palette[idx]);
+
+	Common::String fixedText_WatsonsJournal = fixedText.getText(kFixedText_Journal_WatsonsJournal);
+	Common::String fixedText_Exit           = fixedText.getText(kFixedText_Journal_Exit);
+	Common::String fixedText_Back10         = fixedText.getText(kFixedText_Journal_Back10);
+	Common::String fixedText_Up             = fixedText.getText(kFixedText_Journal_Up);
+	Common::String fixedText_Down           = fixedText.getText(kFixedText_Journal_Down);
+	Common::String fixedText_Ahead10        = fixedText.getText(kFixedText_Journal_Ahead10);
+	Common::String fixedText_Search         = fixedText.getText(kFixedText_Journal_Search);
+	Common::String fixedText_FirstPage      = fixedText.getText(kFixedText_Journal_FirstPage);
+	Common::String fixedText_LastPage       = fixedText.getText(kFixedText_Journal_LastPage);
+	Common::String fixedText_PrintText      = fixedText.getText(kFixedText_Journal_PrintText);
+
+	// Set the palette and print the title
+	screen.setPalette(palette);
+	screen.gPrint(Common::Point(111, 18), BUTTON_BOTTOM, "%s", fixedText_WatsonsJournal.c_str());
+	screen.gPrint(Common::Point(110, 17), INV_FOREGROUND, "%s", fixedText_WatsonsJournal.c_str());
+
+	// Draw the buttons
+	screen.makeButton(Common::Rect(JOURNAL_POINTS[0][0], JOURNAL_BUTTONS_Y,
+		JOURNAL_POINTS[0][1], JOURNAL_BUTTONS_Y + 10),
+		JOURNAL_POINTS[0][2] - screen.stringWidth(fixedText_Exit) / 2, fixedText_Exit);
+	screen.makeButton(Common::Rect(JOURNAL_POINTS[1][0], JOURNAL_BUTTONS_Y,
+		JOURNAL_POINTS[1][1], JOURNAL_BUTTONS_Y + 10),
+		JOURNAL_POINTS[1][2] - screen.stringWidth(fixedText_Back10) / 2, fixedText_Back10);
+	screen.makeButton(Common::Rect(JOURNAL_POINTS[2][0], JOURNAL_BUTTONS_Y,
+		JOURNAL_POINTS[2][1], JOURNAL_BUTTONS_Y + 10),
+		JOURNAL_POINTS[2][2] - screen.stringWidth(fixedText_Up) / 2, fixedText_Up);
+	screen.makeButton(Common::Rect(JOURNAL_POINTS[3][0], JOURNAL_BUTTONS_Y,
+		JOURNAL_POINTS[3][1], JOURNAL_BUTTONS_Y + 10),
+		JOURNAL_POINTS[3][2] - screen.stringWidth(fixedText_Down) / 2, fixedText_Down);
+	screen.makeButton(Common::Rect(JOURNAL_POINTS[4][0], JOURNAL_BUTTONS_Y,
+		JOURNAL_POINTS[4][1], JOURNAL_BUTTONS_Y + 10),
+		JOURNAL_POINTS[4][2] - screen.stringWidth(fixedText_Ahead10) / 2, fixedText_Ahead10);
+	screen.makeButton(Common::Rect(JOURNAL_POINTS[5][0], JOURNAL_BUTTONS_Y + 11,
+		JOURNAL_POINTS[5][1], JOURNAL_BUTTONS_Y + 21),
+		JOURNAL_POINTS[5][2] - screen.stringWidth(fixedText_Search) / 2, fixedText_Search);
+	screen.makeButton(Common::Rect(JOURNAL_POINTS[6][0], JOURNAL_BUTTONS_Y + 11,
+		JOURNAL_POINTS[6][1], JOURNAL_BUTTONS_Y + 21),
+		JOURNAL_POINTS[6][2] - screen.stringWidth(fixedText_FirstPage) / 2, fixedText_FirstPage);
+	screen.makeButton(Common::Rect(JOURNAL_POINTS[7][0], JOURNAL_BUTTONS_Y + 11,
+		JOURNAL_POINTS[7][1], JOURNAL_BUTTONS_Y + 21),
+		JOURNAL_POINTS[7][2] - screen.stringWidth(fixedText_LastPage) / 2, fixedText_LastPage);
+
+	// WORKAROUND: Draw Print Text button as disabled, since we don't support it in ScummVM
+	screen.makeButton(Common::Rect(JOURNAL_POINTS[8][0], JOURNAL_BUTTONS_Y + 11,
+		JOURNAL_POINTS[8][1], JOURNAL_BUTTONS_Y + 21),
+		JOURNAL_POINTS[8][2] - screen.stringWidth(fixedText_PrintText) / 2, fixedText_PrintText);
+	screen.buttonPrint(Common::Point(JOURNAL_POINTS[8][2], JOURNAL_BUTTONS_Y + 11),
+		COMMAND_NULL, false, fixedText_PrintText);
+}
+
+void ScalpelJournal::drawInterface() {
+	Screen &screen = *_vm->_screen;
+
+	drawJournalFrame();
+
+	if (_journal.empty()) {
+		_up = _down = 0;
+	} else {
+		drawJournal(0, 0);
+	}
+
+	doArrows();
+
+	// Show the entire screen
+	screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
+}
+
+void ScalpelJournal::doArrows() {
+	FixedText &fixedText = *_vm->_fixedText;
+	Screen &screen = *_vm->_screen;
+	byte color;
+
+	Common::String fixedText_Back10         = fixedText.getText(kFixedText_Journal_Back10);
+	Common::String fixedText_Up             = fixedText.getText(kFixedText_Journal_Up);
+	Common::String fixedText_Down           = fixedText.getText(kFixedText_Journal_Down);
+	Common::String fixedText_Ahead10        = fixedText.getText(kFixedText_Journal_Ahead10);
+	Common::String fixedText_Search         = fixedText.getText(kFixedText_Journal_Search);
+	Common::String fixedText_FirstPage      = fixedText.getText(kFixedText_Journal_FirstPage);
+	Common::String fixedText_LastPage       = fixedText.getText(kFixedText_Journal_LastPage);
+	Common::String fixedText_PrintText      = fixedText.getText(kFixedText_Journal_PrintText);
+
+	color = (_page > 1) ? COMMAND_FOREGROUND : COMMAND_NULL;
+	screen.buttonPrint(Common::Point(JOURNAL_POINTS[1][2], JOURNAL_BUTTONS_Y), color, false, fixedText_Back10);
+	screen.buttonPrint(Common::Point(JOURNAL_POINTS[2][2], JOURNAL_BUTTONS_Y), color, false, fixedText_Up);
+
+	color = _down ? COMMAND_FOREGROUND : COMMAND_NULL;
+	screen.buttonPrint(Common::Point(JOURNAL_POINTS[3][2], JOURNAL_BUTTONS_Y), color, false, fixedText_Down);
+	screen.buttonPrint(Common::Point(JOURNAL_POINTS[4][2], JOURNAL_BUTTONS_Y), color, false, fixedText_Ahead10);
+	screen.buttonPrint(Common::Point(JOURNAL_POINTS[7][2], JOURNAL_BUTTONS_Y + 11), color, false, fixedText_LastPage);
+
+	color = _journal.size() > 0 ? COMMAND_FOREGROUND : COMMAND_NULL;
+	screen.buttonPrint(Common::Point(JOURNAL_POINTS[5][2], JOURNAL_BUTTONS_Y + 11), color, false, fixedText_Search);
+	screen.buttonPrint(Common::Point(JOURNAL_POINTS[8][2], JOURNAL_BUTTONS_Y + 11), COMMAND_NULL, false, fixedText_PrintText);
+
+	color = _page > 1 ? COMMAND_FOREGROUND : COMMAND_NULL;
+	screen.buttonPrint(Common::Point(JOURNAL_POINTS[6][2], JOURNAL_BUTTONS_Y + 11), color, false, fixedText_FirstPage);
+}
+
+bool ScalpelJournal::drawJournal(int direction, int howFar) {
+	Events &events = *_vm->_events;
+	FixedText &fixedText = *_vm->_fixedText;
+	Screen &screen = *_vm->_screen;
+	Talk &talk = *_vm->_talk;
+	int yp = 37;
+	int startPage = _page;
+	bool endJournal = false;
+	bool firstOccurance = true;
+	bool searchSuccessful = false;
+	bool endFlag = false;
+	int lineNum = 0;
+	int savedIndex;
+	int temp;
+	const char *matchP;
+	int width;
+
+	talk._converseNum = -1;
+	_down = true;
+
+	do {
+		// Get the number of lines for the current journal entry
+		loadJournalFile(false);
+		if (_lines.empty()) {
+			// Entry has no text, so it must be a stealth eny. Move onto further journal entries
+			// until an entry with text is found
+			if (++_index == (int)_journal.size()) {
+				endJournal = true;
+			} else {
+				_sub = 0;
+				loadJournalFile(false);
+			}
+		}
+	} while (!endJournal && _lines.empty());
+
+	// Check if there no further pages with text until the end of the journal
+	if (endJournal) {
+		// If moving forward or backwards, clear the page before printing
+		if (direction)
+			drawJournalFrame();
+
+		screen.gPrint(Common::Point(235, 21), PEN_COLOR, "Page %d", _page);
+		return false;
+	}
+
+	// If the journal page is being changed, set the wait cursor
+	if (direction)
+		events.setCursor(WAIT);
+
+	switch (direction) {
+	case 1:
+	case 4:
+		// Move backwards howFar number of lines unless either the start of the journal is reached,
+		// or a searched for keyword is found
+		do {
+			// Move backwards through the journal file a line at a time
+			if (--_sub < 0) {
+				do {
+					if (--_index < 0) {
+						_index = 0;
+						_sub = 0;
+						endJournal = true;
+					}
+					else {
+						loadJournalFile(false);
+						_sub = _lines.size() - 1;
+					}
+				} while (!endJournal && _lines.empty());
+			}
+
+			// If it's search mode, check each line for the given keyword
+			if (direction >= 3 && !_lines.empty() && !endJournal && !searchSuccessful) {
+				Common::String line = _lines[_sub];
+				line.toUppercase();
+				if (strstr(line.c_str(), _find.c_str()) != nullptr) {
+					// Found a match. Reset howFar so that the start of page that the match
+					// was found on will be displayed
+					searchSuccessful = true;
+					howFar = ((lineNum / LINES_PER_PAGE) + 1) * LINES_PER_PAGE;
+				}
+			}
+
+			++lineNum;
+		} while (lineNum < howFar && !endJournal);
+
+		if (!_index && !_sub)
+			_page = 1;
+		else
+			_page -= howFar / LINES_PER_PAGE;
+		break;
+
+	case 2:
+	case 3:
+		// Move howFar lines ahead unless the end of the journal is reached,
+		// or a searched for keyword is found
+		for (temp = 0; (temp < (howFar / LINES_PER_PAGE)) && !endJournal && !searchSuccessful; ++temp) {
+			// Handle animating mouse cursor
+			int cursorNum = (int)events.getCursor() + 1;
+			if (cursorNum >(WAIT + 2))
+				cursorNum = WAIT;
+			events.setCursor((CursorId)cursorNum);
+
+			lineNum = 0;
+			savedIndex = _index;
+			int savedSub = _sub;
+
+			// Move a single page ahead
+			do {
+				// If in search mode, check for keyword
+				if (direction >= 3 && _page != startPage) {
+					Common::String line = _lines[_sub];
+					line.toUppercase();
+					if (strstr(line.c_str(), _find.c_str()) != nullptr)
+						searchSuccessful = true;
+				}
+
+				// Move forwards a line at a time, unless search word was found
+				if (!searchSuccessful) {
+					if (++_sub == (int)_lines.size()) {
+						// Reached end of page
+						do {
+							if (++_index == (int)_journal.size()) {
+								_index = savedIndex;
+								_sub = savedSub;
+								loadJournalFile(false);
+								endJournal = true;
+							} else {
+								_sub = 0;
+								loadJournalFile(false);
+							}
+						} while (!endJournal && _lines.empty());
+					}
+
+					++lineNum;
+				}
+			} while ((lineNum < LINES_PER_PAGE) && !endJournal && !searchSuccessful);
+
+			if (!endJournal && !searchSuccessful)
+				// Move to next page
+				++_page;
+
+			if (searchSuccessful) {
+				// Search found, so show top of the page it was found on
+				_index = savedIndex;
+				_sub = savedSub;
+				loadJournalFile(false);
+			}
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	if (direction) {
+		events.setCursor(ARROW);
+		drawJournalFrame();
+	}
+
+	Common::String fixedText_Page = fixedText.getText(kFixedText_Journal_Page);
+
+	screen.gPrint(Common::Point(235, 21), PEN_COLOR, fixedText_Page.c_str(), _page);
+
+	temp = _sub;
+	savedIndex = _index;
+	lineNum = 0;
+
+	do {
+		bool inc = true;
+
+		// If there wasn't any line to print at the top of the page, we won't need to
+		// increment the y position
+		if (_lines[temp].empty() && yp == 37)
+			inc = false;
+
+		// If there's a searched for keyword in the line, it will need to be highlighted
+		if (searchSuccessful && firstOccurance) {
+			// Check if line has the keyword
+			Common::String line = _lines[temp];
+			line.toUppercase();
+			if ((matchP = strstr(line.c_str(), _find.c_str())) != nullptr) {
+				matchP = _lines[temp].c_str() + (matchP - line.c_str());
+				firstOccurance = false;
+
+				// Print out the start of the line before the matching keyword
+				Common::String lineStart(_lines[temp].c_str(), matchP);
+				if (lineStart.hasPrefix("@")) {
+					width = screen.stringWidth(lineStart.c_str() + 1);
+					screen.gPrint(Common::Point(53, yp), 15, "%s", lineStart.c_str() + 1);
+				} else {
+					width = screen.stringWidth(lineStart.c_str());
+					screen.gPrint(Common::Point(53, yp), PEN_COLOR, "%s", lineStart.c_str());
+				 }
+
+				// Print out the found keyword
+				Common::String lineMatch(matchP, matchP + _find.size());
+				screen.gPrint(Common::Point(53 + width, yp), INV_FOREGROUND, "%s", lineMatch.c_str());
+				width += screen.stringWidth(lineMatch.c_str());
+
+				// Print remainder of line
+				screen.gPrint(Common::Point(53 + width, yp), PEN_COLOR, "%s", matchP + _find.size());
+			} else if (_lines[temp].hasPrefix("@")) {
+				screen.gPrint(Common::Point(53, yp), 15, "%s", _lines[temp].c_str() + 1);
+			} else {
+				screen.gPrint(Common::Point(53, yp), PEN_COLOR, "%s", _lines[temp].c_str());
+			}
+		} else {
+			if (_lines[temp].hasPrefix("@")) {
+				screen.gPrint(Common::Point(53, yp), 15, "%s", _lines[temp].c_str() + 1);
+			} else {
+				screen.gPrint(Common::Point(53, yp), PEN_COLOR, "%s", _lines[temp].c_str());
+			}
+		}
+
+		if (++temp == (int)_lines.size()) {
+			// Move to next page
+			do {
+				if (_index < ((int)_journal.size() - 1) && lineNum < (LINES_PER_PAGE - 1)) {
+					++_index;
+					loadJournalFile(false);
+					temp = 0;
+				} else {
+					if (_index == ((int)_journal.size() - 1))
+						_down = false;
+					endFlag = true;
+				}
+			} while (!endFlag && _lines.empty());
+		}
+
+		if (inc) {
+			// Move to next line
+			++lineNum;
+			yp += 13;
+		}
+	} while (lineNum < LINES_PER_PAGE && !endFlag);
+
+	_index = savedIndex;
+	_up = _index || _sub;
+
+	return direction >= 3 && searchSuccessful;
+}
+
+JournalButton ScalpelJournal::getHighlightedButton(const Common::Point &pt) {
+	if (pt.x > JOURNAL_POINTS[0][0] && pt.x < JOURNAL_POINTS[0][1] && pt.y >= JOURNAL_BUTTONS_Y &&
+			pt.y < (JOURNAL_BUTTONS_Y + 10))
+		return BTN_EXIT;
+
+	if (pt.x > JOURNAL_POINTS[1][0] && pt.x < JOURNAL_POINTS[1][1] && pt.y >= JOURNAL_BUTTONS_Y &&
+			pt.y < (JOURNAL_BUTTONS_Y + 10) && _page > 1)
+		return BTN_BACK10;
+
+	if (pt.x > JOURNAL_POINTS[2][0] && pt.x < JOURNAL_POINTS[2][1] && pt.y >= JOURNAL_BUTTONS_Y &&
+			pt.y < (JOURNAL_BUTTONS_Y + 10) && _up)
+		return BTN_UP;
+
+	if (pt.x > JOURNAL_POINTS[3][0] && pt.x < JOURNAL_POINTS[3][1] && pt.y >= JOURNAL_BUTTONS_Y &&
+			pt.y < (JOURNAL_BUTTONS_Y + 10) && _down)
+		return BTN_DOWN;
+
+	if (pt.x > JOURNAL_POINTS[4][0] && pt.x < JOURNAL_POINTS[4][1] && pt.y >= JOURNAL_BUTTONS_Y &&
+			pt.y < (JOURNAL_BUTTONS_Y + 10) && _down)
+		return BTN_AHEAD110;
+
+	if (pt.x > JOURNAL_POINTS[5][0] && pt.x < JOURNAL_POINTS[5][1] && pt.y >= (JOURNAL_BUTTONS_Y + 11) &&
+			pt.y < (JOURNAL_BUTTONS_Y + 20) && !_journal.empty())
+		return BTN_SEARCH;
+
+	if (pt.x > JOURNAL_POINTS[6][0] && pt.x < JOURNAL_POINTS[6][1] && pt.y >= (JOURNAL_BUTTONS_Y + 11) &&
+			pt.y < (JOURNAL_BUTTONS_Y + 20) && _up)
+		return BTN_FIRST_PAGE;
+
+	if (pt.x > JOURNAL_POINTS[7][0] && pt.x < JOURNAL_POINTS[7][1] && pt.y >= (JOURNAL_BUTTONS_Y + 11) &&
+			pt.y < (JOURNAL_BUTTONS_Y + 20) && _down)
+		return BTN_LAST_PAGE;
+
+	if (pt.x > JOURNAL_POINTS[8][0] && pt.x < JOURNAL_POINTS[8][1] && pt.y >= (JOURNAL_BUTTONS_Y + 11) &&
+			pt.y < (JOURNAL_BUTTONS_Y + 20) && !_journal.empty())
+		return BTN_PRINT_TEXT;
+
+	return BTN_NONE;
+}
+
+bool ScalpelJournal::handleEvents(int key) {
+	Events    &events    = *_vm->_events;
+	FixedText &fixedText = *_vm->_fixedText;
+	Screen    &screen    = *_vm->_screen;
+	bool doneFlag = false;
+
+	Common::Point pt = events.mousePos();
+	JournalButton btn = getHighlightedButton(pt);
+	byte color;
+
+	if (events._pressed || events._released) {
+		Common::String fixedText_Exit           = fixedText.getText(kFixedText_Journal_Exit);
+		Common::String fixedText_Back10         = fixedText.getText(kFixedText_Journal_Back10);
+		Common::String fixedText_Up             = fixedText.getText(kFixedText_Journal_Up);
+		Common::String fixedText_Down           = fixedText.getText(kFixedText_Journal_Down);
+		Common::String fixedText_Ahead10        = fixedText.getText(kFixedText_Journal_Ahead10);
+		Common::String fixedText_Search         = fixedText.getText(kFixedText_Journal_Search);
+		Common::String fixedText_FirstPage      = fixedText.getText(kFixedText_Journal_FirstPage);
+		Common::String fixedText_LastPage       = fixedText.getText(kFixedText_Journal_LastPage);
+		Common::String fixedText_PrintText      = fixedText.getText(kFixedText_Journal_PrintText);
+
+		// Exit button
+		color = (btn == BTN_EXIT) ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND;
+		screen.buttonPrint(Common::Point(JOURNAL_POINTS[0][2], JOURNAL_BUTTONS_Y), color, true, fixedText_Exit);
+
+		// Back 10 button
+		if (btn == BTN_BACK10) {
+			screen.buttonPrint(Common::Point(JOURNAL_POINTS[1][2], JOURNAL_BUTTONS_Y), COMMAND_HIGHLIGHTED, true, fixedText_Back10);
+		} else if (_page > 1) {
+			screen.buttonPrint(Common::Point(JOURNAL_POINTS[1][2], JOURNAL_BUTTONS_Y), COMMAND_FOREGROUND, true, fixedText_Back10);
+		}
+
+		// Up button
+		if (btn == BTN_UP) {
+			screen.buttonPrint(Common::Point(JOURNAL_POINTS[2][2], JOURNAL_BUTTONS_Y), COMMAND_HIGHLIGHTED, true, fixedText_Up);
+		} else if (_up) {
+			screen.buttonPrint(Common::Point(JOURNAL_POINTS[2][2], JOURNAL_BUTTONS_Y), COMMAND_FOREGROUND, true, fixedText_Up);
+		}
+
+		// Down button
+		if (btn == BTN_DOWN) {
+			screen.buttonPrint(Common::Point(JOURNAL_POINTS[3][2], JOURNAL_BUTTONS_Y), COMMAND_HIGHLIGHTED, true, fixedText_Down);
+		} else if (_down) {
+			screen.buttonPrint(Common::Point(JOURNAL_POINTS[3][2], JOURNAL_BUTTONS_Y), COMMAND_FOREGROUND, true, fixedText_Down);
+		}
+
+		// Ahead 10 button
+		if (btn == BTN_AHEAD110) {
+			screen.buttonPrint(Common::Point(JOURNAL_POINTS[4][2], JOURNAL_BUTTONS_Y), COMMAND_HIGHLIGHTED, true, fixedText_Ahead10);
+		} else if (_down) {
+			screen.buttonPrint(Common::Point(JOURNAL_POINTS[4][2], JOURNAL_BUTTONS_Y), COMMAND_FOREGROUND, true, fixedText_Ahead10);
+		}
+
+		// Search button
+		if (btn == BTN_SEARCH) {
+			color = COMMAND_HIGHLIGHTED;
+		} else if (_journal.empty()) {
+			color = COMMAND_NULL;
+		} else {
+			color = COMMAND_FOREGROUND;
+		}
+		screen.buttonPrint(Common::Point(JOURNAL_POINTS[5][2], JOURNAL_BUTTONS_Y + 11), color, true, fixedText_Search);
+
+		// First Page button
+		if (btn == BTN_FIRST_PAGE) {
+			color = COMMAND_HIGHLIGHTED;
+		} else if (_up) {
+			color = COMMAND_FOREGROUND;
+		} else {
+			color = COMMAND_NULL;
+		}
+		screen.buttonPrint(Common::Point(JOURNAL_POINTS[6][2], JOURNAL_BUTTONS_Y + 11), color, true, fixedText_FirstPage);
+
+		// Last Page button
+		if (btn == BTN_LAST_PAGE) {
+			color = COMMAND_HIGHLIGHTED;
+		} else if (_down) {
+			color = COMMAND_FOREGROUND;
+		} else {
+			color = COMMAND_NULL;
+		}
+		screen.buttonPrint(Common::Point(JOURNAL_POINTS[7][2], JOURNAL_BUTTONS_Y + 11), color, true, fixedText_LastPage);
+
+		// Print Text button
+		screen.buttonPrint(Common::Point(JOURNAL_POINTS[8][2], JOURNAL_BUTTONS_Y + 11), COMMAND_NULL, true, fixedText_PrintText);
+	}
+
+	if (btn == BTN_EXIT && events._released) {
+		// Exit button pressed
+		doneFlag = true;
+
+	} else if (((btn == BTN_BACK10 && events._released) || key == 'B') && (_page > 1)) {
+		// Scrolll up 10 pages
+		if (_page < 11)
+			drawJournal(1, (_page - 1) * LINES_PER_PAGE);
+		else
+			drawJournal(1, 10 * LINES_PER_PAGE);
+
+		doArrows();
+		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
+
+	} else if (((btn == BTN_UP && events._released) || key == 'U') && _up) {
+		// Scroll up
+		drawJournal(1, LINES_PER_PAGE);
+		doArrows();
+		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
+
+	} else if (((btn == BTN_DOWN && events._released) || key == 'D') && _down) {
+		// Scroll down
+		drawJournal(2, LINES_PER_PAGE);
+		doArrows();
+		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
+
+	} else if (((btn == BTN_AHEAD110 && events._released) || key == 'A') && _down) {
+		// Scroll down 10 pages
+		if ((_page + 10) > _maxPage)
+			drawJournal(2, (_maxPage - _page) * LINES_PER_PAGE);
+		else
+			drawJournal(2, 10 * LINES_PER_PAGE);
+
+		doArrows();
+		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
+
+	} else if (((btn == BTN_SEARCH && events._released) || key == 'S') && !_journal.empty()) {
+		screen.buttonPrint(Common::Point(JOURNAL_POINTS[5][2], JOURNAL_BUTTONS_Y + 11), COMMAND_FOREGROUND, true, "Search");
+		bool notFound = false;
+
+		do {
+			int dir;
+			if ((dir = getSearchString(notFound)) != 0) {
+				int savedIndex = _index;
+				int savedSub = _sub;
+				int savedPage = _page;
+
+				if (drawJournal(dir + 2, 1000 * LINES_PER_PAGE) == 0) {
+					_index = savedIndex;
+					_sub = savedSub;
+					_page = savedPage;
+
+					drawJournalFrame();
+					drawJournal(0, 0);
+					notFound = true;
+				} else {
+					doneFlag = true;
+				}
+
+				doArrows();
+				screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
+			} else {
+				doneFlag = true;
+			}
+		} while (!doneFlag);
+		doneFlag = false;
+
+	} else if (((btn == BTN_FIRST_PAGE && events._released) || key == 'F') && _up) {
+		// First page
+		_index = _sub = 0;
+		_up = _down = false;
+		_page = 1;
+
+		drawJournalFrame();
+		drawJournal(0, 0);
+		doArrows();
+		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
+
+	} else if (((btn == BTN_LAST_PAGE && events._released) || key == 'L') && _down) {
+		// Last page
+		if ((_page + 10) > _maxPage)
+			drawJournal(2, (_maxPage - _page) * LINES_PER_PAGE);
+		else
+			drawJournal(2, 1000 * LINES_PER_PAGE);
+
+		doArrows();
+		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
+	}
+
+	events.wait(2);
+
+	return doneFlag;
+}
+
+int ScalpelJournal::getSearchString(bool printError) {
+	enum Button { BTN_NONE, BTN_EXIT, BTN_BACKWARD, BTN_FORWARD };
+
+	Events    &events    = *_vm->_events;
+	FixedText &fixedText = *_vm->_fixedText;
+	Screen    &screen    = *_vm->_screen;
+	Talk &talk = *_vm->_talk;
+	int xp;
+	int yp = 174;
+	bool flag = false;
+	Common::String name;
+	int done = 0;
+	byte color;
+
+	Common::String fixedText_Exit           = fixedText.getText(kFixedText_JournalSearch_Exit);
+	Common::String fixedText_Backward       = fixedText.getText(kFixedText_JournalSearch_Backward);
+	Common::String fixedText_Forward        = fixedText.getText(kFixedText_JournalSearch_Forward);
+	Common::String fixedText_NotFound       = fixedText.getText(kFixedText_JournalSearch_NotFound);
+
+	// Draw search panel
+	screen.makePanel(Common::Rect(6, 171, 313, 199));
+	screen.makeButton(Common::Rect(SEARCH_POINTS[0][0], yp, SEARCH_POINTS[0][1], yp + 10),
+		SEARCH_POINTS[0][2] - screen.stringWidth(fixedText_Exit) / 2, fixedText_Exit);
+	screen.makeButton(Common::Rect(SEARCH_POINTS[1][0], yp, SEARCH_POINTS[1][1], yp + 10),
+		SEARCH_POINTS[1][2] - screen.stringWidth(fixedText_Backward) / 2, fixedText_Backward);
+	screen.makeButton(Common::Rect(SEARCH_POINTS[2][0], yp, SEARCH_POINTS[2][1], yp + 10),
+		SEARCH_POINTS[2][2] - screen.stringWidth(fixedText_Forward) / 2, fixedText_Forward);
+
+	screen.gPrint(Common::Point(SEARCH_POINTS[0][2] - screen.stringWidth(fixedText_Exit) / 2, yp),
+		COMMAND_HIGHLIGHTED, "%c", fixedText_Exit[0]);
+	screen.gPrint(Common::Point(SEARCH_POINTS[1][2] - screen.stringWidth(fixedText_Backward) / 2, yp),
+		COMMAND_HIGHLIGHTED, "%c", fixedText_Backward[0]);
+	screen.gPrint(Common::Point(SEARCH_POINTS[2][2] - screen.stringWidth(fixedText_Forward) / 2, yp),
+		COMMAND_HIGHLIGHTED, "%c", fixedText_Forward[0]);
+
+	screen.makeField(Common::Rect(12, 185, 307, 196));
+
+	screen.fillRect(Common::Rect(12, 185, 307, 186), BUTTON_BOTTOM);
+	screen.vLine(12, 185, 195, BUTTON_BOTTOM);
+	screen.hLine(13, 195, 306, BUTTON_TOP);
+	screen.hLine(306, 186, 195, BUTTON_TOP);
+
+	if (printError) {
+		screen.gPrint(Common::Point((SHERLOCK_SCREEN_WIDTH - screen.stringWidth(fixedText_NotFound)) / 2, 185),
+			INV_FOREGROUND, "%s", fixedText_NotFound.c_str());
+	} else if (!_find.empty()) {
+		// There's already a search term, display it already
+		screen.gPrint(Common::Point(15, 185), TALK_FOREGROUND, "%s", _find.c_str());
+		name = _find;
+	}
+
+	screen.slamArea(6, 171, 307, 28);
+
+	if (printError) {
+		// Give time for user to see the message
+		events.setButtonState();
+		for (int idx = 0; idx < 40 && !_vm->shouldQuit() && !events.kbHit() && !events._released; ++idx) {
+			events.pollEvents();
+			events.setButtonState();
+			events.wait(2);
+		}
+
+		events.clearKeyboard();
+		screen._backBuffer1.fillRect(Common::Rect(13, 186, 306, 195), BUTTON_MIDDLE);
+
+		if (!_find.empty()) {
+			screen.gPrint(Common::Point(15, 185), TALK_FOREGROUND, "%s", _find.c_str());
+			name = _find;
+		}
+
+		screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
+	}
+
+	xp = JOURNAL_SEARCH_LEFT + screen.stringWidth(name);
+	yp = JOURNAL_SEARCH_TOP;
+
+	do {
+		events._released = false;
+		Button found = BTN_NONE;
+
+		while (!_vm->shouldQuit() && !events.kbHit() && !events._released) {
+			found = BTN_NONE;
+			if (talk._talkToAbort)
+				return 0;
+
+			// Check if key or mouse button press has occurred
+			events.setButtonState();
+			Common::Point pt = events.mousePos();
+
+			flag = !flag;
+			screen.vgaBar(Common::Rect(xp, yp, xp + 8, yp + 9), flag ? INV_FOREGROUND : BUTTON_MIDDLE);
+
+			if (events._pressed || events._released) {
+				if (pt.x > SEARCH_POINTS[0][0] && pt.x < SEARCH_POINTS[0][1] && pt.y > 174 && pt.y < 183) {
+					found = BTN_EXIT;
+					color = COMMAND_HIGHLIGHTED;
+				} else {
+					color = COMMAND_FOREGROUND;
+				}
+				screen.print(Common::Point(SEARCH_POINTS[0][2] - screen.stringWidth(fixedText_Exit) / 2, 175), color, "%s", fixedText_Exit.c_str());
+
+				if (pt.x > SEARCH_POINTS[1][0] && pt.x < SEARCH_POINTS[1][1] && pt.y > 174 && pt.y < 183) {
+					found = BTN_BACKWARD;
+					color = COMMAND_HIGHLIGHTED;
+				} else {
+					color = COMMAND_FOREGROUND;
+				}
+				screen.print(Common::Point(SEARCH_POINTS[1][2] - screen.stringWidth(fixedText_Backward) / 2, 175), color, "%s", fixedText_Backward.c_str());
+
+				if (pt.x > SEARCH_POINTS[2][0] && pt.x < SEARCH_POINTS[2][1] && pt.y > 174 && pt.y < 183) {
+					found = BTN_FORWARD;
+					color = COMMAND_HIGHLIGHTED;
+				} else {
+					color = COMMAND_FOREGROUND;
+				}
+				screen.print(Common::Point(SEARCH_POINTS[2][2] - screen.stringWidth(fixedText_Forward) / 2, 175), color, "%s", fixedText_Forward.c_str());
+			}
+
+			events.wait(2);
+		}
+
+		if (events.kbHit()) {
+			Common::KeyState keyState = events.getKey();
+
+			if ((keyState.keycode == Common::KEYCODE_BACKSPACE) && (name.size() > 0)) {
+				screen.vgaBar(Common::Rect(xp - screen.charWidth(name.lastChar()), yp, xp + 8, yp + 9), BUTTON_MIDDLE);
+				xp -= screen.charWidth(name.lastChar());
+				screen.vgaBar(Common::Rect(xp, yp, xp + 8, yp + 9), INV_FOREGROUND);
+				name.deleteLastChar();
+
+			} else  if (keyState.keycode == Common::KEYCODE_RETURN) {
+				done = 1;
+
+			}  else if (keyState.keycode == Common::KEYCODE_ESCAPE) {
+				screen.vgaBar(Common::Rect(xp, yp, xp + 8, yp + 9), BUTTON_MIDDLE);
+				done = -1;
+
+			} else if (keyState.ascii >= ' ' && keyState.ascii <= 'z' && keyState.keycode != Common::KEYCODE_AT &&
+				name.size() < JOURNAL_SEACRH_MAX_CHARS && (xp + screen.charWidth(keyState.ascii)) < JOURNAL_SEARCH_RIGHT) {
+				char ch = toupper(keyState.ascii);
+				screen.vgaBar(Common::Rect(xp, yp, xp + 8, yp + 9), BUTTON_MIDDLE);
+				screen.print(Common::Point(xp, yp), TALK_FOREGROUND, "%c", ch);
+				xp += screen.charWidth(ch);
+				name += ch;
+			}
+		}
+
+		if (events._released) {
+			switch (found) {
+			case BTN_EXIT:
+				done = -1; break;
+			case BTN_BACKWARD:
+				done = 2; break;
+			case BTN_FORWARD:
+				done = 1; break;
+			default:
+				break;
+			}
+		}
+	} while (!done && !_vm->shouldQuit());
+
+	if (done != -1) {
+		_find = name;
+	} else {
+		done = 0;
+	}
+
+	// Redisplay the journal screen
+	drawJournalFrame();
+	drawJournal(0, 0);
+	screen.slamArea(0, 0, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT);
+
+	return done;
+}
+
+void ScalpelJournal::resetPosition() {
+	_index = _sub = _up = _down = 0;
+	_page = 1;
+}
+
+void ScalpelJournal::synchronize(Serializer &s) {
+	s.syncAsSint16LE(_index);
+	s.syncAsSint16LE(_sub);
+	s.syncAsSint16LE(_page);
+	s.syncAsSint16LE(_maxPage);
+
+	int journalCount = _journal.size();
+	s.syncAsUint16LE(journalCount);
+	if (s.isLoading())
+		_journal.resize(journalCount);
+
+	for (uint idx = 0; idx < _journal.size(); ++idx) {
+		JournalEntry &je = _journal[idx];
+
+		s.syncAsSint16LE(je._converseNum);
+		s.syncAsByte(je._replyOnly);
+		s.syncAsSint16LE(je._statementNum);
+	}
+}
+
+} // End of namespace Scalpel
+
+} // End of namespace Sherlock
diff --git a/engines/sherlock/scalpel/scalpel_journal.h b/engines/sherlock/scalpel/scalpel_journal.h
new file mode 100644
index 0000000..ba2c828
--- /dev/null
+++ b/engines/sherlock/scalpel/scalpel_journal.h
@@ -0,0 +1,142 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef SHERLOCK_SCALPEL_JOURNAL_H
+#define SHERLOCK_SCALPEL_JOURNAL_H
+
+#include "sherlock/journal.h"
+#include "sherlock/saveload.h"
+#include "common/scummsys.h"
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/str-array.h"
+#include "common/stream.h"
+
+namespace Sherlock {
+
+namespace Scalpel {
+
+#define JOURNAL_MAX_WIDTH 230
+#define JOURNAL_MAX_CHARS 80
+
+enum JournalButton {
+	BTN_NONE, BTN_EXIT, BTN_BACK10, BTN_UP, BTN_DOWN, BTN_AHEAD110, BTN_SEARCH,
+	BTN_FIRST_PAGE, BTN_LAST_PAGE, BTN_PRINT_TEXT
+};
+
+
+struct JournalEntry {
+	int _converseNum;
+	bool _replyOnly;
+	int _statementNum;
+
+	JournalEntry() : _converseNum(0), _replyOnly(false), _statementNum(0) {}
+	JournalEntry(int converseNum, int statementNum, bool replyOnly = false) :
+		_converseNum(converseNum), _statementNum(statementNum), _replyOnly(replyOnly) {}
+};
+
+class ScalpelJournal: public Journal {
+private:
+	Common::Array<JournalEntry> _journal;
+	Common::StringArray _directory;
+	Common::StringArray _locations;
+	Common::StringArray _lines;
+	int _maxPage;
+	int _index;
+	int _sub;
+	bool _up, _down;
+	int _page;
+	Common::String _find;
+
+	/**
+	 * Load the list of location names that the journal will make reference to
+	 */
+	void loadJournalLocations();
+
+	/**
+	 * Loads the description for the current display index in the journal, and then
+	 * word wraps the result to prepare it for being displayed
+	 * @param alreadyLoaded		Indicates whether the journal file is being loaded for the
+	 *		first time, or being reloaded
+	 */
+	void loadJournalFile(bool alreadyLoaded);
+
+	/**
+	 * Display the arrows that can be used to scroll up and down pages
+	 */
+	void doArrows();
+
+	/**
+	 * Displays a page of the journal at the current index
+	 */
+	bool drawJournal(int direction, int howFar);
+
+	/**
+	 * Show the search submenu and allow the player to enter a search string
+	 */
+	int getSearchString(bool printError);
+
+	/**
+	 * Draw the journal background, frame, and interface buttons
+	 */
+	void drawJournalFrame();
+
+	/**
+	 * Returns the button, if any, that is under the specified position
+	 */
+	JournalButton getHighlightedButton(const Common::Point &pt);
+public:
+	ScalpelJournal(SherlockEngine *vm);
+	virtual ~ScalpelJournal() {}
+
+	/**
+	 * Display the journal
+	 */
+	void drawInterface();
+
+	/**
+	 * Handle events whilst the journal is being displayed
+	 */
+	bool handleEvents(int key);
+
+	/**
+	 * Records statements that are said, in the order which they are said. The player
+	 * can then read the journal to review them
+	 */
+	virtual void record(int converseNum, int statementNum, bool replyOnly = false);
+
+	/**
+	 * Reset viewing position to the start of the journal
+	 */
+	virtual void resetPosition();
+
+	/**
+	 * Synchronize the data for a savegame
+	 */
+	virtual void synchronize(Serializer &s);
+};
+
+} // End of namespace Scalpel
+
+} // End of namespace Sherlock
+
+#endif
diff --git a/engines/sherlock/scalpel/scalpel_user_interface.cpp b/engines/sherlock/scalpel/scalpel_user_interface.cpp
index 5ddf2ec..f85c95e 100644
--- a/engines/sherlock/scalpel/scalpel_user_interface.cpp
+++ b/engines/sherlock/scalpel/scalpel_user_interface.cpp
@@ -21,9 +21,10 @@
  */
 
 #include "sherlock/scalpel/scalpel_user_interface.h"
+#include "sherlock/scalpel/scalpel_journal.h"
 #include "sherlock/scalpel/scalpel_people.h"
-#include "sherlock/sherlock.h"
 #include "sherlock/scalpel/settings.h"
+#include "sherlock/sherlock.h"
 
 namespace Sherlock {
 
@@ -1473,7 +1474,7 @@ void ScalpelUserInterface::doPickControl() {
 void ScalpelUserInterface::doTalkControl() {
 	Events &events = *_vm->_events;
 	FixedText &fixedText = *_vm->_fixedText;
-	Journal &journal = *_vm->_journal;
+	ScalpelJournal &journal = *(ScalpelJournal *)_vm->_journal;
 	ScalpelPeople &people = *(ScalpelPeople *)_vm->_people;
 	Screen &screen = *_vm->_screen;
 	Sound &sound = *_vm->_sound;
@@ -1732,7 +1733,7 @@ void ScalpelUserInterface::doTalkControl() {
 
 void ScalpelUserInterface::journalControl() {
 	Events &events = *_vm->_events;
-	Journal &journal = *_vm->_journal;
+	ScalpelJournal &journal = *(ScalpelJournal *)_vm->_journal;
 	Scene &scene = *_vm->_scene;
 	Screen &screen = *_vm->_screen;
 	bool doneFlag = false;
diff --git a/engines/sherlock/sherlock.cpp b/engines/sherlock/sherlock.cpp
index d1e52f6..aac6986 100644
--- a/engines/sherlock/sherlock.cpp
+++ b/engines/sherlock/sherlock.cpp
@@ -99,7 +99,7 @@ void SherlockEngine::initialize() {
 	_inventory = new Inventory(this);
 	_map = Map::init(this);
 	_music = new Music(this, _mixer);
-	_journal = new Journal(this);
+	_journal = Journal::init(this);
 	_people = People::init(this);
 	_saves = new SaveManager(this, _targetName);
 	_scene = Scene::init(this);
diff --git a/engines/sherlock/tattoo/tattoo_journal.cpp b/engines/sherlock/tattoo/tattoo_journal.cpp
new file mode 100644
index 0000000..ff4f6b3
--- /dev/null
+++ b/engines/sherlock/tattoo/tattoo_journal.cpp
@@ -0,0 +1,39 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "sherlock/tattoo/tattoo_journal.h"
+#include "sherlock/sherlock.h"
+
+namespace Sherlock {
+
+namespace Tattoo {
+
+TattooJournal::TattooJournal(SherlockEngine *vm) : Journal(vm) {
+}
+
+void TattooJournal::synchronize(Serializer &s) {
+	// TODO
+}
+
+} // End of namespace Tattoo
+
+} // End of namespace Sherlock
diff --git a/engines/sherlock/tattoo/tattoo_journal.h b/engines/sherlock/tattoo/tattoo_journal.h
new file mode 100644
index 0000000..ebfcd46
--- /dev/null
+++ b/engines/sherlock/tattoo/tattoo_journal.h
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*
+*/
+
+#ifndef SHERLOCK_TATTOO_JOURNAL_H
+#define SHERLOCK_TATTOO_JOURNAL_H
+
+#include "sherlock/journal.h"
+
+namespace Sherlock {
+
+namespace Tattoo {
+
+class TattooJournal : public Journal {
+public:
+	TattooJournal(SherlockEngine *vm);
+	virtual ~TattooJournal() {}
+
+	/**
+	 * Synchronize the data for a savegame
+	 */
+	virtual void synchronize(Serializer &s);
+};
+
+} // End of namespace Tattoo
+
+} // End of namespace Sherlock
+
+#endif
diff --git a/engines/sherlock/tattoo/tattoo_user_interface.cpp b/engines/sherlock/tattoo/tattoo_user_interface.cpp
index 322655f..462eccc 100644
--- a/engines/sherlock/tattoo/tattoo_user_interface.cpp
+++ b/engines/sherlock/tattoo/tattoo_user_interface.cpp
@@ -487,7 +487,7 @@ void TattooUserInterface::doStandardControl() {
 			if (people[_bgFound - 1000]._description.empty() || people[_bgFound - 1000]._description.hasPrefix(" "))
 				noDesc = true;
 		} else if (_bgFound != -1) {
-			if (people[_bgFound - 1000]._description.empty() || people[_bgFound - 1000]._description.hasPrefix(" "))
+			if (_bgShape->_description.empty() || _bgShape->_description.hasPrefix(" "))
 				noDesc = true;
 		} else {
 			noDesc = true;
diff --git a/engines/sherlock/tattoo/widget_inventory.cpp b/engines/sherlock/tattoo/widget_inventory.cpp
index ee8faa3..60da821 100644
--- a/engines/sherlock/tattoo/widget_inventory.cpp
+++ b/engines/sherlock/tattoo/widget_inventory.cpp
@@ -532,7 +532,7 @@ void WidgetInventory::handleEvents() {
 }
 
 void WidgetInventory::updateDescription() {
-
+	// TODO
 }
 
 void WidgetInventory::checkInvTabbingKeys() {






More information about the Scummvm-git-logs mailing list