[Scummvm-git-logs] scummvm master -> 181368f1c509ae9d54e4bb9f6d153d8706a21d16

athrxx athrxx at scummvm.org
Sun Nov 22 18:05:03 UTC 2020


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

Summary:
fa671a2c40 SAGA: (ITE/PC98) - prepare font class for Japanese rendering
2db289f419 SAGA: (ITE/PC98) - implement dual layer rendering
cf08894520 GRAPHICS: (SJIS) - SAGA adjustments
30704a98dd SAGA: (ITE/PC98) -adjustments to hardcoded intro data
7d75487d93 SAGA: (ITE/PC98) - implement original mouse cursor
8306fee7e3 SAGA: (ITE/PC98) - set GF_ITE_FLOPPY flag
ffdd648c88 SAGA: (ITE/PC98) - fix option panel string
c16f00f0f6 SAGA: (ITE/PC98) - make version specific interface changes
c22768e4fb SAGA: (ITE/PC98) - implement sjis font drawing
5c8f6507ef SAGA: (ITE/PC98) - adapt actor speech positioning
4a6b2b1897 SAGA: minor fix to horizontal actor text positioning
eaeadbb622 SAGA: (ITE/PC98) - fix override warning
3aded1d5ba SAGA: (ITE/PC98) - fix intro credits text positioning
c9f41699aa SAGA: (ITE/PC98) - fine tune intro line breaks
181368f1c5 SAGA: (ITE/PC98) - fix intro text frame width


Commit: fa671a2c40c8131c4d246320be317a4ffe4254cb
    https://github.com/scummvm/scummvm/commit/fa671a2c40c8131c4d246320be317a4ffe4254cb
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:43+01:00

Commit Message:
SAGA: (ITE/PC98) - prepare font class for Japanese rendering

Reorganize font code into sub classes to allow independent rendering of the Japanese ROM font. No actual implementation for the Japanese rendering yet.

Changed paths:
    engines/saga/font.cpp
    engines/saga/font.h
    engines/saga/font_map.cpp
    engines/saga/saga.cpp


diff --git a/engines/saga/font.cpp b/engines/saga/font.cpp
index b69f237106..3166ac0391 100644
--- a/engines/saga/font.cpp
+++ b/engines/saga/font.cpp
@@ -29,209 +29,338 @@
 #include "saga/font.h"
 #include "saga/render.h"
 
-namespace Saga {
-
-Font::Font(SagaEngine *vm) : _vm(vm) {
-	int i;
-
-	// Load font module resource context
-
-	assert(_vm->getFontsCount() > 0);
+#include "graphics/sjis.h"
 
-	_fonts.resize(_vm->getFontsCount());
-	for (i = 0; i < _vm->getFontsCount(); i++) {
-#ifdef __DS__
-		_fonts[i].outline.font = NULL;
-		_fonts[i].normal.font = NULL;
-#endif
-		loadFont(&_fonts[i],	_vm->getFontDescription(i)->fontResourceId);
-	}
+namespace Saga {
 
-	_fontMapping = 0;
-}
+Font::FontId Font::knownFont2FontIdx(KnownFont font) {
+	FontId fontId = kSmallFont;
 
-Font::~Font() {
-	debug(8, "Font::~Font(): Freeing fonts.");
+	// The demo version of IHNM has 3 font types (like ITE), not 6 (like the full version of IHNM)
+	if (_vm->getGameId() == GID_ITE || _vm->isIHNMDemo()) {
+		switch (font) {
+		case (kKnownFontSmall):
+		default:
+		fontId = kSmallFont;
+		break;
+		case (kKnownFontMedium):
+		fontId = kMediumFont;
+		break;
+		case (kKnownFontBig):
+		fontId = kBigFont;
+		break;
 
-#ifdef __DS__
-	for (int i = 0; i < _vm->getFontsCount(); i++) {
-		if (_fonts[i].outline.font) {
-			free(_fonts[i].outline.font);
+		case (kKnownFontVerb):
+		fontId = kSmallFont;
+		break;
+		case (kKnownFontScript):
+		fontId = kMediumFont;
+		break;
+		case (kKnownFontPause):
+		fontId = _vm->_font->valid(kBigFont) ? kBigFont : kMediumFont;
+		break;
 		}
+#ifdef ENABLE_IHNM
+	} else if (_vm->getGameId() == GID_IHNM && !_vm->isIHNMDemo()) {
+		switch (font) {
+		case (kKnownFontSmall):
+		default:
+		fontId = kSmallFont;
+		break;
+		case (kKnownFontMedium):
+		fontId = kMediumFont;
+		break;
+		case (kKnownFontBig):
+		fontId = kBigFont;
+		break;
 
-		if (_fonts[i].normal.font) {
-			free(_fonts[i].normal.font);
+		case (kKnownFontVerb):
+		fontId = kIHNMFont8;
+		break;
+		case (kKnownFontScript):
+		fontId = kIHNMMainFont;
+		break;
+		case (kKnownFontPause):
+		fontId = kMediumFont; // unchecked
+		break;
 		}
-	}
 #endif
-}
-
-
-void Font::loadFont(FontData *font, uint32 fontResourceId) {
-	ByteArray fontResourceData;
-	int numBits;
-	int c;
-	ResourceContext *fontContext;
-
-	debug(1, "Font::loadFont(): Reading fontResourceId %d...", fontResourceId);
-
-	fontContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
-	if (fontContext == NULL) {
-		error("Font::Font() resource context not found");
-	}
-
-	// Load font resource
-	_vm->_resource->loadResource(fontContext, fontResourceId, fontResourceData);
-
-	if (fontResourceData.size() < FONT_DESCSIZE) {
-		error("Font::loadFont() Invalid font length (%i < %i)", (int)fontResourceData.size(), FONT_DESCSIZE);
 	}
+	return fontId;
+}
 
-	ByteArrayReadStreamEndian readS(fontResourceData, fontContext->isBigEndian());
+int Font::getHeight(FontId fontId, const char *text, int width, FontEffectFlags flags) {
+	int textWidth;
+	int textLength;
+	int fitWidth;
+	const char *searchPointer;
+	const char *measurePointer;
+	const char *foundPointer;
+	int len;
+	int w;
+	const char *endPointer;
+	int h;
+	int wc;
+	int w_total;
+	int len_total;
+	Common::Point textPoint;
 
-	// Read font header
-	font->normal.header.charHeight = readS.readUint16();
-	font->normal.header.charWidth = readS.readUint16();
-	font->normal.header.rowLength = readS.readUint16();
+	textLength = strlen(text);
 
+	textWidth = getStringWidth(fontId, text, textLength, flags);
+	h = getHeight(fontId);
+	fitWidth = width;
 
-	debug(2, "Character width: %d", font->normal.header.charWidth);
-	debug(2, "Character height: %d", font->normal.header.charHeight);
-	debug(2, "Row padding: %d", font->normal.header.rowLength);
+	textPoint.x = (fitWidth / 2);
+	textPoint.y = 0;
 
-	for (c = 0; c < FONT_CHARCOUNT; c++) {
-		font->normal.fontCharEntry[c].index = readS.readUint16();
+	if (fitWidth >= textWidth) {
+		return h;
 	}
 
-	for (c = 0; c < FONT_CHARCOUNT; c++) {
-		numBits = font->normal.fontCharEntry[c].width = readS.readByte();
-		font->normal.fontCharEntry[c].byteWidth = getByteLen(numBits);
-	}
+	// String won't fit on one line
+	w_total = 0;
+	len_total = 0;
+	wc = 0;
 
-	for (c = 0; c < FONT_CHARCOUNT; c++) {
-		font->normal.fontCharEntry[c].flag = readS.readByte();
-	}
+	measurePointer = text;
+	searchPointer = text;
+	endPointer = text + textLength;
 
-	for (c = 0; c < FONT_CHARCOUNT; c++) {
-		font->normal.fontCharEntry[c].tracking = readS.readByte();
-	}
+	for (;;) {
+		foundPointer = strchr(searchPointer, ' ');
+		if (foundPointer == NULL) {
+			// Ran to the end of the buffer
+			len = endPointer - measurePointer;
+		} else {
+			len = foundPointer - measurePointer;
+		}
 
-	if (readS.pos() != FONT_DESCSIZE) {
-		error("Invalid font resource size");
-	}
+		w = getStringWidth(fontId, measurePointer, len, flags);
+		measurePointer = foundPointer;
 
-#ifndef __DS__
-	font->normal.font.resize(fontResourceData.size() - FONT_DESCSIZE);
-	memcpy(font->normal.font.getBuffer(), fontResourceData.getBuffer() + FONT_DESCSIZE, fontResourceData.size() - FONT_DESCSIZE);
-#else
-	if (font->normal.font) {
-		free(font->normal.font);
+		if ((w_total + w) > fitWidth) {
+			// This word won't fit
+			if (wc == 0) {
+				// The first word in the line didn't fit. Still print it
+				searchPointer = measurePointer + 1;
+			}
+			// Wrap what we've got and restart
+			textPoint.y += h + TEXT_LINESPACING;
+			if (foundPointer == NULL) {
+				// Since word hit NULL but fit, we are done
+				return textPoint.y + h;
+			}
+			w_total = 0;
+			len_total = 0;
+			wc = 0;
+			measurePointer = searchPointer;
+		} else {
+			// Word will fit ok
+			w_total += w;
+			len_total += len;
+			wc++;
+			if (foundPointer == NULL) {
+				// Since word hit NULL but fit, we are done
+				return textPoint.y + h;
+			}
+			searchPointer = measurePointer + 1;
+		}
 	}
-
-	font->normal.font = (byte *) malloc(fontResourceData.size() - FONT_DESCSIZE);
-	memcpy(font->normal.font, fontResourceData.getBuffer() + FONT_DESCSIZE, fontResourceData.size() - FONT_DESCSIZE);
-#endif
-
-	// Create outline font style
-	createOutline(font);
 }
 
-void Font::createOutline(FontData *font) {
-	int i;
-	int row;
-	int newByteWidth;
-	int newRowLength = 0;
-	int currentByte;
-	byte *basePointer;
-	byte *srcPointer;
-	byte *destPointer1;
-	byte *destPointer2;
-	byte *destPointer3;
-	byte charRep;
-
-	// Populate new font style character data
-	for (i = 0; i < FONT_CHARCOUNT; i++) {
-		newByteWidth = 0;
+void Font::textDrawRect(FontId fontId, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags) {
+	int textWidth;
+	int textLength;
+	int fitWidth;
+	const char *startPointer;
+	const char *searchPointer;
+	const char *measurePointer;
+	const char *foundPointer;
+	int len;
+	int w;
+	const char *endPointer;
+	int h;
+	int wc;
+	int w_total;
+	int len_total;
+	Common::Point textPoint;
+	Common::Point textPoint2;
 
-		font->outline.fontCharEntry[i].index = newRowLength;
-		font->outline.fontCharEntry[i].tracking = font->normal.fontCharEntry[i].tracking;
-		font->outline.fontCharEntry[i].flag = font->normal.fontCharEntry[i].flag;
+	textLength = strlen(text);
 
-		if (font->normal.fontCharEntry[i].width != 0)
-			newByteWidth = getByteLen(font->normal.fontCharEntry[i].width + 2);
+	textWidth = getStringWidth(fontId, text, textLength, flags);
+	fitWidth = rect.width();
 
-		font->outline.fontCharEntry[i].width = font->normal.fontCharEntry[i].width + 2;
-		font->outline.fontCharEntry[i].byteWidth = newByteWidth;
+	textPoint.x = rect.left + (fitWidth / 2);
+	textPoint.y = rect.top;
 
-		newRowLength += newByteWidth;
+	if (fitWidth >= textWidth) {
+		// Entire string fits, draw it
+		textPoint.x -= (textWidth / 2);
+		draw(fontId, text, textLength, textPoint, color, effectColor, flags);
+		return;
 	}
 
-	debug(2, "New row length: %d", newRowLength);
+	// String won't fit on one line
+	h = getHeight(fontId);
+	w_total = 0;
+	len_total = 0;
+	wc = 0;
 
-	font->outline.header = font->normal.header;
-	font->outline.header.charWidth += 2;
-	font->outline.header.charHeight += 2;
-	font->outline.header.rowLength = newRowLength;
+	startPointer = text;
+	measurePointer = text;
+	searchPointer = text;
+	endPointer = text + textLength;
 
-	// Allocate new font representation storage
-#ifdef __DS__
-	if (font->outline.font) {
-		free(font->outline.font);
-	}
+	for (;;) {
+		foundPointer = strchr(searchPointer, ' ');
+		if (foundPointer == NULL) {
+			// Ran to the end of the buffer
+			len = endPointer - measurePointer;
+		} else {
+			len = foundPointer - measurePointer;
+		}
 
-	font->outline.font = (byte *) calloc(newRowLength * font->outline.header.charHeight, 1);
-#else
-	font->outline.font.resize(newRowLength * font->outline.header.charHeight);
-#endif
+		w = getStringWidth(fontId, measurePointer, len, flags);
+		measurePointer = foundPointer;
 
+		if ((w_total + w) > fitWidth) {
+			// This word won't fit
+			if (wc == 0) {
+				w_total = fitWidth;
+				len_total = len;
+			}
 
-	// Generate outline font representation
-	for (i = 0; i < FONT_CHARCOUNT; i++) {
-		for (row = 0; row < font->normal.header.charHeight; row++) {
-			for (currentByte = 0; currentByte < font->outline.fontCharEntry[i].byteWidth; currentByte++) {
-				basePointer = &font->outline.font[font->outline.fontCharEntry[i].index + currentByte];
-				destPointer1 = basePointer + newRowLength * row;
-				destPointer2 = basePointer + newRowLength * (row + 1);
-				destPointer3 = basePointer + newRowLength * (row + 2);
-				if (currentByte > 0) {
-					// Get last two columns from previous byte
-					srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1)];
-					charRep = *srcPointer;
-					*destPointer1 |= ((charRep << 6) | (charRep << 7));
-					*destPointer2 |= ((charRep << 6) | (charRep << 7));
-					*destPointer3 |= ((charRep << 6) | (charRep << 7));
-				}
+			// Wrap what we've got and restart
+			textPoint2.x = textPoint.x - (w_total / 2);
+			textPoint2.y = textPoint.y;
+			draw(fontId, startPointer, len_total, textPoint2, color, effectColor, flags);
+			textPoint.y += h + TEXT_LINESPACING;
+			if (textPoint.y >= rect.bottom) {
+				return;
+			}
+			w_total = 0;
+			len_total = 0;
+			if (wc == 0 && measurePointer) {
+				searchPointer = measurePointer + 1;
+			}
+			wc = 0;
 
-				if (currentByte < font->normal.fontCharEntry[i].byteWidth) {
-					srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte];
-					charRep = *srcPointer;
-					*destPointer1 |= charRep | (charRep >> 1) | (charRep >> 2);
-					*destPointer2 |= charRep | (charRep >> 1) | (charRep >> 2);
-					*destPointer3 |= charRep | (charRep >> 1) | (charRep >> 2);
-				}
+			// Advance the search pointer to the next non-space.
+			// Otherwise, the first "word" to be measured will be
+			// an empty string. Measuring or drawing a string of
+			// length 0 is interpreted as measure/draw the entire
+			// buffer, which certainly is not what we want here.
+			//
+			// This happes because a string may contain several
+			// spaces in a row, e.g. after a period.
+
+			while (*searchPointer == ' ')
+				searchPointer++;
+
+			measurePointer = searchPointer;
+			startPointer = searchPointer;
+		} else {
+			// Word will fit ok
+			w_total += w;
+			len_total += len;
+			wc++;
+			if (foundPointer == NULL) {
+				// Since word hit NULL but fit, we are done
+				textPoint2.x = textPoint.x - (w_total / 2);
+				textPoint2.y = textPoint.y;
+				draw(fontId, startPointer, len_total, textPoint2, color,
+					effectColor, flags);
+				return;
 			}
+			searchPointer = measurePointer + 1;
 		}
+	}
+}
 
-		// "Hollow out" character to prevent overdraw
-		for (row = 0; row < font->normal.header.charHeight; row++) {
-			for (currentByte = 0; currentByte < font->outline.fontCharEntry[i].byteWidth; currentByte++) {
-				destPointer2 = &font->outline.font[font->outline.header.rowLength * (row + 1) + font->outline.fontCharEntry[i].index + currentByte];
-				if (currentByte > 0) {
-					// Get last two columns from previous byte
-					srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1)];
-					*destPointer2 &= ((*srcPointer << 7) ^ 0xFFU);
-				}
+void Font::textDraw(FontId fontId, const char *text, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) {
+	int textWidth;
+	int textLength;
+	int fitWidth;
+	Common::Point textPoint(point);
 
-				if (currentByte < font->normal.fontCharEntry[i].byteWidth) {
-					srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte];
-					*destPointer2 &= ((*srcPointer >> 1) ^ 0xFFU);
-				}
-			}
+	textLength = strlen(text);
+
+	if (!(flags & kFontCentered)) {
+		// Text is not centered; No formatting required
+		draw(fontId, text, textLength, point, color, effectColor, flags);
+		return;
+	}
+
+	// Text is centered... format output
+	// Enforce minimum and maximum center points for centered text
+	if (textPoint.x < TEXT_CENTERLIMIT) {
+		textPoint.x = TEXT_CENTERLIMIT;
+	}
+
+	if (textPoint.x > _vm->_gfx->getBackBufferWidth() - TEXT_CENTERLIMIT) {
+		textPoint.x = _vm->_gfx->getBackBufferWidth() - TEXT_CENTERLIMIT;
+	}
+
+	if (textPoint.x < (TEXT_MARGIN * 2)) {
+		// Text can't be centered if it's too close to the margin
+		return;
+	}
+
+	textWidth = getStringWidth(fontId, text, textLength, flags);
+
+	if (textPoint.x < (_vm->_gfx->getBackBufferWidth() / 2)) {
+		// Fit to right side
+		fitWidth = (textPoint.x - TEXT_MARGIN) * 2;
+	} else {
+		// Fit to left side
+		fitWidth = ((_vm->_gfx->getBackBufferWidth() - TEXT_MARGIN) - textPoint.x) * 2;
+	}
+
+	if (fitWidth < textWidth) {
+		warning("text too long to be displayed in one line");
+		textWidth = fitWidth;
+	}
+	// Entire string fits, draw it
+	textPoint.x = textPoint.x - (textWidth / 2);
+	draw(fontId, text, textLength, textPoint, color, effectColor, flags);
+}
+
+DefaultFont::DefaultFont(SagaEngine *vm) : Font(vm), _fontMapping(0) {
+	int i;
+
+	// Load font module resource context
+
+	assert(_vm->getFontsCount() > 0);
+
+	_fonts.resize(_vm->getFontsCount());
+	for (i = 0; i < _vm->getFontsCount(); i++) {
+#ifdef __DS__
+		_fonts[i].outline.font = NULL;
+		_fonts[i].normal.font = NULL;
+#endif
+		loadFont(&_fonts[i],	_vm->getFontDescription(i)->fontResourceId);
+	}
+}
+
+DefaultFont::~DefaultFont() {
+	debug(8, "DefaultFont::~DefaultFont(): Freeing fonts.");
+
+#ifdef __DS__
+	for (int i = 0; i < _vm->getFontsCount(); i++) {
+		if (_fonts[i].outline.font) {
+			free(_fonts[i].outline.font);
+		}
+
+		if (_fonts[i].normal.font) {
+			free(_fonts[i].normal.font);
 		}
 	}
+#endif
 }
 
-int Font::translateChar(int charId) {
+int DefaultFont::translateChar(int charId) {
 	if (charId <= 127 || (_vm->getLanguage() == Common::RU_RUS && charId <= 254))
 		return charId;					// normal character
 	else
@@ -242,16 +371,12 @@ int Font::translateChar(int charId) {
 // of at most 'count' characters of the string 'text', taking
 // into account any formatting options specified by 'flags'.
 // If 'count' is 0, all characters of 'test' are counted.
-int Font::getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags) {
-	FontData *font;
+int DefaultFont::getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags) {
 	size_t ct;
 	int width = 0;
 	int ch;
 	const byte *txt;
-
-
-	font = getFont(fontId);
-
+	FontData *font = getFont(fontId);
 	txt = (const byte *) text;
 
 	for (ct = count; *txt && (!count || ct > 0); txt++, ct--) {
@@ -269,13 +394,11 @@ int Font::getStringWidth(FontId fontId, const char *text, size_t count, FontEffe
 	return width;
 }
 
-
-void Font::draw(FontId fontId, const char *text, size_t count, const Common::Point &point,
+void DefaultFont::draw(FontId fontId, const char *text, size_t count, const Common::Point &point,
 			   int color, int effectColor, FontEffectFlags flags) {
-	FontData *font;
-	Point offsetPoint(point);
 
-	font = getFont(fontId);
+	Point offsetPoint(point);
+	FontData *font = getFont(fontId);
 
 	if (flags & kFontOutline) {
 		offsetPoint.x--;
@@ -292,7 +415,7 @@ void Font::draw(FontId fontId, const char *text, size_t count, const Common::Poi
 	}
 }
 
-void Font::outFont(const FontStyle &drawFont, const char *text, size_t count, const Common::Point &point, int color, FontEffectFlags flags) {
+void DefaultFont::outFont(const FontStyle &drawFont, const char *text, size_t count, const Common::Point &point, int color, FontEffectFlags flags) {
 	const byte *textPointer;
 	const byte *c_dataPointer;
 	int c_code;
@@ -338,8 +461,8 @@ void Font::outFont(const FontStyle &drawFont, const char *text, size_t count, co
 				} else {
 					// The in-game fonts of the Italian version should not be mapped.
 					// The ones in the intro are hardcoded and should be mapped normally.
-					 if (_vm->_scene->isInIntro())
-						 c_code = translateChar(c_code);
+					if (_vm->_scene->isInIntro())
+						c_code = translateChar(c_code);
 				}
 			}
 		} else if (_fontMapping == 1) {
@@ -410,299 +533,202 @@ void Font::outFont(const FontStyle &drawFont, const char *text, size_t count, co
 	_vm->_render->addDirtyRect(Common::Rect(point.x, point.y, textPoint.x, rowLimit));
 }
 
+void DefaultFont::loadFont(FontData *font, uint32 fontResourceId) {
+	ByteArray fontResourceData;
+	int numBits;
+	int c;
+	ResourceContext *fontContext;
 
-void Font::textDraw(FontId fontId, const char *text, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) {
-	int textWidth;
-	int textLength;
-	int fitWidth;
-	Common::Point textPoint(point);
-
-	textLength = strlen(text);
+	debug(1, "Font::loadFont(): Reading fontResourceId %d...", fontResourceId);
 
-	if (!(flags & kFontCentered)) {
-		// Text is not centered; No formatting required
-		draw(fontId, text, textLength, point, color, effectColor, flags);
-		return;
+	fontContext = _vm->_resource->getContext(GAME_RESOURCEFILE);
+	if (fontContext == NULL) {
+		error("DefaultFont::Font() resource context not found");
 	}
 
-	// Text is centered... format output
-	// Enforce minimum and maximum center points for centered text
-	if (textPoint.x < TEXT_CENTERLIMIT) {
-		textPoint.x = TEXT_CENTERLIMIT;
-	}
+	// Load font resource
+	_vm->_resource->loadResource(fontContext, fontResourceId, fontResourceData);
 
-	if (textPoint.x > _vm->_gfx->getBackBufferWidth() - TEXT_CENTERLIMIT) {
-		textPoint.x = _vm->_gfx->getBackBufferWidth() - TEXT_CENTERLIMIT;
+	if (fontResourceData.size() < FONT_DESCSIZE) {
+		error("DefaultFont::loadFont() Invalid font length (%i < %i)", (int)fontResourceData.size(), FONT_DESCSIZE);
 	}
 
-	if (textPoint.x < (TEXT_MARGIN * 2)) {
-		// Text can't be centered if it's too close to the margin
-		return;
-	}
+	ByteArrayReadStreamEndian readS(fontResourceData, fontContext->isBigEndian());
 
-	textWidth = getStringWidth(fontId, text, textLength, flags);
+	// Read font header
+	font->normal.header.charHeight = readS.readUint16();
+	font->normal.header.charWidth = readS.readUint16();
+	font->normal.header.rowLength = readS.readUint16();
 
-	if (textPoint.x < (_vm->_gfx->getBackBufferWidth() / 2)) {
-		// Fit to right side
-		fitWidth = (textPoint.x - TEXT_MARGIN) * 2;
-	} else {
-		// Fit to left side
-		fitWidth = ((_vm->_gfx->getBackBufferWidth() - TEXT_MARGIN) - textPoint.x) * 2;
-	}
 
-	if (fitWidth < textWidth) {
-		warning("text too long to be displayed in one line");
-		textWidth = fitWidth;
+	debug(2, "Character width: %d", font->normal.header.charWidth);
+	debug(2, "Character height: %d", font->normal.header.charHeight);
+	debug(2, "Row padding: %d", font->normal.header.rowLength);
+
+	for (c = 0; c < FONT_CHARCOUNT; c++) {
+		font->normal.fontCharEntry[c].index = readS.readUint16();
 	}
-	// Entire string fits, draw it
-	textPoint.x = textPoint.x - (textWidth / 2);
-	draw(fontId, text, textLength, textPoint, color, effectColor, flags);
-}
 
-int Font::getHeight(FontId fontId, const char *text, int width, FontEffectFlags flags) {
-	int textWidth;
-	int textLength;
-	int fitWidth;
-	const char *searchPointer;
-	const char *measurePointer;
-	const char *foundPointer;
-	int len;
-	int w;
-	const char *endPointer;
-	int h;
-	int wc;
-	int w_total;
-	int len_total;
-	Common::Point textPoint;
+	for (c = 0; c < FONT_CHARCOUNT; c++) {
+		numBits = font->normal.fontCharEntry[c].width = readS.readByte();
+		font->normal.fontCharEntry[c].byteWidth = getByteLen(numBits);
+	}
 
-	textLength = strlen(text);
+	for (c = 0; c < FONT_CHARCOUNT; c++) {
+		font->normal.fontCharEntry[c].flag = readS.readByte();
+	}
 
-	textWidth = getStringWidth(fontId, text, textLength, flags);
-	h = getHeight(fontId);
-	fitWidth = width;
+	for (c = 0; c < FONT_CHARCOUNT; c++) {
+		font->normal.fontCharEntry[c].tracking = readS.readByte();
+	}
 
-	textPoint.x = (fitWidth / 2);
-	textPoint.y = 0;
+	if (readS.pos() != FONT_DESCSIZE) {
+		error("Invalid font resource size");
+	}
 
-	if (fitWidth >= textWidth) {
-		return h;
+#ifndef __DS__
+	font->normal.font.resize(fontResourceData.size() - FONT_DESCSIZE);
+	memcpy(font->normal.font.getBuffer(), fontResourceData.getBuffer() + FONT_DESCSIZE, fontResourceData.size() - FONT_DESCSIZE);
+#else
+	if (font->normal.font) {
+		free(font->normal.font);
 	}
 
-	// String won't fit on one line
-	w_total = 0;
-	len_total = 0;
-	wc = 0;
+	font->normal.font = (byte *)malloc(fontResourceData.size() - FONT_DESCSIZE);
+	memcpy(font->normal.font, fontResourceData.getBuffer() + FONT_DESCSIZE, fontResourceData.size() - FONT_DESCSIZE);
+#endif
 
-	measurePointer = text;
-	searchPointer = text;
-	endPointer = text + textLength;
+	// Create outline font style
+	createOutline(font);
+}
 
-	for (;;) {
-		foundPointer = strchr(searchPointer, ' ');
-		if (foundPointer == NULL) {
-			// Ran to the end of the buffer
-			len = endPointer - measurePointer;
-		} else {
-			len = foundPointer - measurePointer;
-		}
+void DefaultFont::createOutline(FontData *font) {
+	int i;
+	int row;
+	int newByteWidth;
+	int newRowLength = 0;
+	int currentByte;
+	byte *basePointer;
+	byte *srcPointer;
+	byte *destPointer1;
+	byte *destPointer2;
+	byte *destPointer3;
+	byte charRep;
 
-		w = getStringWidth(fontId, measurePointer, len, flags);
-		measurePointer = foundPointer;
+	// Populate new font style character data
+	for (i = 0; i < FONT_CHARCOUNT; i++) {
+		newByteWidth = 0;
 
-		if ((w_total + w) > fitWidth) {
-			// This word won't fit
-			if (wc == 0) {
-				// The first word in the line didn't fit. Still print it
-				searchPointer = measurePointer + 1;
-			}
-			// Wrap what we've got and restart
-			textPoint.y += h + TEXT_LINESPACING;
-			if (foundPointer == NULL) {
-				// Since word hit NULL but fit, we are done
-				return textPoint.y + h;
-			}
-			w_total = 0;
-			len_total = 0;
-			wc = 0;
-			measurePointer = searchPointer;
-		} else {
-			// Word will fit ok
-			w_total += w;
-			len_total += len;
-			wc++;
-			if (foundPointer == NULL) {
-				// Since word hit NULL but fit, we are done
-				return textPoint.y + h;
-			}
-			searchPointer = measurePointer + 1;
-		}
-	}
-}
+		font->outline.fontCharEntry[i].index = newRowLength;
+		font->outline.fontCharEntry[i].tracking = font->normal.fontCharEntry[i].tracking;
+		font->outline.fontCharEntry[i].flag = font->normal.fontCharEntry[i].flag;
 
-void Font::textDrawRect(FontId fontId, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags) {
-	int textWidth;
-	int textLength;
-	int fitWidth;
-	const char *startPointer;
-	const char *searchPointer;
-	const char *measurePointer;
-	const char *foundPointer;
-	int len;
-	int w;
-	const char *endPointer;
-	int h;
-	int wc;
-	int w_total;
-	int len_total;
-	Common::Point textPoint;
-	Common::Point textPoint2;
+		if (font->normal.fontCharEntry[i].width != 0)
+			newByteWidth = getByteLen(font->normal.fontCharEntry[i].width + 2);
 
-	textLength = strlen(text);
+		font->outline.fontCharEntry[i].width = font->normal.fontCharEntry[i].width + 2;
+		font->outline.fontCharEntry[i].byteWidth = newByteWidth;
 
-	textWidth = getStringWidth(fontId, text, textLength, flags);
-	fitWidth = rect.width();
+		newRowLength += newByteWidth;
+	}
 
-	textPoint.x = rect.left + (fitWidth / 2);
-	textPoint.y = rect.top;
+	debug(2, "New row length: %d", newRowLength);
 
-	if (fitWidth >= textWidth) {
-		// Entire string fits, draw it
-		textPoint.x -= (textWidth / 2);
-		draw(fontId, text, textLength, textPoint, color, effectColor, flags);
-		return;
+	font->outline.header = font->normal.header;
+	font->outline.header.charWidth += 2;
+	font->outline.header.charHeight += 2;
+	font->outline.header.rowLength = newRowLength;
+
+	// Allocate new font representation storage
+#ifdef __DS__
+	if (font->outline.font) {
+		free(font->outline.font);
 	}
 
-	// String won't fit on one line
-	h = getHeight(fontId);
-	w_total = 0;
-	len_total = 0;
-	wc = 0;
+	font->outline.font = (byte *)calloc(newRowLength * font->outline.header.charHeight, 1);
+#else
+	font->outline.font.resize(newRowLength * font->outline.header.charHeight);
+#endif
 
-	startPointer = text;
-	measurePointer = text;
-	searchPointer = text;
-	endPointer = text + textLength;
 
-	for (;;) {
-		foundPointer = strchr(searchPointer, ' ');
-		if (foundPointer == NULL) {
-			// Ran to the end of the buffer
-			len = endPointer - measurePointer;
-		} else {
-			len = foundPointer - measurePointer;
+	// Generate outline font representation
+	for (i = 0; i < FONT_CHARCOUNT; i++) {
+		for (row = 0; row < font->normal.header.charHeight; row++) {
+			for (currentByte = 0; currentByte < font->outline.fontCharEntry[i].byteWidth; currentByte++) {
+				basePointer = &font->outline.font[font->outline.fontCharEntry[i].index + currentByte];
+				destPointer1 = basePointer + newRowLength * row;
+				destPointer2 = basePointer + newRowLength * (row + 1);
+				destPointer3 = basePointer + newRowLength * (row + 2);
+				if (currentByte > 0) {
+					// Get last two columns from previous byte
+					srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1)];
+					charRep = *srcPointer;
+					*destPointer1 |= ((charRep << 6) | (charRep << 7));
+					*destPointer2 |= ((charRep << 6) | (charRep << 7));
+					*destPointer3 |= ((charRep << 6) | (charRep << 7));
+				}
+
+				if (currentByte < font->normal.fontCharEntry[i].byteWidth) {
+					srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte];
+					charRep = *srcPointer;
+					*destPointer1 |= charRep | (charRep >> 1) | (charRep >> 2);
+					*destPointer2 |= charRep | (charRep >> 1) | (charRep >> 2);
+					*destPointer3 |= charRep | (charRep >> 1) | (charRep >> 2);
+				}
+			}
 		}
 
-		w = getStringWidth(fontId, measurePointer, len, flags);
-		measurePointer = foundPointer;
+		// "Hollow out" character to prevent overdraw
+		for (row = 0; row < font->normal.header.charHeight; row++) {
+			for (currentByte = 0; currentByte < font->outline.fontCharEntry[i].byteWidth; currentByte++) {
+				destPointer2 = &font->outline.font[font->outline.header.rowLength * (row + 1) + font->outline.fontCharEntry[i].index + currentByte];
+				if (currentByte > 0) {
+					// Get last two columns from previous byte
+					srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + (currentByte - 1)];
+					*destPointer2 &= ((*srcPointer << 7) ^ 0xFFU);
+				}
 
-		if ((w_total + w) > fitWidth) {
-			// This word won't fit
-			if (wc == 0) {
-				w_total = fitWidth;
-				len_total = len;
+				if (currentByte < font->normal.fontCharEntry[i].byteWidth) {
+					srcPointer = &font->normal.font[font->normal.header.rowLength * row + font->normal.fontCharEntry[i].index + currentByte];
+					*destPointer2 &= ((*srcPointer >> 1) ^ 0xFFU);
+				}
 			}
+		}
+	}
+}
 
-			// Wrap what we've got and restart
-			textPoint2.x = textPoint.x - (w_total / 2);
-			textPoint2.y = textPoint.y;
-			draw(fontId, startPointer, len_total, textPoint2, color, effectColor, flags);
-			textPoint.y += h + TEXT_LINESPACING;
-			if (textPoint.y >= rect.bottom) {
-				return;
-			}
-			w_total = 0;
-			len_total = 0;
-			if (wc == 0 && measurePointer) {
-				searchPointer = measurePointer + 1;
-			}
-			wc = 0;
+SJISFont::SJISFont(SagaEngine *vm) : Font(vm), _font(0) {
+	_font = Graphics::FontSJIS::createFont(vm->getPlatform());
+	assert(_font);
+}
 
-			// Advance the search pointer to the next non-space.
-			// Otherwise, the first "word" to be measured will be
-			// an empty string. Measuring or drawing a string of
-			// length 0 is interpreted as measure/draw the entire
-			// buffer, which certainly is not what we want here.
-			//
-			// This happes because a string may contain several
-			// spaces in a row, e.g. after a period.
+SJISFont::~SJISFont() {
+	delete _font;
+}
 
-			while (*searchPointer == ' ')
-				searchPointer++;
+int SJISFont::getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags) {
+	int res = 0;
+	for (uint16 c = fetchChar(text); c; c = fetchChar(text))
+		res += _font->getCharWidth(c);
+	return res;
+}
 
-			measurePointer = searchPointer;
-			startPointer = searchPointer;
-		} else {
-			// Word will fit ok
-			w_total += w;
-			len_total += len;
-			wc++;
-			if (foundPointer == NULL) {
-				// Since word hit NULL but fit, we are done
-				textPoint2.x = textPoint.x - (w_total / 2);
-				textPoint2.y = textPoint.y;
-				draw(fontId, startPointer, len_total, textPoint2, color,
-					effectColor, flags);
-				return;
-			}
-			searchPointer = measurePointer + 1;
-		}
-	}
+int SJISFont::getHeight(FontId fontId) {
+	return _font->getFontHeight();
 }
 
-Font::FontId Font::knownFont2FontIdx(KnownFont font) {
-	FontId fontId = kSmallFont;
+void SJISFont::draw(FontId fontId, const char *text, size_t count, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) {
 
-	// The demo version of IHNM has 3 font types (like ITE), not 6 (like the full version of IHNM)
-	if (_vm->getGameId() == GID_ITE || _vm->isIHNMDemo()) {
-		switch (font) {
-		case (kKnownFontSmall):
-		default:
-			fontId = kSmallFont;
-			break;
-		case (kKnownFontMedium):
-			fontId = kMediumFont;
-			break;
-		case (kKnownFontBig):
-			fontId = kBigFont;
-			break;
+}
 
-		case (kKnownFontVerb):
-			fontId = kSmallFont;
-			break;
-		case (kKnownFontScript):
-			fontId = kMediumFont;
-			break;
-		case (kKnownFontPause):
-			fontId = _vm->_font->valid(kBigFont) ? kBigFont : kMediumFont;
-			break;
-		}
-#ifdef ENABLE_IHNM
-	} else if (_vm->getGameId() == GID_IHNM && !_vm->isIHNMDemo()) {
-		switch (font) {
-		case (kKnownFontSmall):
-		default:
-			fontId = kSmallFont;
-			break;
-		case (kKnownFontMedium):
-			fontId = kMediumFont;
-			break;
-		case (kKnownFontBig):
-			fontId = kBigFont;
-			break;
+uint16 SJISFont::fetchChar(const char *&s) const {
+	uint16 ch = (uint8)*s++;
 
-		case (kKnownFontVerb):
-			fontId = kIHNMFont8;
-			break;
-		case (kKnownFontScript):
-			fontId = kIHNMMainFont;
-			break;
-		case (kKnownFontPause):
-			fontId = kMediumFont; // unchecked
-			break;
-		}
-#endif
-	}
-	return fontId;
+	if (ch <= 0x7F || (ch >= 0xA1 && ch <= 0xDF))
+		return ch;
+
+	ch |= (uint8)(*s++) << 8;
+	return ch;
 }
 
 } // End of namespace Saga
diff --git a/engines/saga/font.h b/engines/saga/font.h
index 6a483b2867..8c9e323263 100644
--- a/engines/saga/font.h
+++ b/engines/saga/font.h
@@ -28,6 +28,10 @@
 #include "common/list.h"
 #include "saga/gfx.h"
 
+namespace Graphics {
+	class FontSJIS;
+}
+
 namespace Saga {
 
 #define FONT_SHOWUNDEFINED 1	// Define to draw undefined characters * as ?'s
@@ -139,58 +143,71 @@ struct FontData {
 };
 
 class Font {
- public:
-	Font(SagaEngine *vm);
-	~Font();
+public:
+	Font(SagaEngine *vm) : _vm(vm) {}
+	virtual ~Font() {}
+
 	int getStringWidth(KnownFont font, const char *text, size_t count, FontEffectFlags flags) {
 		return getStringWidth(knownFont2FontIdx(font), text, count, flags);
 	}
+
 	int getHeight(KnownFont font) {
 		return getHeight(knownFont2FontIdx(font));
 	}
+
 	int getHeight(KnownFont font, const char *text, int width, FontEffectFlags flags) {
 		return getHeight(knownFont2FontIdx(font), text, width, flags);
 	}
+
 	void textDraw(KnownFont font, const char *string, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) {
 		textDraw(knownFont2FontIdx(font), string, point, color, effectColor, flags);
 	}
+
 	void textDrawRect(KnownFont font, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags) {
 		textDrawRect(knownFont2FontIdx(font), text, rect, color, effectColor, flags);
 	}
+
+	virtual void setFontMapping(int) {}
+
+protected:
+	enum FontId {
+		kSmallFont,
+		kMediumFont,
+		kBigFont,
+		kIHNMUnknown,
+		kIHNMFont8,
+		kIHNMUnknown2,
+		kIHNMMainFont
+	};
+
+	SagaEngine *_vm;
+
+private:
+	FontId knownFont2FontIdx(KnownFont font);
+	int getHeight(FontId fontId, const char *text, int width, FontEffectFlags flags);
+	void textDrawRect(FontId fontId, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags);
+	void textDraw(FontId fontId, const char *string, const Common::Point &point, int color, int effectColor, FontEffectFlags flags);
+
+	virtual int translateChar(int charId) = 0;
+	virtual int getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags) = 0;
+	virtual int getHeight(FontId fontId) = 0;
+	virtual bool valid(FontId) = 0;
+	virtual void draw(FontId fontId, const char *text, size_t count, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) = 0;
+};
+
+class DefaultFont : public Font {
+ public:
+	DefaultFont(SagaEngine *vm);
+	~DefaultFont() override;
+
 	void setFontMapping(int mapping) {
 		_fontMapping = mapping;
 	}
 
  private:
-	 enum FontId {
-		 kSmallFont,
-		 kMediumFont,
-		 kBigFont,
-		 kIHNMUnknown,
-		 kIHNMFont8,
-		 kIHNMUnknown2,
-		 kIHNMMainFont
-	 };
-
-	 Font::FontId knownFont2FontIdx(KnownFont font);
-	 int translateChar(int charId);
-
-	 int getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags);
-	 int getHeight(FontId fontId, const char *text, int width, FontEffectFlags flags);
-	 void textDrawRect(FontId fontId, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags);
-	 void textDraw(FontId fontId, const char *string, const Common::Point &point, int color, int effectColor, FontEffectFlags flags);
-
-	 void loadFont(FontData *font, uint32 fontResourceId);
-	 void createOutline(FontData *font);
-	 void draw(FontId fontId, const char *text, size_t count, const Common::Point &point, int color, int effectColor, FontEffectFlags flags);
-	 void outFont(const FontStyle &drawFont, const char *text, size_t count, const Common::Point &point, int color, FontEffectFlags flags);
-
-	 FontData *getFont(FontId fontId) {
-		 validate(fontId);
-		 return &_fonts[fontId];
-	 }
-
-	int getHeight(FontId fontId) {
+	 int translateChar(int charId) override;
+	 int getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags) override;
+	 int getHeight(FontId fontId) override {
 		 return getFont(fontId)->normal.header.charHeight;
 	 }
 
@@ -199,9 +216,21 @@ class Font {
 			 error("Font::validate: Invalid font id");
 		 }
 	 }
-	 bool valid(FontId fontId) {
+
+	 bool valid(FontId fontId) override {
 		 return (uint(fontId) < _fonts.size());
 	 }
+
+	 FontData *getFont(FontId fontId) {
+		 validate(fontId);
+		 return &_fonts[fontId];
+	 }
+
+	 void draw(FontId fontId, const char *text, size_t count, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) override;
+	 void outFont(const FontStyle &drawFont, const char *text, size_t count, const Common::Point &point, int color, FontEffectFlags flags);
+	 void loadFont(FontData *font, uint32 fontResourceId);
+	 void createOutline(FontData *font);
+ 
 	 int getByteLen(int numBits) const {
 		 int byteLength = numBits / 8;
 
@@ -213,11 +242,27 @@ class Font {
 	 }
 
 	static const int _charMap[128];
-	SagaEngine *_vm;
 
+	Common::Array<FontData> _fonts;
 	int _fontMapping;
+};
 
-	Common::Array<FontData> _fonts;
+class SJISFont : public Font {
+public:
+	SJISFont(SagaEngine *vm);
+	~SJISFont() override;
+
+private:
+	int translateChar(int charId) override { return charId; }
+	int getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags) override;
+	int getHeight(FontId fontId) override;
+	bool valid(FontId fontId) override { return fontId != kBigFont; }
+
+	void draw(FontId fontId, const char *text, size_t count, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) override;
+
+	uint16 fetchChar(const char *&s) const;
+
+	Graphics::FontSJIS *_font;
 };
 
 } // End of namespace Saga
diff --git a/engines/saga/font_map.cpp b/engines/saga/font_map.cpp
index 145956f25f..dba7a21f6d 100644
--- a/engines/saga/font_map.cpp
+++ b/engines/saga/font_map.cpp
@@ -29,7 +29,7 @@
 
 namespace Saga {
 
-const int Font::_charMap[128] = {
+const int DefaultFont::_charMap[128] = {
 	// Characters 0 - 127 are mapped directly to ISO 8859-1
 	199,		// 128 LATIN CAPITAL LETTER C WITH CEDILLA
 	252,		// 129 LATIN SMALL LETTER U WITH DIAERESIS
diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp
index 4f6ad22b44..fdba956a83 100644
--- a/engines/saga/saga.cpp
+++ b/engines/saga/saga.cpp
@@ -254,7 +254,10 @@ Common::Error SagaEngine::run() {
 	_events = new Events(this);
 
 	if (!isSaga2()) {
-		_font = new Font(this);
+		if (getLanguage() == Common::JA_JPN)
+			_font = new SJISFont(this);
+		else
+			_font = new DefaultFont(this);
 		_sprite = new Sprite(this);
 		_script = new SAGA1Script(this);
 	} else {


Commit: 2db289f41952acfca35b84cf7d92c6e53707e112
    https://github.com/scummvm/scummvm/commit/2db289f41952acfca35b84cf7d92c6e53707e112
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:43+01:00

Commit Message:
SAGA: (ITE/PC98) - implement dual layer rendering

- The Japanese font is drawn in double resolution on the top layer. The rest of the graphics gets drawn in standard VGA resolution on the bottom layer, then scaled 2x and merged with the top layer.
- Adjust mouse handling. In dual layer mode the event manager reports mouse coordinates in double resolution, so these have to be scaled down.

Changed paths:
    engines/saga/gfx.cpp
    engines/saga/gfx.h
    engines/saga/input.cpp
    engines/saga/render.cpp
    engines/saga/render.h


diff --git a/engines/saga/gfx.cpp b/engines/saga/gfx.cpp
index 79bd66250a..844c86e15f 100644
--- a/engines/saga/gfx.cpp
+++ b/engines/saga/gfx.cpp
@@ -40,12 +40,18 @@ namespace Saga {
 #define RID_IHNM_HOURGLASS_CURSOR 11 // not in demo
 
 Gfx::Gfx(SagaEngine *vm, OSystem *system, int width, int height) : _vm(vm), _system(system) {
-	initGraphics(width, height);
+	if (vm->getLanguage() == Common::JA_JPN)
+		initGraphics(width << 1, height << 1);
+	else
+		initGraphics(width, height);
 
 	debug(5, "Init screen %dx%d", width, height);
 	// Convert surface data to R surface data
 	_backBuffer.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
 
+	if (vm->getLanguage() == Common::JA_JPN)
+		_sjisBackBuffer.create(width << 1, height << 1, Graphics::PixelFormat::createFormatCLUT8());
+
 	// Start with the cursor shown. It will be hidden before the intro, if
 	// there is an intro. (With boot params, there may not be.)
 	setCursor(kCursorNormal);
@@ -54,6 +60,7 @@ Gfx::Gfx(SagaEngine *vm, OSystem *system, int width, int height) : _vm(vm), _sys
 
 Gfx::~Gfx() {
 	_backBuffer.free();
+	_sjisBackBuffer.free();
 }
 
 #ifdef SAGA_DEBUG
@@ -552,24 +559,31 @@ void Gfx::drawFrame(const Common::Point &p1, const Common::Point &p2, int color)
 // This method adds a dirty rectangle automatically
 void Gfx::drawRect(const Common::Rect &destRect, int color) {
 	_backBuffer.drawRect(destRect, color);
+	_sjisBackBuffer.clearRect2x(destRect);
 	_vm->_render->addDirtyRect(destRect);
 }
 
 // This method adds a dirty rectangle automatically
 void Gfx::fillRect(const Common::Rect &destRect, uint32 color) {
 	_backBuffer.fillRect(destRect, color);
+	// Clear corresponding area of the sjis text layer (if the pixels buffer was actually created)
+	_sjisBackBuffer.clearRect2x(destRect);
 	_vm->_render->addDirtyRect(destRect);
 }
 
 // This method adds a dirty rectangle automatically
 void Gfx::drawRegion(const Common::Rect &destRect, const byte *sourceBuffer) {
 	_backBuffer.blit(destRect, sourceBuffer);
+	// Clear corresponding area of the sjis text layer (if the pixels buffer was actually created)
+	_sjisBackBuffer.clearRect2x(destRect);
 	_vm->_render->addDirtyRect(destRect);
 }
 
 // This method does not add a dirty rectangle automatically
 void Gfx::drawBgRegion(const Common::Rect &destRect, const byte *sourceBuffer) {
 	_backBuffer.blit(destRect, sourceBuffer);
+	// Clear corresponding area of the sjis text layer (if the pixels buffer was actually created)
+	_sjisBackBuffer.clearRect2x(destRect);
 }
 
 
diff --git a/engines/saga/gfx.h b/engines/saga/gfx.h
index ef2c2bde43..314b317505 100644
--- a/engines/saga/gfx.h
+++ b/engines/saga/gfx.h
@@ -115,6 +115,14 @@ struct Surface : Graphics::Surface {
 			fillRect(rect, color);
 		}
 	}
+
+	void clearRect2x(Common::Rect r) {
+	// This clears a 2x scaled rect (used for Japanese font removal).
+	// The pixels buffer only gets allocated for game versions that actually require it.
+		if (!pixels)
+			return;	
+		fillRect(Common::Rect(r.left << 1, r.top << 1, r.right << 1, r.bottom << 1), 0);
+	}
 };
 
 #define PAL_ENTRIES 256
@@ -174,6 +182,8 @@ public:
 	// to add the corresponding dirty rectangle itself
 	void hLine(int x, int y, int x2, uint32 color) {
 		_backBuffer.hLine(x, y, x2, color);
+		// Clear corresponding area of the sjis text layer (if the pixels buffer was actually created)
+		_sjisBackBuffer.clearRect2x(Common::Rect(x, y, x2, y + 1));
 	}
 
 	// WARNING: This method does not add a dirty rectangle automatically.
@@ -181,6 +191,8 @@ public:
 	// to add the corresponding dirty rectangle itself
 	void vLine(int x, int y, int y2, uint32 color) {
 		_backBuffer.vLine(x, y, y2, color);
+		// Clear corresponding area of the sjis text layer (if the pixels buffer was actually created)
+		_sjisBackBuffer.clearRect2x(Common::Rect(x, y, x + 1, y2));
 	}
 
 	// WARNING: This method does not add a dirty rectangle automatically.
@@ -188,6 +200,11 @@ public:
 	// to add the corresponding dirty rectangle itself
 	void setPixelColor(int x, int y, byte color) {
 		((byte *)_backBuffer.getBasePtr(x, y))[0] = color;
+		// Clear corresponding area of the sjis text layer (if the pixels buffer was actually created)
+		if (_sjisBackBuffer.getPixels()) {
+			*((uint16 *)_sjisBackBuffer.getBasePtr(x << 1, y << 1)) = 0;
+			*((uint16 *)_sjisBackBuffer.getBasePtr(x << 1, (y << 1) + 1)) = 0;
+		}
 	}
 
 	// WARNING: This method does not add a dirty rectangle automatically.
@@ -204,24 +221,42 @@ public:
 		return (byte *)_backBuffer.getPixels();
 	}
 
+	// Same as getBackBufferPixels(), but for the hires sjis buffers
+	byte *getSJISBackBufferPixels() {
+		return (byte *)_sjisBackBuffer.getPixels();
+	}
+
 	uint16 getBackBufferWidth() {
 		return _backBuffer.w;
 	}
 
+	uint16 getSJISBackBufferWidth() {
+		return _sjisBackBuffer.w;
+	}
+
 	uint16 getBackBufferHeight() {
 		return _backBuffer.h;
 	}
 
+	uint16 getSJISBackBufferHeight() {
+		return _sjisBackBuffer.h;
+	}
+
 	uint16 getBackBufferPitch() {
 		return _backBuffer.pitch;
 	}
 
+	uint16 getSJISBackBufferPitch() {
+		return _sjisBackBuffer.pitch;
+	}
+
 	void getBackBufferRect(Common::Rect &rect) {
 		_backBuffer.getRect(rect);
 	}
 
 private:
 	Surface _backBuffer;
+	Surface _sjisBackBuffer;
 	byte _currentPal[PAL_ENTRIES * 3];
 	OSystem *_system;
 	SagaEngine *_vm;
diff --git a/engines/saga/input.cpp b/engines/saga/input.cpp
index ac253b6de2..47dc15a28e 100644
--- a/engines/saga/input.cpp
+++ b/engines/saga/input.cpp
@@ -39,6 +39,13 @@ int SagaEngine::processInput() {
 	Common::Event event;
 
 	while (_eventMan->pollEvent(event)) {
+		// Scale down mouse coordinates for the Japanese version which runs in double resolution internally.
+		if ((event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_RBUTTONDOWN ||
+			event.type == Common::EVENT_WHEELUP || event.type == Common::EVENT_WHEELDOWN) && getLanguage() == Common::JA_JPN) {
+				event.mouse.x >>= 1;
+				event.mouse.y >>= 1;
+		}
+
 		switch (event.type) {
 		case Common::EVENT_KEYDOWN:
 			if (_interface->_textInput || _interface->_statusTextInput) {
@@ -145,7 +152,14 @@ int SagaEngine::processInput() {
 }
 
 Point SagaEngine::mousePos() const {
-	return _eventMan->getMousePos();
+	Common::Point pos = _eventMan->getMousePos();
+	// Scale down mouse coordinates for the Japanese version which runs in double resolution internally.
+	if (getLanguage() == Common::JA_JPN) {
+		pos.x >>= 1;
+		pos.y >>= 1;
+	}
+
+	return pos;
 }
 
 } // End of namespace Saga
diff --git a/engines/saga/render.cpp b/engines/saga/render.cpp
index b932e228ad..eb345aa98e 100644
--- a/engines/saga/render.cpp
+++ b/engines/saga/render.cpp
@@ -47,6 +47,7 @@ Render::Render(SagaEngine *vm, OSystem *system) {
 	_system = system;
 	_initialized = false;
 	_fullRefresh = true;
+	_dualSurface = (vm->getLanguage() == Common::JA_JPN);
 
 #ifdef SAGA_DEBUG
 	// Initialize FPS timer callback
@@ -55,6 +56,9 @@ Render::Render(SagaEngine *vm, OSystem *system) {
 
 	_backGroundSurface.create(_vm->getDisplayInfo().width, _vm->getDisplayInfo().height, Graphics::PixelFormat::createFormatCLUT8());
 
+	if (_dualSurface)
+		_mergeSurface.create(_vm->getDisplayInfo().width << 1, _vm->getDisplayInfo().height << 1, Graphics::PixelFormat::createFormatCLUT8());
+
 	_flags = 0;
 
 	_initialized = true;
@@ -66,6 +70,7 @@ Render::~Render() {
 #endif
 
 	_backGroundSurface.free();
+	_mergeSurface.free();
 
 	_initialized = false;
 }
@@ -242,13 +247,20 @@ void Render::addDirtyRect(Common::Rect r) {
 		_dirtyRects.push_back(r);
 }
 
+#define mCopyRectToScreen(x, y, w, h) \
+	if (_dualSurface) { \
+		scale2xAndMergeOverlay(x, y, w, h); \
+		_system->copyRectToScreen(_mergeSurface.getPixels(), _mergeSurface.pitch, x << 1, y << 1, w << 1, h << 1); \
+	} else \
+		_system->copyRectToScreen(_vm->_gfx->getBackBufferPixels(), _vm->_gfx->getBackBufferWidth(), x, y, w, h)
+
 void Render::restoreChangedRects() {
 	if (!_fullRefresh) {
-		Common::List<Common::Rect>::const_iterator it;
-		for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
+		for (Common::List<Common::Rect>::const_iterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
 			//_backGroundSurface.frameRect(*it, 1);		// DEBUG
-			if (_vm->_interface->getFadeMode() != kFadeOut)
-				g_system->copyRectToScreen(_vm->_gfx->getBackBufferPixels(), _backGroundSurface.w, it->left, it->top, it->width(), it->height());
+			if (_vm->_interface->getFadeMode() != kFadeOut) {
+				mCopyRectToScreen(it->left, it->top, it->width(), it->height());
+			}
 		}
 	}
 	_dirtyRects.clear();
@@ -256,20 +268,55 @@ void Render::restoreChangedRects() {
 
 void Render::drawDirtyRects() {
 	if (!_fullRefresh) {
-		Common::List<Common::Rect>::const_iterator it;
-		for (it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
+		for (Common::List<Common::Rect>::const_iterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
 			//_backGroundSurface.frameRect(*it, 2);		// DEBUG
-			if (_vm->_interface->getFadeMode() != kFadeOut)
-				g_system->copyRectToScreen(_vm->_gfx->getBackBufferPixels(), _backGroundSurface.w, it->left, it->top, it->width(), it->height());
+			if (_vm->_interface->getFadeMode() != kFadeOut) {
+				mCopyRectToScreen(it->left, it->top, it->width(), it->height());
+			}
 		}
 	} else {
-		_system->copyRectToScreen(_vm->_gfx->getBackBufferPixels(), _vm->_gfx->getBackBufferWidth(), 0, 0,
-								  _vm->_gfx->getBackBufferWidth(), _vm->_gfx->getBackBufferHeight());
+		mCopyRectToScreen(0, 0, _backGroundSurface.w, _backGroundSurface.h);
 	}
-
 	_dirtyRects.clear();
 }
 
+#undef mCopyRectToScreen
+
+void Render::scale2xAndMergeOverlay(int x, int y, int w, int h) {
+	int src0Pitch = _vm->_gfx->getBackBufferPitch();
+	int src1Pitch = _vm->_gfx->getSJISBackBufferPitch();
+	int dst1Pitch = _mergeSurface.pitch;
+	const byte *src00 = _vm->_gfx->getBackBufferPixels() + y * src0Pitch + x;
+	const byte *src10 = _vm->_gfx->getSJISBackBufferPixels() + y * 2 * src1Pitch + x * 2;
+	const byte *src11 = src10 + src1Pitch;
+	byte *dst10 = (byte*)_mergeSurface.getBasePtr(x << 1, y << 1);
+	byte *dst11 = dst10 + dst1Pitch;
+	src0Pitch -= w;
+	src1Pitch += (src1Pitch - (w << 1));
+	dst1Pitch += (dst1Pitch - (w << 1));
+
+	while (h--) {
+		for (int i = 0; i < w; ++i) {
+			// v0: pixels from "normal" surface that have to be scaled
+			// v1: pixels from hires text surface that go on top
+			uint8 v0 = *src00++;
+			uint8 v1 = *src10++;
+			*dst10++ = v1 ? v1 : v0;
+			v1 = *src10++;
+			*dst10++ = v1 ? v1 : v0;
+			v1 = *src11++;
+			*dst11++ = v1 ? v1 : v0;
+			v1 = *src11++;
+			*dst11++ = v1 ? v1 : v0;
+		}
+		src00 += src0Pitch;
+		src10 += src1Pitch;
+		src11 += src1Pitch;
+		dst10 += dst1Pitch;
+		dst11 += dst1Pitch;
+	}
+}
+
 #ifdef SAGA_DEBUG
 void Render::fpsTimerCallback(void *refCon) {
 	((Render *)refCon)->fpsTimer();
diff --git a/engines/saga/render.h b/engines/saga/render.h
index 4aaaf61eb4..2851c6c18e 100644
--- a/engines/saga/render.h
+++ b/engines/saga/render.h
@@ -91,6 +91,7 @@ public:
 	}
 
 	void drawDirtyRects();
+	void scale2xAndMergeOverlay(int x, int y, int w, int h);
 	void restoreChangedRects();
 
 private:
@@ -106,9 +107,11 @@ private:
 	bool _initialized;
 	Common::List<Common::Rect> _dirtyRects;
 	bool _fullRefresh;
+	bool _dualSurface;
 
 	// Module data
 	Surface _backGroundSurface;
+	Surface _mergeSurface;
 
 	uint32 _flags;
 };


Commit: cf08894520612c5b40132c78394547c632577471
    https://github.com/scummvm/scummvm/commit/cf08894520612c5b40132c78394547c632577471
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:43+01:00

Commit Message:
GRAPHICS: (SJIS) - SAGA adjustments

Changed paths:
    graphics/sjis.h


diff --git a/graphics/sjis.h b/graphics/sjis.h
index f1b4d9a05d..19500ec043 100644
--- a/graphics/sjis.h
+++ b/graphics/sjis.h
@@ -26,7 +26,7 @@
 // for dynamic engine plugins.
 // If you plan to use this code in another engine, you will have
 // to add the proper define check here.
-#if !(defined(ENABLE_KYRA) || defined(ENABLE_SCI) || defined(ENABLE_SCUMM) || defined(DYNAMIC_MODULES))
+#if !(defined(ENABLE_KYRA) || defined(ENABLE_SCI) || defined(ENABLE_SCUMM) || defined(ENABLE_SAGA) || defined(DYNAMIC_MODULES))
 
 // If neither of the above mentioned is enabled, do not include the SJIS code.
 


Commit: 30704a98dd1f01af09dd7891c642c1ac79561fd4
    https://github.com/scummvm/scummvm/commit/30704a98dd1f01af09dd7891c642c1ac79561fd4
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:43+01:00

Commit Message:
SAGA: (ITE/PC98) -adjustments to hardcoded intro data

(text colors and layout for the Japanese version)

Changed paths:
    engines/saga/introproc_ite.cpp
    engines/saga/itedata.cpp
    engines/saga/itedata.h
    engines/saga/saga.cpp
    engines/saga/saga.h


diff --git a/engines/saga/introproc_ite.cpp b/engines/saga/introproc_ite.cpp
index 41e6ae1bda..d40bbf3dfd 100644
--- a/engines/saga/introproc_ite.cpp
+++ b/engines/saga/introproc_ite.cpp
@@ -121,10 +121,10 @@ EventColumns *Scene::queueIntroDialogue(EventColumns *eventColumns, int n_dialog
 
 	// Queue narrator dialogue list
 	textEntry.knownColor = kKnownColorSubtitleTextColor;
-	textEntry.effectKnownColor = kKnownColorTransparent;
+	textEntry.effectKnownColor = (_vm->getPlatform() == Common::kPlatformPC98) ? kKnownColorSubtitleEffectColorPC98 : kKnownColorTransparent;
 	textEntry.useRect = true;
-	textEntry.rect.left = 0;
-	textEntry.rect.right = _vm->getDisplayInfo().width;
+	textEntry.rect.left = (_vm->getPlatform() == Common::kPlatformPC98) ? 10 : 0;
+	textEntry.rect.right = _vm->getDisplayInfo().width - (_vm->getPlatform() == Common::kPlatformPC98 ? 20 : 0);
 	if (_vm->getLanguage() == Common::DE_DEU) {
 		textEntry.rect.top = INTRO_DE_CAPTION_Y;
 	} else if (_vm->getLanguage() == Common::IT_ITA) {
@@ -140,6 +140,11 @@ EventColumns *Scene::queueIntroDialogue(EventColumns *eventColumns, int n_dialog
 
 	for (i = 0; i < n_dialogues; i++) {
 		textEntry.text = dialogue[i].i_str;
+
+		// For the Japanese version align each string to the bottom of the screen
+		if (_vm->getLanguage() == Common::JA_JPN)
+			textEntry.rect.top = textEntry.rect.bottom - _vm->_font->getHeight(textEntry.font, textEntry.text, textEntry.rect.width(), textEntry.flags);
+
 		entry = _vm->_scene->_textList.addEntry(textEntry);
 
 		if (_vm->_subtitlesEnabled) {
@@ -198,6 +203,8 @@ EventColumns *Scene::queueCredits(int delta_time, int duration, int n_credits, c
 		game = kITECreditsWyrmKeep;
 	else if (_vm->getPlatform() == Common::kPlatformMacintosh)
 		game = kITECreditsMac;
+	else if (_vm->getPlatform() == Common::kPlatformPC98)
+		game = kITECreditsPC98;
 	else if (_vm->getFeatures() & GF_EXTRA_ITE_CREDITS)
 		game = kITECreditsPCCD;
 	else
@@ -234,6 +241,9 @@ EventColumns *Scene::queueCredits(int delta_time, int duration, int n_credits, c
 			error("Unknown credit type");
 		}
 
+		if (_vm->getPlatform() == Common::kPlatformPC98)
+			line_spacing -= 2;
+
 		credits_height += (_vm->_font->getHeight(font) + line_spacing);
 	}
 
@@ -275,6 +285,9 @@ EventColumns *Scene::queueCredits(int delta_time, int duration, int n_credits, c
 			break;
 		}
 
+		if (_vm->getPlatform() == Common::kPlatformPC98)
+			line_spacing -= 2;
+
 		textEntry.text = credits[i].string;
 		textEntry.font = font;
 		textEntry.point.y = y;
@@ -731,8 +744,7 @@ int Scene::ITEIntroFaireTentProc(int param) {
 		_vm->_events->chain(eventColumns, event);
 
 		// Queue PC98 extra credits
-		if (_vm->getPlatform() == Common::kPlatformPC98)
-			eventColumns = queueCredits(DISSOLVE_DURATION, CREDIT_DURATION1, ARRAYSIZE(creditsTent), creditsTent);
+		eventColumns = queueCredits(DISSOLVE_DURATION, CREDIT_DURATION1, ARRAYSIZE(creditsTent), creditsTent);
 
 		// End scene after momentary pause
 		event.type = kEvTOneshot;
diff --git a/engines/saga/itedata.cpp b/engines/saga/itedata.cpp
index 5a7511be1d..3ce36e5f39 100644
--- a/engines/saga/itedata.cpp
+++ b/engines/saga/itedata.cpp
@@ -1181,6 +1181,7 @@ const IntroCredit creditsTreeHouse1[] = {
 	{Common::DE_DEU, kITECreditsAny, kITECreditsText, "und Carolly Hauksdottir"},
 	{Common::IT_ITA, kITECreditsAny, kITECreditsText, "e Carolly Hauksdottir"},
 	{Common::FR_FRA, kITECreditsAny, kITECreditsText, "et Carolly Hauksdottir"},
+	{Common::JA_JPN, kITECreditsAny, kITECreditsText, "and Carolly Hauksdottir"},
 	{Common::EN_ANY, kITECreditsAny, kITECreditsHeader, "Screenplay and Dialog"},
 	{Common::EN_ANY, kITECreditsAny, kITECreditsText, "Robert Leh, Len Wein, and Bill Rotsler"},
 	{Common::DE_DEU, kITECreditsAny, kITECreditsHeader, "Geschichte und Dialoge"},
@@ -1190,7 +1191,8 @@ const IntroCredit creditsTreeHouse1[] = {
 	{Common::FR_FRA, kITECreditsAny, kITECreditsHeader, "Sc\202nario et Dialogues"},
 	{Common::FR_FRA, kITECreditsAny, kITECreditsText, "Robert Leh, Len Wein et Bill Rotsler"},
 	{Common::JA_JPN, kITECreditsAny, kITECreditsHeader, "Screenplay and Dialog"},
-	{Common::JA_JPN, kITECreditsAny, kITECreditsText, "Robert Leh, Len Wein, and Bill Rotsler"},
+	{Common::JA_JPN, kITECreditsAny, kITECreditsText, "Robert Leh, Len Wein"},
+	{Common::JA_JPN, kITECreditsAny, kITECreditsText, "and Bill Rotsler"}
 };
 
 const IntroCredit creditsTreeHouse2[] = {
@@ -1202,7 +1204,11 @@ const IntroCredit creditsTreeHouse2[] = {
 	{Common::FR_FRA, kITECreditsAny, kITECreditsHeader, "Graphismes"},
 	{Common::JA_JPN, kITECreditsAny, kITECreditsHeader, "Art"},
 	{Common::UNK_LANG, kITECreditsWyrmKeep, kITECreditsText, "Ed Lacabanne, Glenn Price, April Lee,"},
-	{Common::UNK_LANG, kITECreditsNotWyrmKeep, kITECreditsText, "Edward Lacabanne, Glenn Price, April Lee,"},
+	{Common::EN_ANY, kITECreditsNotWyrmKeep, kITECreditsText, "Edward Lacabanne, Glenn Price, April Lee,"},
+	{Common::DE_DEU, kITECreditsNotWyrmKeep, kITECreditsText, "Edward Lacabanne, Glenn Price, April Lee,"},
+	{Common::IT_ITA, kITECreditsNotWyrmKeep, kITECreditsText, "Edward Lacabanne, Glenn Price, April Lee,"},
+	{Common::FR_FRA, kITECreditsNotWyrmKeep, kITECreditsText, "Edward Lacabanne, Glenn Price, April Lee,"},
+	{Common::JA_JPN, kITECreditsNotWyrmKeep, kITECreditsText, "Edward Lacabanne, Glenn Price,"},
 	{Common::UNK_LANG, kITECreditsWyrmKeep, kITECreditsText, "Lisa Sample, Brian Dowrick, Reed Waller,"},
 	{Common::EN_ANY, kITECreditsWyrmKeep, kITECreditsText, "Allison Hershey and Talin"},
 	{Common::DE_DEU, kITECreditsWyrmKeep, kITECreditsText, "Allison Hershey und Talin"},
@@ -1210,11 +1216,12 @@ const IntroCredit creditsTreeHouse2[] = {
 	{Common::FR_FRA, kITECreditsWyrmKeep, kITECreditsText, "Allison Hershey et Talin"},
 	{Common::EN_ANY, kITECreditsNotWyrmKeep, kITECreditsText, "Lisa Iennaco, Brian Dowrick, Reed"},
 	{Common::EN_ANY, kITECreditsNotWyrmKeep, kITECreditsText, "Waller, Allison Hershey and Talin"},
-	{Common::JA_JPN, kITECreditsNotWyrmKeep, kITECreditsText, "Lisa Iennaco, Brian Dowrick, Reed"},
-	{Common::JA_JPN, kITECreditsNotWyrmKeep, kITECreditsText, "Waller, Allison Hershey and Talin"},
 	{Common::DE_DEU, kITECreditsAny, kITECreditsText, "Waller, Allison Hershey und Talin"},
 	{Common::IT_ITA, kITECreditsAny, kITECreditsText, "Waller, Allison Hershey e Talin"},
 	{Common::FR_FRA, kITECreditsAny, kITECreditsText, "Waller, Allison Hershey et Talin"},
+	{Common::JA_JPN, kITECreditsAny, kITECreditsText, "April Lee, Lisa Iennaco,"},
+	{Common::JA_JPN, kITECreditsAny, kITECreditsText, "Brian Dowrick, Reed Waller,"},
+	{Common::JA_JPN, kITECreditsAny, kITECreditsText, "Allison Hershey and Talin"},
 	{Common::EN_ANY, kITECreditsNotWyrmKeep, kITECreditsHeader, "Art Direction"},
 	{Common::DE_DEU, kITECreditsNotWyrmKeep, kITECreditsHeader, "Grafische Leitung"},
 	{Common::IT_ITA, kITECreditsNotWyrmKeep, kITECreditsHeader, "Direzione Grafica"},
@@ -1261,12 +1268,12 @@ const IntroCredit creditsFairePath2[] = {
 };
 
 const IntroCredit creditsTent[6] = {
-	{Common::JA_JPN, kITECreditsAny, kITECreditsHeader, "NEC PC-9821 version by"},
-	{Common::JA_JPN, kITECreditsAny, kITECreditsText, "STARCRAFT,Inc."},
-	{Common::JA_JPN, kITECreditsAny, kITECreditsHeader, "Programming"},
-	{Common::JA_JPN, kITECreditsAny, kITECreditsText, "Toshio Sato"},
-	{Common::JA_JPN, kITECreditsAny, kITECreditsHeader, "Music and Sound"},
-	{Common::JA_JPN, kITECreditsAny, kITECreditsText, "Takeshi Abo"}
+	{Common::JA_JPN, kITECreditsPC98, kITECreditsHeader, "NEC PC-9821 version by"},
+	{Common::JA_JPN, kITECreditsPC98, kITECreditsText, "STARCRAFT,Inc."},
+	{Common::JA_JPN, kITECreditsPC98, kITECreditsHeader, "Programming"},
+	{Common::JA_JPN, kITECreditsPC98, kITECreditsText, "Toshio Sato"},
+	{Common::JA_JPN, kITECreditsPC98, kITECreditsHeader, "Music and Sound"},
+	{Common::JA_JPN, kITECreditsPC98, kITECreditsText, "Takeshi Abo"}
 };
 
 } // End of namespace Saga
diff --git a/engines/saga/itedata.h b/engines/saga/itedata.h
index 23fac1575c..dad7479928 100644
--- a/engines/saga/itedata.h
+++ b/engines/saga/itedata.h
@@ -101,6 +101,7 @@ enum {
 	kITECreditsPCCD         = (1 << 1),
 	kITECreditsMac          = (1 << 2),
 	kITECreditsWyrmKeep     = (1 << 3),
+	kITECreditsPC98		    = (1 << 4),
 	kITECreditsAny          = 0xffff,
 	kITECreditsNotWyrmKeep  = kITECreditsAny & ~kITECreditsWyrmKeep
 };
@@ -138,8 +139,8 @@ extern const IntroDialogue introDialogueCave3[][3];
 extern const IntroDialogue introDialogueCave4[][4];
 
 extern const IntroCredit creditsValley[19];
-extern const IntroCredit creditsTreeHouse1[20];
-extern const IntroCredit creditsTreeHouse2[27];
+extern const IntroCredit creditsTreeHouse1[22];
+extern const IntroCredit creditsTreeHouse2[32];
 extern const IntroCredit creditsFairePath1[23];
 extern const IntroCredit creditsFairePath2[8];
 extern const IntroCredit creditsTent[6];
diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp
index fdba956a83..4905cfcd2c 100644
--- a/engines/saga/saga.cpp
+++ b/engines/saga/saga.cpp
@@ -576,6 +576,9 @@ ColorId SagaEngine::KnownColor2ColorId(KnownColor knownColor) {
 		case (kKnownColorSubtitleTextColor):
 			colorId = (ColorId)255;
 			break;
+		case (kKnownColorSubtitleEffectColorPC98):
+			colorId = (ColorId)210;
+			break;
 		case (kKnownColorVerbText):
 			colorId = kITEColorBlue;
 			break;
diff --git a/engines/saga/saga.h b/engines/saga/saga.h
index 0d8b5634c6..514031f7fe 100644
--- a/engines/saga/saga.h
+++ b/engines/saga/saga.h
@@ -338,6 +338,7 @@ enum KnownColor {
 	kKnownColorBlack,
 
 	kKnownColorSubtitleTextColor,
+	kKnownColorSubtitleEffectColorPC98,
 	kKnownColorVerbText,
 	kKnownColorVerbTextShadow,
 	kKnownColorVerbTextActive


Commit: 7d75487d93447912e2143d324310e73bc4cc09fb
    https://github.com/scummvm/scummvm/commit/7d75487d93447912e2143d324310e73bc4cc09fb
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:43+01:00

Commit Message:
SAGA: (ITE/PC98) - implement original mouse cursor

Like the DOS version the PC-98 version has a hard-coded cursor. It has a size of 16x16 though, so it is suitable for a higher resolution.

Changed paths:
    engines/saga/gfx.cpp
    engines/saga/gfx.h


diff --git a/engines/saga/gfx.cpp b/engines/saga/gfx.cpp
index 844c86e15f..baef59af4e 100644
--- a/engines/saga/gfx.cpp
+++ b/engines/saga/gfx.cpp
@@ -471,7 +471,7 @@ void Gfx::setCursor(CursorType cursorType) {
 		const byte A = kITEColorLightGrey;
 		const byte B = kITEColorWhite;
 
-		const byte cursor_img[CURSOR_W * CURSOR_H] = {
+		const byte cursor_img_default[CURSOR_W * CURSOR_H] = {
 			0, 0, 0, A, 0, 0, 0,
 			0, 0, 0, A, 0, 0, 0,
 			0, 0, 0, A, 0, 0, 0,
@@ -481,7 +481,29 @@ void Gfx::setCursor(CursorType cursorType) {
 			0, 0, 0, A, 0, 0, 0,
 		};
 
-		CursorMan.replaceCursor(cursor_img, CURSOR_W, CURSOR_H, 3, 3, 0);
+		const byte cursor_img_pc98[CURSOR_PC98_W * CURSOR_PC98_H] = {
+			A, A, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			A, B, A, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			A, B, B, A, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			A, B, B, B, A, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			A, B, B, B, B, A, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			A, B, B, B, B, B, A, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+			A, B, B, B, B, B, B, A, 0, 0, 0, 0, 0, 0, 0, 0,
+			A, B, B, B, B, B, B, B, A, 0, 0, 0, 0, 0, 0, 0,
+			A, B, B, B, B, B, B, B, B, A, 0, 0, 0, 0, 0, 0,
+			A, B, B, B, B, B, B, B, B, B, A, 0, 0, 0, 0, 0,
+			A, B, B, B, B, B, B, B, B, B, B, A, 0, 0, 0, 0,
+			A, B, B, B, B, B, B, B, B, B, B, B, A, 0, 0, 0,
+			A, B, B, B, B, B, B, B, B, B, B, B, B, A, 0, 0,
+			A, A, A, A, A, A, B, B, B, A, A, A, A, A, A, 0,
+			0, 0, 0, 0, 0, 0, A, B, B, B, A, 0, 0, 0, 0, 0,
+			0, 0, 0, 0, 0, 0, 0, A, A, A, A, A, 0, 0, 0, 0
+		};
+
+		if (_vm->getPlatform() == Common::kPlatformPC98)
+			CursorMan.replaceCursor(cursor_img_pc98, CURSOR_PC98_W, CURSOR_PC98_H, 0, 0, 0);
+		else
+			CursorMan.replaceCursor(cursor_img_default, CURSOR_W, CURSOR_H, 3, 3, 0);
 	} else {
 		uint32 resourceId;
 
diff --git a/engines/saga/gfx.h b/engines/saga/gfx.h
index 314b317505..5dd3a9672a 100644
--- a/engines/saga/gfx.h
+++ b/engines/saga/gfx.h
@@ -129,6 +129,8 @@ struct Surface : Graphics::Surface {
 
 #define CURSOR_W 7
 #define CURSOR_H 7
+#define CURSOR_PC98_W 16
+#define CURSOR_PC98_H 16
 
 #define CURSOR_ORIGIN_X 4
 #define CURSOR_ORIGIN_Y 4


Commit: 8306fee7e3d874eb3b6809dc28a76627f66ccec0
    https://github.com/scummvm/scummvm/commit/8306fee7e3d874eb3b6809dc28a76627f66ccec0
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:44+01:00

Commit Message:
SAGA: (ITE/PC98) - set GF_ITE_FLOPPY flag

This version behaves like a DOS floppy version in all the locations where we check for the  GF_ITE_FLOPPY flag

Changed paths:
    engines/saga/detection_tables.h


diff --git a/engines/saga/detection_tables.h b/engines/saga/detection_tables.h
index d9771cd3bd..44e2339ca4 100644
--- a/engines/saga/detection_tables.h
+++ b/engines/saga/detection_tables.h
@@ -643,7 +643,7 @@ static const SAGAGameDescription gameDescriptions[] = {
  			GUIO0()
  		},
  		GID_ITE,
- 		0,
+		GF_ITE_FLOPPY,	// Even it that game version comes on a CD it behaves like a DOS floppy version
  		ITE_DEFAULT_SCENE,
  		&ITE_Resources,
  		ARRAYSIZE(ITE_GameFonts),


Commit: ffdd648c885cdcf3c8a9b761e6848f58725b47f3
    https://github.com/scummvm/scummvm/commit/ffdd648c885cdcf3c8a9b761e6848f58725b47f3
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:44+01:00

Commit Message:
SAGA: (ITE/PC98) - fix option panel string

Changed paths:
    engines/saga/itedata.cpp


diff --git a/engines/saga/itedata.cpp b/engines/saga/itedata.cpp
index 3ce36e5f39..e91ea43846 100644
--- a/engines/saga/itedata.cpp
+++ b/engines/saga/itedata.cpp
@@ -479,7 +479,7 @@ const char *ITEinterfaceTextStrings[][53] = {
 		// "オン", "オフ", "ゲーム再開", "ロード", "セーブ",
 		"\x83I\x83\x93", "\x83I\x83t", "\x83Q\x81[\x83\x80\x8D\xC4\x8AJ", "\x83\x8D\x81[\x83h", "\x83Z\x81[\x83u",
 		// "ゲームオプション", "表示速度", "音楽", "効果音", "無効",
-		"\x83Q\x81[\x83\x80\x83I\x83v\x83V\x83\x87\x83\x93", "\x95\\x8E\xA6\x91\xAC\x93x", "\x89\xB9\x8Ay", "\x8C\xF8\x89\xCA\x89\xB9", "\x96\xB3\x8C\xF8",
+		"\x83Q\x81[\x83\x80\x83I\x83v\x83V\x83\x87\x83\x93", "\x95""\\""\x8e\xa6\x91\xac\x93""x", "\x89\xB9\x8Ay", "\x8C\xF8\x89\xCA\x89\xB9", "\x96\xB3\x8C\xF8",
 		// "終了", "OK", "普通", "クリック", "10%",
 		"\x8FI\x97\xB9", "\x82n\x82j", "\x95\x81\x92\xCA", "\x83N\x83\x8A\x83\x62\x83N", "\x82P\x82O\x81\x93",
 		// "20%", "30%", "40%", "50%", "60%",


Commit: c16f00f0f64df820fa5599b10b5153cbfb1c0436
    https://github.com/scummvm/scummvm/commit/c16f00f0f64df820fa5599b10b5153cbfb1c0436
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:44+01:00

Commit Message:
SAGA: (ITE/PC98) - make version specific interface changes

The font style is diiferent for various strings (e.g. verbs are drawn outlined instead of shaded, converse line are drawn normally instead of shaded).
Also implement sjis version of "bullet" for the converse strings.

Changed paths:
    engines/saga/interface.cpp


diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp
index 0456f6f0af..4855b533f3 100644
--- a/engines/saga/interface.cpp
+++ b/engines/saga/interface.cpp
@@ -921,19 +921,21 @@ void Interface::drawPanelText(InterfacePanel *panel, PanelButton *panelButton) {
 	} else {
 		// The standard case is used for the things that look a bit like buttons
 		// but are not clickable, e.g. texts like "Music", "Sound", etc.
-		if (_vm->getGameId() == GID_ITE) {
+		if (_vm->getGameId() == GID_ITE && _vm->getPlatform() != Common::kPlatformPC98) {
 			rect.left = rect.right - textWidth - 3;
 		} else {
 			rect.left = (rect.right + rect.left - textWidth) / 2;
+			if (_vm->getGameId() == GID_ITE)
+				rect.left += 4;
 		}
 		rect.top = (rect.top + rect.bottom - textHeight) / 2;
 	}
 
 	textPoint.x = rect.left;
-	textPoint.y = rect.top + 1;
+	textPoint.y = rect.top + (_vm->getPlatform() == Common::kPlatformPC98 ? 0 : 1);
 
 	_vm->_font->textDraw(textFont, text, textPoint,
-						_vm->KnownColor2ColorId(kKnownColorVerbText), _vm->KnownColor2ColorId(textShadowKnownColor), kFontShadow);
+						_vm->KnownColor2ColorId(kKnownColorVerbText), _vm->KnownColor2ColorId(textShadowKnownColor), _vm->getPlatform() == Common::kPlatformPC98 ?  kFontOutline : kFontShadow);
 }
 
 void Interface::drawOption() {
@@ -2390,7 +2392,7 @@ void Interface::drawPanelButtonText(InterfacePanel *panel, PanelButton *panelBut
 	}
 
 	_vm->_font->textDraw(textFont, text, point,
-		_vm->KnownColor2ColorId(textColor), _vm->KnownColor2ColorId(textShadowKnownColor), kFontShadow);
+		_vm->KnownColor2ColorId(textColor), _vm->KnownColor2ColorId(textShadowKnownColor), _vm->getPlatform() == Common::kPlatformPC98 ?  kFontOutline : kFontShadow);
 }
 
 void Interface::drawPanelButtonArrow(InterfacePanel *panel, PanelButton *panelButton) {
@@ -2443,7 +2445,7 @@ void Interface::drawVerbPanelText(PanelButton *panelButton, KnownColor textKnown
 
 	_vm->_font->textDraw(kKnownFontVerb, text, point,
 						_vm->KnownColor2ColorId(textKnownColor), _vm->KnownColor2ColorId(textShadowKnownColor),
-						(textShadowKnownColor != kKnownColorTransparent) ? kFontShadow : kFontNormal);
+						(textShadowKnownColor != kKnownColorTransparent) ? (_vm->getPlatform() == Common::kPlatformPC98 ?  kFontOutline : kFontShadow) : kFontNormal);
 }
 
 
@@ -2553,9 +2555,10 @@ void Interface::converseDisplayTextLines() {
 	byte bulletForegnd;
 	byte bulletBackgnd;
 	const char *str;
-	char bullet[2] = {
-		(char)0xb7, 0
+	static const char bulletStr[3][3] = {
+		"\xb7", "\x81\x45", ">"
 	};
+	const char *bullet = (_vm->getGameId() == GID_ITE) ? (_vm->getPlatform() == Common::kPlatformPC98 ? bulletStr[1] : bulletStr[0]) : bulletStr[2];
 
 	assert(_conversePanel.buttonsCount >= 6);
 	Rect rect(8, _vm->getDisplayInfo().converseTextLines * _vm->getDisplayInfo().converseTextHeight);
@@ -2569,7 +2572,6 @@ void Interface::converseDisplayTextLines() {
 	} else {
 		bulletForegnd = _vm->KnownColor2ColorId(kKnownColorBrightWhite);
 		bulletBackgnd = _vm->KnownColor2ColorId(kKnownColorBlack);
-		bullet[0] = '>';				// different bullet in IHNM
 	}
 
 	if (_vm->getGameId() == GID_ITE)
@@ -2614,14 +2616,14 @@ void Interface::converseDisplayTextLines() {
 			textPoint.y = rect.top;
 
 			if (_vm->getGameId() == GID_ITE)
-				_vm->_font->textDraw(kKnownFontSmall, bullet, textPoint, bulletForegnd, bulletBackgnd, (FontEffectFlags)(kFontShadow | kFontDontmap));
+				_vm->_font->textDraw(kKnownFontSmall, bullet, textPoint, bulletForegnd, bulletBackgnd, _vm->getPlatform() == Common::kPlatformPC98 ?  kFontNormal : (FontEffectFlags)(kFontShadow | kFontDontmap));
 			else
 				_vm->_font->textDraw(kKnownFontVerb, bullet, textPoint, bulletForegnd, bulletBackgnd, (FontEffectFlags)(kFontShadow | kFontDontmap));
 		}
 		textPoint.x = rect.left + 1;
 		textPoint.y = rect.top;
 		if (_vm->getGameId() == GID_ITE)
-			_vm->_font->textDraw(kKnownFontSmall, str, textPoint, foregnd, kITEColorBlack, kFontShadow);
+			_vm->_font->textDraw(kKnownFontSmall, str, textPoint, foregnd, kITEColorBlack, _vm->getPlatform() == Common::kPlatformPC98 ?  kFontNormal : kFontShadow);
 		else
 			_vm->_font->textDraw(kKnownFontVerb, str, textPoint, foregnd, _vm->KnownColor2ColorId(kKnownColorBlack), kFontShadow);
 	}


Commit: c22768e4fb86bc340cb856b313820f40e637ca46
    https://github.com/scummvm/scummvm/commit/c22768e4fb86bc340cb856b313820f40e637ca46
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:44+01:00

Commit Message:
SAGA: (ITE/PC98) - implement sjis font drawing

Changed paths:
    engines/saga/font.cpp
    engines/saga/font.h
    engines/saga/gfx.h


diff --git a/engines/saga/font.cpp b/engines/saga/font.cpp
index 3166ac0391..4bf4f07ee6 100644
--- a/engines/saga/font.cpp
+++ b/engines/saga/font.cpp
@@ -89,87 +89,88 @@ Font::FontId Font::knownFont2FontIdx(KnownFont font) {
 	return fontId;
 }
 
-int Font::getHeight(FontId fontId, const char *text, int width, FontEffectFlags flags) {
+void Font::textDraw(FontId fontId, const char *text, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) {
 	int textWidth;
 	int textLength;
 	int fitWidth;
-	const char *searchPointer;
-	const char *measurePointer;
-	const char *foundPointer;
-	int len;
-	int w;
-	const char *endPointer;
-	int h;
-	int wc;
-	int w_total;
-	int len_total;
-	Common::Point textPoint;
+	Common::Point textPoint(point);
 
-	textLength = strlen(text);
+	textLength = getStringLength(text);
+
+	if (!(flags & kFontCentered)) {
+		// Text is not centered; No formatting required
+		draw(fontId, text, textLength, point, color, effectColor, flags);
+		return;
+	}
+
+	// Text is centered... format output
+	// Enforce minimum and maximum center points for centered text
+	if (textPoint.x < TEXT_CENTERLIMIT) {
+		textPoint.x = TEXT_CENTERLIMIT;
+	}
+
+	if (textPoint.x > _vm->_gfx->getBackBufferWidth() - TEXT_CENTERLIMIT) {
+		textPoint.x = _vm->_gfx->getBackBufferWidth() - TEXT_CENTERLIMIT;
+	}
+
+	if (textPoint.x < (TEXT_MARGIN * 2)) {
+		// Text can't be centered if it's too close to the margin
+		return;
+	}
 
 	textWidth = getStringWidth(fontId, text, textLength, flags);
-	h = getHeight(fontId);
-	fitWidth = width;
 
-	textPoint.x = (fitWidth / 2);
-	textPoint.y = 0;
+	if (textPoint.x < (_vm->_gfx->getBackBufferWidth() / 2)) {
+		// Fit to right side
+		fitWidth = (textPoint.x - TEXT_MARGIN) * 2;
+	} else {
+		// Fit to left side
+		fitWidth = ((_vm->_gfx->getBackBufferWidth() - TEXT_MARGIN) - textPoint.x) * 2;
+	}
 
-	if (fitWidth >= textWidth) {
-		return h;
+	if (fitWidth < textWidth) {
+		warning("text too long to be displayed in one line");
+		textWidth = fitWidth;
 	}
+	// Entire string fits, draw it
+	textPoint.x = textPoint.x - (textWidth / 2);
+	draw(fontId, text, textLength, textPoint, color, effectColor, flags);
+}
 
-	// String won't fit on one line
-	w_total = 0;
-	len_total = 0;
-	wc = 0;
+DefaultFont::DefaultFont(SagaEngine *vm) : Font(vm), _fontMapping(0) {
+	int i;
 
-	measurePointer = text;
-	searchPointer = text;
-	endPointer = text + textLength;
+	// Load font module resource context
 
-	for (;;) {
-		foundPointer = strchr(searchPointer, ' ');
-		if (foundPointer == NULL) {
-			// Ran to the end of the buffer
-			len = endPointer - measurePointer;
-		} else {
-			len = foundPointer - measurePointer;
-		}
+	assert(_vm->getFontsCount() > 0);
 
-		w = getStringWidth(fontId, measurePointer, len, flags);
-		measurePointer = foundPointer;
+	_fonts.resize(_vm->getFontsCount());
+	for (i = 0; i < _vm->getFontsCount(); i++) {
+#ifdef __DS__
+		_fonts[i].outline.font = NULL;
+		_fonts[i].normal.font = NULL;
+#endif
+		loadFont(&_fonts[i],	_vm->getFontDescription(i)->fontResourceId);
+	}
+}
 
-		if ((w_total + w) > fitWidth) {
-			// This word won't fit
-			if (wc == 0) {
-				// The first word in the line didn't fit. Still print it
-				searchPointer = measurePointer + 1;
-			}
-			// Wrap what we've got and restart
-			textPoint.y += h + TEXT_LINESPACING;
-			if (foundPointer == NULL) {
-				// Since word hit NULL but fit, we are done
-				return textPoint.y + h;
-			}
-			w_total = 0;
-			len_total = 0;
-			wc = 0;
-			measurePointer = searchPointer;
-		} else {
-			// Word will fit ok
-			w_total += w;
-			len_total += len;
-			wc++;
-			if (foundPointer == NULL) {
-				// Since word hit NULL but fit, we are done
-				return textPoint.y + h;
-			}
-			searchPointer = measurePointer + 1;
+DefaultFont::~DefaultFont() {
+	debug(8, "DefaultFont::~DefaultFont(): Freeing fonts.");
+
+#ifdef __DS__
+	for (int i = 0; i < _vm->getFontsCount(); i++) {
+		if (_fonts[i].outline.font) {
+			free(_fonts[i].outline.font);
+		}
+
+		if (_fonts[i].normal.font) {
+			free(_fonts[i].normal.font);
 		}
 	}
+#endif
 }
 
-void Font::textDrawRect(FontId fontId, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags) {
+void DefaultFont::textDrawRect(FontId fontId, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags) {
 	int textWidth;
 	int textLength;
 	int fitWidth;
@@ -187,7 +188,7 @@ void Font::textDrawRect(FontId fontId, const char *text, const Common::Rect &rec
 	Common::Point textPoint;
 	Common::Point textPoint2;
 
-	textLength = strlen(text);
+	textLength = getStringLength(text);
 
 	textWidth = getStringWidth(fontId, text, textLength, flags);
 	fitWidth = rect.width();
@@ -279,87 +280,6 @@ void Font::textDrawRect(FontId fontId, const char *text, const Common::Rect &rec
 	}
 }
 
-void Font::textDraw(FontId fontId, const char *text, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) {
-	int textWidth;
-	int textLength;
-	int fitWidth;
-	Common::Point textPoint(point);
-
-	textLength = strlen(text);
-
-	if (!(flags & kFontCentered)) {
-		// Text is not centered; No formatting required
-		draw(fontId, text, textLength, point, color, effectColor, flags);
-		return;
-	}
-
-	// Text is centered... format output
-	// Enforce minimum and maximum center points for centered text
-	if (textPoint.x < TEXT_CENTERLIMIT) {
-		textPoint.x = TEXT_CENTERLIMIT;
-	}
-
-	if (textPoint.x > _vm->_gfx->getBackBufferWidth() - TEXT_CENTERLIMIT) {
-		textPoint.x = _vm->_gfx->getBackBufferWidth() - TEXT_CENTERLIMIT;
-	}
-
-	if (textPoint.x < (TEXT_MARGIN * 2)) {
-		// Text can't be centered if it's too close to the margin
-		return;
-	}
-
-	textWidth = getStringWidth(fontId, text, textLength, flags);
-
-	if (textPoint.x < (_vm->_gfx->getBackBufferWidth() / 2)) {
-		// Fit to right side
-		fitWidth = (textPoint.x - TEXT_MARGIN) * 2;
-	} else {
-		// Fit to left side
-		fitWidth = ((_vm->_gfx->getBackBufferWidth() - TEXT_MARGIN) - textPoint.x) * 2;
-	}
-
-	if (fitWidth < textWidth) {
-		warning("text too long to be displayed in one line");
-		textWidth = fitWidth;
-	}
-	// Entire string fits, draw it
-	textPoint.x = textPoint.x - (textWidth / 2);
-	draw(fontId, text, textLength, textPoint, color, effectColor, flags);
-}
-
-DefaultFont::DefaultFont(SagaEngine *vm) : Font(vm), _fontMapping(0) {
-	int i;
-
-	// Load font module resource context
-
-	assert(_vm->getFontsCount() > 0);
-
-	_fonts.resize(_vm->getFontsCount());
-	for (i = 0; i < _vm->getFontsCount(); i++) {
-#ifdef __DS__
-		_fonts[i].outline.font = NULL;
-		_fonts[i].normal.font = NULL;
-#endif
-		loadFont(&_fonts[i],	_vm->getFontDescription(i)->fontResourceId);
-	}
-}
-
-DefaultFont::~DefaultFont() {
-	debug(8, "DefaultFont::~DefaultFont(): Freeing fonts.");
-
-#ifdef __DS__
-	for (int i = 0; i < _vm->getFontsCount(); i++) {
-		if (_fonts[i].outline.font) {
-			free(_fonts[i].outline.font);
-		}
-
-		if (_fonts[i].normal.font) {
-			free(_fonts[i].normal.font);
-		}
-	}
-#endif
-}
-
 int DefaultFont::translateChar(int charId) {
 	if (charId <= 127 || (_vm->getLanguage() == Common::RU_RUS && charId <= 254))
 		return charId;					// normal character
@@ -394,6 +314,85 @@ int DefaultFont::getStringWidth(FontId fontId, const char *text, size_t count, F
 	return width;
 }
 
+int DefaultFont::getHeight(FontId fontId, const char *text, int width, FontEffectFlags flags) {
+	int textWidth;
+	int textLength;
+	int fitWidth;
+	const char *searchPointer;
+	const char *measurePointer;
+	const char *foundPointer;
+	int len;
+	int w;
+	const char *endPointer;
+	int h;
+	int wc;
+	int w_total;
+	int len_total;
+	Common::Point textPoint;
+
+	textLength = getStringLength(text);
+	textWidth = getStringWidth(fontId, text, textLength, flags);
+	h = getHeight(fontId);
+	fitWidth = width;
+
+	textPoint.x = (fitWidth / 2);
+	textPoint.y = 0;
+
+	if (fitWidth >= textWidth) {
+		return h;
+	}
+
+	// String won't fit on one line
+	w_total = 0;
+	len_total = 0;
+	wc = 0;
+
+	measurePointer = text;
+	searchPointer = text;
+	endPointer = text + textLength;
+
+	for (;;) {
+		foundPointer = strchr(searchPointer, ' ');
+		if (foundPointer == NULL) {
+			// Ran to the end of the buffer
+			len = endPointer - measurePointer;
+		} else {
+			len = foundPointer - measurePointer;
+		}
+
+		w = getStringWidth(fontId, measurePointer, len, flags);
+		measurePointer = foundPointer;
+
+		if ((w_total + w) > fitWidth) {
+			// This word won't fit
+			if (wc == 0) {
+				// The first word in the line didn't fit. Still print it
+				searchPointer = measurePointer + 1;
+			}
+			// Wrap what we've got and restart
+			textPoint.y += h + TEXT_LINESPACING;
+			if (foundPointer == NULL) {
+				// Since word hit NULL but fit, we are done
+				return textPoint.y + h;
+			}
+			w_total = 0;
+			len_total = 0;
+			wc = 0;
+			measurePointer = searchPointer;
+		} else {
+			// Word will fit ok
+			w_total += w;
+			len_total += len;
+			wc++;
+			if (foundPointer == NULL) {
+				// Since word hit NULL but fit, we are done
+				return textPoint.y + h;
+			}
+			searchPointer = measurePointer + 1;
+		}
+	}
+}
+
 void DefaultFont::draw(FontId fontId, const char *text, size_t count, const Common::Point &point,
 			   int color, int effectColor, FontEffectFlags flags) {
 
@@ -706,19 +705,143 @@ SJISFont::~SJISFont() {
 	delete _font;
 }
 
-int SJISFont::getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags) {
+void SJISFont::textDrawRect(FontId fontId, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags) {
+	Common::Point textPoint(rect.left, rect.top);
+	int curW = 0;
+	int numChar = 0;
+	const char *pos = text;
+	const char *last = 0;
+
+	for (uint16 c = fetchChar(pos); c; c = fetchChar(pos)) {
+		curW += (_font->getCharWidth(c) >> 1);
+		if (curW > rect.width() || c == (uint16)'\r' || c == (uint16)'\n') {
+			draw(fontId, text, numChar, textPoint, color, effectColor, flags);
+			numChar = 0;
+			textPoint.x = rect.left;
+			textPoint.y += (getHeight(fontId));
+			// Abort if there is no more space inside the rect
+			if (textPoint.y + getHeight(fontId) > rect.bottom)
+				return;
+			// Skip linebreak characters
+			if (c == (uint16)'\r' || c == (uint16)'\n')
+				last++;
+			pos = text = last;
+			last = 0;
+			curW = 0;
+		} else {
+			numChar++;
+			last = pos;
+		}
+	}
+
+	// If the whole string fits into one line it gets aligned to the center
+	if (textPoint.y == rect.top)
+		textPoint.x = textPoint.x + rect.width() / 2 - (getStringWidth(fontId, text, 0, flags) / 2);
+
+	draw(fontId, text, numChar, textPoint, color, effectColor, flags);	
+}
+
+int SJISFont::getStringLength(const char *text) {
 	int res = 0;
-	for (uint16 c = fetchChar(text); c; c = fetchChar(text))
-		res += _font->getCharWidth(c);
+	while (fetchChar(text))
+		res++;
+
 	return res;
 }
 
+int SJISFont::getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags) {
+	// The spacing is always the same regardless of the fontId and font style
+	_font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
+	int curW = 0;
+	int maxW = 0;
+
+	for (uint16 c = fetchChar(text); c; c = fetchChar(text)) {
+		if (c == (uint16)'\r' || c == (uint16)'\n') {
+			maxW = MAX<int>(curW, maxW);
+#if 1
+			curW = 0;
+			continue;
+#else
+			break;
+#endif
+		}
+		curW += _font->getCharWidth(c);
+		if (!--count)
+			break;
+	}
+
+	return MAX<int>(curW, maxW) >> 1;
+}
+
+int SJISFont::getHeight(FontId fontId, const char *text, int width, FontEffectFlags flags) {
+	Graphics::FontSJIS::DrawingMode mode = Graphics::FontSJIS::kDefaultMode;
+	if (flags & kFontOutline)
+		mode = Graphics::FontSJIS::kOutlineMode;
+	else if (flags & kFontShadow)
+		mode = Graphics::FontSJIS::kShadowRightMode;
+
+	_font->setDrawingMode(mode);
+	int res = _font->getFontHeight();
+	int tmpWidth = 0;
+	for (uint16 c = fetchChar(text); c; c = fetchChar(text)) {
+		// The spacing is always the same (regardless of the fontId and font style) for the char spacing, but not for the line spacing.
+		_font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
+		tmpWidth += (_font->getCharWidth(c) >> 1);
+		if (tmpWidth > width || c == (uint16)'\r' || c == (uint16)'\n') {
+			tmpWidth = tmpWidth > width ? _font->getCharWidth(c) >> 1 : 0;
+			_font->setDrawingMode(mode);
+			res += _font->getFontHeight();
+		}		
+	}
+
+	return (res + 1) >> 1;
+}
+
 int SJISFont::getHeight(FontId fontId) {
-	return _font->getFontHeight();
+	// The spacing here is always the same regardless of the style
+	_font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
+	return (_font->getFontHeight() >> 1) + 1;
 }
 
 void SJISFont::draw(FontId fontId, const char *text, size_t count, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) {
+	int16 x = point.x << 1;
+	int16 y = point.y << 1;
+
+	Graphics::FontSJIS::DrawingMode mode = Graphics::FontSJIS::kDefaultMode;
+	if (effectColor != 0x80) {
+		if (flags & kFontOutline)
+			mode = Graphics::FontSJIS::kOutlineMode;
+		else if (flags & kFontShadow)
+			mode = Graphics::FontSJIS::kShadowRightMode;
+	}
+
+	// DEBUG: The Graphics::FontSJIS code currently does not allow glyphs to be outlined and shaded at the same time. I'll implement it if I have to, but currently I don't think that this is the case...
+	assert((flags & 3) != 3);
+	_font->setDrawingMode(mode);
+	Common::Rect dirtyRect((flags & kFontShadow) ? MAX<int16>(point.x - 1, 0) : point.x, point.y, point.x + 1, point.y + (_font->getFontHeight() >> 1));
+
+	while (*text) {
+		uint16 ch = fetchChar(text);
+		_font->setDrawingMode(mode);
+		if (ch == (uint16)'\r' || ch == (uint16)'\n') {
+			dirtyRect.right = MAX<int16>(x >> 1, dirtyRect.right);
+			y += _font->getFontHeight();
+			x = point.x << 1;
+			continue;
+		}
+		_font->drawChar(_vm->_gfx->getSJISBackBuffer(), ch, x, y, color, effectColor);
+		// Reset drawing mode for the shadow mode extra drawing and for the character spacing (not line spacing)
+		_font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
+		if (flags & kFontShadow)
+			_font->drawChar(_vm->_gfx->getSJISBackBuffer(), ch, MAX<int16>(x - 1, 0), y, color, 0);
+		x += _font->getCharWidth(ch);
+		if (!--count)
+			break;
+	}
 
+	dirtyRect.right = MAX<int16>(x >> 1, dirtyRect.right);
+	dirtyRect.bottom = (y + _font->getFontHeight()) >> 1;
+	_vm->_render->addDirtyRect(dirtyRect);
 }
 
 uint16 SJISFont::fetchChar(const char *&s) const {
diff --git a/engines/saga/font.h b/engines/saga/font.h
index 8c9e323263..54cf125a36 100644
--- a/engines/saga/font.h
+++ b/engines/saga/font.h
@@ -184,12 +184,13 @@ protected:
 
 private:
 	FontId knownFont2FontIdx(KnownFont font);
-	int getHeight(FontId fontId, const char *text, int width, FontEffectFlags flags);
-	void textDrawRect(FontId fontId, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags);
 	void textDraw(FontId fontId, const char *string, const Common::Point &point, int color, int effectColor, FontEffectFlags flags);
 
+	virtual void textDrawRect(FontId fontId, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags) = 0;
 	virtual int translateChar(int charId) = 0;
+	virtual int getStringLength(const char *text) = 0;
 	virtual int getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags) = 0;
+	virtual int getHeight(FontId fontId, const char *text, int width, FontEffectFlags flags) = 0;
 	virtual int getHeight(FontId fontId) = 0;
 	virtual bool valid(FontId) = 0;
 	virtual void draw(FontId fontId, const char *text, size_t count, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) = 0;
@@ -205,8 +206,14 @@ class DefaultFont : public Font {
 	}
 
  private:
+	 void textDrawRect(FontId fontId, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags) override;
 	 int translateChar(int charId) override;
+	 int getStringLength(const char *text) override {
+		 return strlen(text);
+	 }
+
 	 int getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags) override;
+	 int getHeight(FontId fontId, const char *text, int width, FontEffectFlags flags) override;
 	 int getHeight(FontId fontId) override {
 		 return getFont(fontId)->normal.header.charHeight;
 	 }
@@ -253,8 +260,11 @@ public:
 	~SJISFont() override;
 
 private:
+	void textDrawRect(FontId fontId, const char *text, const Common::Rect &rect, int color, int effectColor, FontEffectFlags flags) override;
 	int translateChar(int charId) override { return charId; }
+	int getStringLength(const char *text) override;
 	int getStringWidth(FontId fontId, const char *text, size_t count, FontEffectFlags flags) override;
+	int getHeight(FontId fontId, const char *text, int width, FontEffectFlags flags) override;
 	int getHeight(FontId fontId) override;
 	bool valid(FontId fontId) override { return fontId != kBigFont; }
 
diff --git a/engines/saga/gfx.h b/engines/saga/gfx.h
index 5dd3a9672a..d3c6f89e71 100644
--- a/engines/saga/gfx.h
+++ b/engines/saga/gfx.h
@@ -223,11 +223,18 @@ public:
 		return (byte *)_backBuffer.getPixels();
 	}
 
-	// Same as getBackBufferPixels(), but for the hires sjis buffers
+	// Same as getBackBufferPixels(), but for the hires sjis buffer
 	byte *getSJISBackBufferPixels() {
 		return (byte *)_sjisBackBuffer.getPixels();
 	}
 
+	// Expose the sjis buffer directly. One of the two implementations of Graphics::FontSJIS::drawChar()
+	// allows a Common::Surface as a parameter which makes the rendering a bit nicer compared to using
+	// the raw pixel buffer.
+	Surface &getSJISBackBuffer() {
+		return _sjisBackBuffer;
+	}
+
 	uint16 getBackBufferWidth() {
 		return _backBuffer.w;
 	}


Commit: 5c8f6507ef85776f62aa3180d925c9115baf5af9
    https://github.com/scummvm/scummvm/commit/5c8f6507ef85776f62aa3180d925c9115baf5af9
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:44+01:00

Commit Message:
SAGA: (ITE/PC98) - adapt actor speech positioning

Changed paths:
    engines/saga/actor.cpp


diff --git a/engines/saga/actor.cpp b/engines/saga/actor.cpp
index 3c6a10e853..0dcf77af35 100644
--- a/engines/saga/actor.cpp
+++ b/engines/saga/actor.cpp
@@ -1136,7 +1136,11 @@ void Actor::drawSpeech() {
 				_activeSpeech.speechColor[i], _activeSpeech.outlineColor[i], _activeSpeech.getFontFlags(i));
 		}
 	} else {
-		_vm->_font->textDrawRect(kKnownFontScript, &outputString.front(), _activeSpeech.drawRect, _activeSpeech.speechColor[0],
+		Common::Rect drawRect(_activeSpeech.drawRect);
+		// The PC-98 version does a vertical center alignment which we have to imitate for pixel exact text output.
+		if (_vm->getPlatform() == Common::kPlatformPC98)
+			drawRect.top -= (_vm->_font->getHeight(kKnownFontScript, &outputString.front(), drawRect.width(), _activeSpeech.getFontFlags(0)) >> 1);
+		_vm->_font->textDrawRect(kKnownFontScript, &outputString.front(), drawRect, _activeSpeech.speechColor[0],
 			_activeSpeech.outlineColor[0], _activeSpeech.getFontFlags(0));
 	}
 }
@@ -1165,7 +1169,7 @@ void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount,
 	dist = MIN(actor->_screenPosition.x - 10, _vm->getDisplayInfo().width - 10 - actor->_screenPosition.x);
 
 	if (_vm->getGameId() == GID_ITE)
-		dist = CLIP<int16>(dist, 60, 150);
+		dist = (_vm->getPlatform() == Common::kPlatformPC98) ? CLIP<int16>(dist, 110, 200) : CLIP<int16>(dist, 60, 150);
 	else
 		dist = CLIP<int16>(dist, 120, 300);
 
@@ -1180,7 +1184,6 @@ void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount,
 		_activeSpeech.speechBox.left -= _activeSpeech.speechBox.right - _vm->getDisplayInfo().width - 10;
 		_activeSpeech.speechBox.right = _vm->getDisplayInfo().width - 10;
 	}
-
 }
 
 void Actor::nonActorSpeech(const Common::Rect &box, const char **strings, int stringsCount, int sampleResourceId, int speechFlags) {


Commit: 4a6b2b18978758a18c956f8a213720f28076c28c
    https://github.com/scummvm/scummvm/commit/4a6b2b18978758a18c956f8a213720f28076c28c
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:45+01:00

Commit Message:
SAGA: minor fix to horizontal actor text positioning

(seems to be like a typo; fixed from disasm and screen comparison)

Changed paths:
    engines/saga/actor.cpp


diff --git a/engines/saga/actor.cpp b/engines/saga/actor.cpp
index 0dcf77af35..602ee6e194 100644
--- a/engines/saga/actor.cpp
+++ b/engines/saga/actor.cpp
@@ -1177,11 +1177,11 @@ void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount,
 	_activeSpeech.speechBox.right = actor->_screenPosition.x + dist;
 
 	if (_activeSpeech.speechBox.left < 10) {
-		_activeSpeech.speechBox.right += 10 - _activeSpeech.speechBox.left;
+		_activeSpeech.speechBox.right += (10 - _activeSpeech.speechBox.left);
 		_activeSpeech.speechBox.left = 10;
 	}
 	if (_activeSpeech.speechBox.right > _vm->getDisplayInfo().width - 10) {
-		_activeSpeech.speechBox.left -= _activeSpeech.speechBox.right - _vm->getDisplayInfo().width - 10;
+		_activeSpeech.speechBox.left -= _activeSpeech.speechBox.right - (_vm->getDisplayInfo().width - 10);
 		_activeSpeech.speechBox.right = _vm->getDisplayInfo().width - 10;
 	}
 }


Commit: eaeadbb62223ff2bc562295604bd6f83dedae224
    https://github.com/scummvm/scummvm/commit/eaeadbb62223ff2bc562295604bd6f83dedae224
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:46+01:00

Commit Message:
SAGA: (ITE/PC98) - fix override warning

Changed paths:
    engines/saga/font.h


diff --git a/engines/saga/font.h b/engines/saga/font.h
index 54cf125a36..3c5c2bff82 100644
--- a/engines/saga/font.h
+++ b/engines/saga/font.h
@@ -201,7 +201,7 @@ class DefaultFont : public Font {
 	DefaultFont(SagaEngine *vm);
 	~DefaultFont() override;
 
-	void setFontMapping(int mapping) {
+	void setFontMapping(int mapping) override {
 		_fontMapping = mapping;
 	}
 


Commit: 3aded1d5ba487ca8e5c22076fa536423369f142d
    https://github.com/scummvm/scummvm/commit/3aded1d5ba487ca8e5c22076fa536423369f142d
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:46+01:00

Commit Message:
SAGA: (ITE/PC98) - fix intro credits text positioning

This commit aims at getting the text almost pixel-exact like the original (from disasm and from comparison with an emulator).

It seems that the original messed up the upscaling (from 320x200 to 640x400). The whole method how they do it is weird. The text is rendered on a 320x200 pixel surface and this surface gets blitted in the center of a 640x400 pixel screen. Sometimes they
 seem to assume to draw onto 320x200, sometimes onto 640x400 (e. g. the line height for the paragraph headlines is assumed to be 11, although the font height is 18; and it will use height 18 for all the other text lines). Also, the orginal will use different printing methods for the headlines and for the text blocks below, which also doesn't help to get this right...

Now, some credits text lines are still off by 1 pixel vertically due to my method of upscaling (at the very last moment in drawDirtyRects()). I find this a much cleaner approach than the original method which is just hacking up the whole graphics code to use 640x400 coords (except for the places they forgot, see above). But this also means I can't hit the odd lines for text positioning, thus the one pixel off sometimes...

Changed paths:
    engines/saga/introproc_ite.cpp


diff --git a/engines/saga/introproc_ite.cpp b/engines/saga/introproc_ite.cpp
index d40bbf3dfd..62c58ec474 100644
--- a/engines/saga/introproc_ite.cpp
+++ b/engines/saga/introproc_ite.cpp
@@ -210,7 +210,7 @@ EventColumns *Scene::queueCredits(int delta_time, int duration, int n_credits, c
 	else
 		game = kITECreditsPC;
 
-	int line_spacing = 0;
+	int lineHeight = 0;
 	int paragraph_spacing;
 	KnownFont font = kKnownFontSmall;
 	int i;
@@ -230,27 +230,26 @@ EventColumns *Scene::queueCredits(int delta_time, int duration, int n_credits, c
 		switch (credits[i].type) {
 		case kITECreditsHeader:
 			font = kKnownFontSmall;
-			line_spacing = 4;
+			// First glance at disasm might suggest that the 12 here is a typo (instead of 11). But it isn't.
+			// I take into account the extra pixel per paragraph here which the original code will consider
+			// elsewhere. In tbe second (queueing) loop below I have to be a bit more elaborate to get this
+			// right (using extra variable yOffs2), but here it works fine like this...
+			lineHeight = (_vm->getPlatform() == Common::kPlatformPC98) ? 12 : _vm->_font->getHeight(font) + 4;
 			n_paragraphs++;
 			break;
 		case kITECreditsText:
 			font = kKnownFontMedium;
-			line_spacing = 2;
+			lineHeight = (_vm->getPlatform() == Common::kPlatformPC98) ? (_vm->_font->getHeight(font) << 1) : _vm->_font->getHeight(font) + 2;
 			break;
 		default:
 			error("Unknown credit type");
 		}
 
-		if (_vm->getPlatform() == Common::kPlatformPC98)
-			line_spacing -= 2;
-
-		credits_height += (_vm->_font->getHeight(font) + line_spacing);
+		credits_height += lineHeight;
 	}
 
 	paragraph_spacing = (200 - credits_height) / (n_paragraphs + 3);
-	credits_height += (n_paragraphs * paragraph_spacing);
-
-	int y = paragraph_spacing;
+	int y = (_vm->getPlatform() == Common::kPlatformPC98) ? paragraph_spacing + 80 : paragraph_spacing;
 
 	TextListEntry textEntry;
 	TextListEntry *entry;
@@ -261,6 +260,8 @@ EventColumns *Scene::queueCredits(int delta_time, int duration, int n_credits, c
 	textEntry.effectKnownColor = (_vm->getPlatform() == Common::kPlatformPC98) ? kKnownColorVerbTextShadow : kKnownColorTransparent;
 	textEntry.flags = (FontEffectFlags)(((_vm->getPlatform() == Common::kPlatformPC98) ? kFontShadow : kFontOutline) | kFontCentered);
 	textEntry.point.x = 160;
+	int yOffs = 0;
+	int yOffs2 = 0;
 
 	for (i = 0; i < n_credits; i++) {
 		if (credits[i].lang != lang && credits[i].lang != Common::UNK_LANG) {
@@ -274,23 +275,27 @@ EventColumns *Scene::queueCredits(int delta_time, int duration, int n_credits, c
 		switch (credits[i].type) {
 		case kITECreditsHeader:
 			font = kKnownFontSmall;
-			line_spacing = 4;
-			y += paragraph_spacing;
+			lineHeight = (_vm->getPlatform() == Common::kPlatformPC98) ? 11 : _vm->_font->getHeight(font) + 4;
+			yOffs = (_vm->getPlatform() == Common::kPlatformPC98) ? -3 : 0;
+			y = y + paragraph_spacing + yOffs2;
 			break;
 		case kITECreditsText:
 			font = kKnownFontMedium;
-			line_spacing = 2;
+			lineHeight = (_vm->getPlatform() == Common::kPlatformPC98) ? (_vm->_font->getHeight(font) << 1) : _vm->_font->getHeight(font) + 2;
+			yOffs = 0;
 			break;
 		default:
 			break;
 		}
 
-		if (_vm->getPlatform() == Common::kPlatformPC98)
-			line_spacing -= 2;
-
 		textEntry.text = credits[i].string;
 		textEntry.font = font;
-		textEntry.point.y = y;
+		textEntry.point.y = y + yOffs;
+
+		if (_vm->getPlatform() == Common::kPlatformPC98) {
+			textEntry.point.y >>= 1;
+			yOffs2 = 1;
+		}
 
 		entry = _vm->_scene->_textList.addEntry(textEntry);
 
@@ -310,7 +315,7 @@ EventColumns *Scene::queueCredits(int delta_time, int duration, int n_credits, c
 		event.time = duration;
 		_vm->_events->chain(eventColumns, event);
 
-		y += (_vm->_font->getHeight(font) + line_spacing);
+		y += lineHeight;
 	}
 
 	return eventColumns;


Commit: c9f41699aa0489c0472292a630a50078f47555a9
    https://github.com/scummvm/scummvm/commit/c9f41699aa0489c0472292a630a50078f47555a9
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:47+01:00

Commit Message:
SAGA: (ITE/PC98) - fine tune intro line breaks

I whish I knew whether I am implementing an original bug here or if this is intended behavior. But it does achieve text output faithful to the original. And it even does actually look better...

Changed paths:
    engines/saga/font.cpp
    engines/saga/font.h


diff --git a/engines/saga/font.cpp b/engines/saga/font.cpp
index 4bf4f07ee6..4f4c193a5d 100644
--- a/engines/saga/font.cpp
+++ b/engines/saga/font.cpp
@@ -711,10 +711,11 @@ void SJISFont::textDrawRect(FontId fontId, const char *text, const Common::Rect
 	int numChar = 0;
 	const char *pos = text;
 	const char *last = 0;
+	int checkWidth = (rect.width() - 16) & ~7;
 
 	for (uint16 c = fetchChar(pos); c; c = fetchChar(pos)) {
 		curW += (_font->getCharWidth(c) >> 1);
-		if (curW > rect.width() || c == (uint16)'\r' || c == (uint16)'\n') {
+		if ((curW > checkWidth && !preventLineBreakForCharacter(c)) || c == (uint16)'\r' || c == (uint16)'\n') {
 			draw(fontId, text, numChar, textPoint, color, effectColor, flags);
 			numChar = 0;
 			textPoint.x = rect.left;
@@ -736,9 +737,9 @@ void SJISFont::textDrawRect(FontId fontId, const char *text, const Common::Rect
 
 	// If the whole string fits into one line it gets aligned to the center
 	if (textPoint.y == rect.top)
-		textPoint.x = textPoint.x + rect.width() / 2 - (getStringWidth(fontId, text, 0, flags) / 2);
+		textPoint.x = textPoint.x + (rect.width() - getStringWidth(fontId, text, 0, flags)) / 2;
 
-	draw(fontId, text, numChar, textPoint, color, effectColor, flags);	
+	draw(fontId, text, numChar, textPoint, color, effectColor, flags);
 }
 
 int SJISFont::getStringLength(const char *text) {
@@ -758,12 +759,8 @@ int SJISFont::getStringWidth(FontId fontId, const char *text, size_t count, Font
 	for (uint16 c = fetchChar(text); c; c = fetchChar(text)) {
 		if (c == (uint16)'\r' || c == (uint16)'\n') {
 			maxW = MAX<int>(curW, maxW);
-#if 1
 			curW = 0;
 			continue;
-#else
-			break;
-#endif
 		}
 		curW += _font->getCharWidth(c);
 		if (!--count)
@@ -782,16 +779,18 @@ int SJISFont::getHeight(FontId fontId, const char *text, int width, FontEffectFl
 
 	_font->setDrawingMode(mode);
 	int res = _font->getFontHeight();
+	int checkWidth = (width - 16) & ~7;
 	int tmpWidth = 0;
+
 	for (uint16 c = fetchChar(text); c; c = fetchChar(text)) {
 		// The spacing is always the same (regardless of the fontId and font style) for the char spacing, but not for the line spacing.
 		_font->setDrawingMode(Graphics::FontSJIS::kDefaultMode);
 		tmpWidth += (_font->getCharWidth(c) >> 1);
-		if (tmpWidth > width || c == (uint16)'\r' || c == (uint16)'\n') {
+		if ((tmpWidth > checkWidth && !preventLineBreakForCharacter(c)) || c == (uint16)'\r' || c == (uint16)'\n') {
 			tmpWidth = tmpWidth > width ? _font->getCharWidth(c) >> 1 : 0;
 			_font->setDrawingMode(mode);
 			res += _font->getFontHeight();
-		}		
+		}
 	}
 
 	return (res + 1) >> 1;
@@ -854,4 +853,9 @@ uint16 SJISFont::fetchChar(const char *&s) const {
 	return ch;
 }
 
+bool SJISFont::preventLineBreakForCharacter(uint16 ch) const {
+	uint8 c = (ch >> 8) & 0xFF;
+	return c && ((c >= 0x81 && c <= 0x9F) || c >= 0xE0);
+}
+
 } // End of namespace Saga
diff --git a/engines/saga/font.h b/engines/saga/font.h
index 3c5c2bff82..04a07be0cf 100644
--- a/engines/saga/font.h
+++ b/engines/saga/font.h
@@ -237,7 +237,7 @@ class DefaultFont : public Font {
 	 void outFont(const FontStyle &drawFont, const char *text, size_t count, const Common::Point &point, int color, FontEffectFlags flags);
 	 void loadFont(FontData *font, uint32 fontResourceId);
 	 void createOutline(FontData *font);
- 
+
 	 int getByteLen(int numBits) const {
 		 int byteLength = numBits / 8;
 
@@ -271,6 +271,7 @@ private:
 	void draw(FontId fontId, const char *text, size_t count, const Common::Point &point, int color, int effectColor, FontEffectFlags flags) override;
 
 	uint16 fetchChar(const char *&s) const;
+	bool preventLineBreakForCharacter(uint16 ch) const;
 
 	Graphics::FontSJIS *_font;
 };


Commit: 181368f1c509ae9d54e4bb9f6d153d8706a21d16
    https://github.com/scummvm/scummvm/commit/181368f1c509ae9d54e4bb9f6d153d8706a21d16
Author: athrxx (athrxx at scummvm.org)
Date: 2020-11-22T19:02:47+01:00

Commit Message:
SAGA: (ITE/PC98) - fix intro text frame width

Changed paths:
    engines/saga/introproc_ite.cpp


diff --git a/engines/saga/introproc_ite.cpp b/engines/saga/introproc_ite.cpp
index 62c58ec474..b1b80b9845 100644
--- a/engines/saga/introproc_ite.cpp
+++ b/engines/saga/introproc_ite.cpp
@@ -124,7 +124,7 @@ EventColumns *Scene::queueIntroDialogue(EventColumns *eventColumns, int n_dialog
 	textEntry.effectKnownColor = (_vm->getPlatform() == Common::kPlatformPC98) ? kKnownColorSubtitleEffectColorPC98 : kKnownColorTransparent;
 	textEntry.useRect = true;
 	textEntry.rect.left = (_vm->getPlatform() == Common::kPlatformPC98) ? 10 : 0;
-	textEntry.rect.right = _vm->getDisplayInfo().width - (_vm->getPlatform() == Common::kPlatformPC98 ? 20 : 0);
+	textEntry.rect.right = _vm->getDisplayInfo().width - (_vm->getPlatform() == Common::kPlatformPC98 ? 10 : 0);
 	if (_vm->getLanguage() == Common::DE_DEU) {
 		textEntry.rect.top = INTRO_DE_CAPTION_Y;
 	} else if (_vm->getLanguage() == Common::IT_ITA) {




More information about the Scummvm-git-logs mailing list