[Scummvm-git-logs] scummvm master -> 4f31022bb0c282722c4182b94d61b36610357be5
wonst719
wonst719 at gmail.com
Sun Nov 15 06:45:11 UTC 2020
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
4f31022bb0 SCUMM: Implement language bundle for Korean fan translation (#2620)
Commit: 4f31022bb0c282722c4182b94d61b36610357be5
https://github.com/scummvm/scummvm/commit/4f31022bb0c282722c4182b94d61b36610357be5
Author: wonst719 (wonst719 at gmail.com)
Date: 2020-11-15T15:45:07+09:00
Commit Message:
SCUMM: Implement language bundle for Korean fan translation (#2620)
Changed paths:
engines/scumm/detection_internal.h
engines/scumm/resource.cpp
engines/scumm/scumm.cpp
engines/scumm/scumm.h
engines/scumm/string.cpp
diff --git a/engines/scumm/detection_internal.h b/engines/scumm/detection_internal.h
index 8e6c1116c3..356487bff8 100644
--- a/engines/scumm/detection_internal.h
+++ b/engines/scumm/detection_internal.h
@@ -201,8 +201,8 @@ static bool detectSpeech(const Common::FSList &fslist, const GameSettings *gs) {
return false;
}
-// The following function tries to detect the language for COMI and DIG.
-static Common::Language detectLanguage(const Common::FSList &fslist, byte id) {
+// The following function tries to detect the language.
+static Common::Language detectLanguage(const Common::FSList &fslist, byte id, Common::Language originalLanguage = Common::UNK_LANG) {
// First try to detect Chinese translation.
Common::FSNode fontFile;
@@ -211,10 +211,18 @@ static Common::Language detectLanguage(const Common::FSList &fslist, byte id) {
return Common::ZH_CNA;
}
- // Now try to detect COMI and Dig by language files.
- if (id != GID_CMI && id != GID_DIG)
- return Common::UNK_LANG;
+ if (id != GID_CMI && id != GID_DIG) {
+ // Detect Korean fan translated games
+ Common::FSNode langFile;
+ if (searchFSNode(fslist, "korean.trs", langFile)) {
+ debug(0, "Korean fan translation detected");
+ return Common::KO_KOR;
+ }
+ return originalLanguage;
+ }
+
+ // Now try to detect COMI and Dig by language files.
// Check for LANGUAGE.BND (Dig) resp. LANGUAGE.TAB (CMI).
// These are usually inside the "RESOURCE" subdirectory.
// If found, we match based on the file size (should we
@@ -302,7 +310,7 @@ static Common::Language detectLanguage(const Common::FSList &fslist, byte id) {
}
}
- return Common::UNK_LANG;
+ return originalLanguage;
}
@@ -337,8 +345,8 @@ static void computeGameSettingsFromMD5(const Common::FSList &fslist, const GameF
}
// HACK: Try to detect languages for translated games.
- if (dr.language == UNK_LANG) {
- dr.language = detectLanguage(fslist, dr.game.id);
+ if (dr.language == UNK_LANG || dr.language == Common::EN_ANY) {
+ dr.language = detectLanguage(fslist, dr.game.id, dr.language);
}
// HACK: Detect between 68k and PPC versions.
diff --git a/engines/scumm/resource.cpp b/engines/scumm/resource.cpp
index eebd36c78c..45de5be53f 100644
--- a/engines/scumm/resource.cpp
+++ b/engines/scumm/resource.cpp
@@ -1103,20 +1103,42 @@ void ScummEngine::loadPtrToResource(ResType type, ResId idx, const byte *source)
byte *alloced;
int len;
+ bool sourceWasNull = !source;
+ int originalLen;
+
_res->nukeResource(type, idx);
len = resStrLen(source) + 1;
if (len <= 0)
return;
+ originalLen = len;
+
+ // Translate resource text
+ byte translateBuffer[512];
+ if (isScummvmKorTarget()) {
+ if (!source) {
+ refreshScriptPointer();
+ source = _scriptPointer;
+ }
+ translateText(source, translateBuffer);
+
+ source = translateBuffer;
+ len = resStrLen(source) + 1;
+ }
+
alloced = _res->createResource(type, idx, len);
if (!source) {
// Need to refresh the script pointer, since createResource may
// have caused the script resource to expire.
refreshScriptPointer();
- memcpy(alloced, _scriptPointer, len);
- _scriptPointer += len;
+ memcpy(alloced, _scriptPointer, originalLen);
+ _scriptPointer += originalLen;
+ } else if (sourceWasNull) {
+ refreshScriptPointer();
+ memcpy(alloced, source, len);
+ _scriptPointer += originalLen;
} else {
memcpy(alloced, source, len);
}
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index d28b7fe6c8..ad1812b366 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -323,6 +323,11 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_msgCount = 0;
_costumeLoader = NULL;
_costumeRenderer = NULL;
+ _existLanguageFile = false;
+ _languageBuffer = 0;
+ _numTranslatedLines = 0;
+ _translatedLines = 0;
+ _languageLineIndex = 0;
_2byteFontPtr = 0;
_krStrPost = 0;
_V1TalkingActor = 0;
@@ -617,8 +622,12 @@ ScummEngine::~ScummEngine() {
delete[] _sortedActors;
+ delete[] _languageBuffer;
+ delete[] _translatedLines;
+ delete[] _languageLineIndex;
+
if (_2byteFontPtr && !_useMultiFont)
- delete _2byteFontPtr;
+ delete[] _2byteFontPtr;
for (int i = 0; i < 20; i++)
if (_2byteMultiFontPtr[i])
delete _2byteMultiFontPtr[i];
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index d060a6badd..eb8aa5d151 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -356,7 +356,7 @@ protected:
void setupCharsetRenderer();
void setupCostumeRenderer();
- virtual void loadLanguageBundle() {}
+ virtual void loadLanguageBundle();
void loadCJKFont();
void loadKorFont();
void setupMusic(int midi);
@@ -1158,6 +1158,33 @@ public:
int _2byteMultiWidth[20];
int _2byteMultiShadow[20];
+private:
+ struct TranslatedLine {
+ uint32 originalTextOffset;
+ uint32 translatedTextOffset;
+ };
+
+ struct TranslationRange {
+ uint32 left;
+ uint32 right;
+
+ TranslationRange(uint32 left_, uint32 right_) : left(left_), right(right_) {}
+ TranslationRange() : left(0), right(0) {}
+ };
+
+ struct TranslationRoom {
+ Common::HashMap<uint32, TranslationRange> scriptRanges;
+ };
+
+ bool _existLanguageFile;
+ byte *_languageBuffer;
+ int _numTranslatedLines;
+ TranslatedLine *_translatedLines;
+ uint16 *_languageLineIndex;
+ Common::HashMap<byte, TranslationRoom> _roomIndex;
+
+ const byte *searchTranslatedLine(const byte *text, const TranslationRange &range, bool useIndex);
+
public:
/* Scumm Vars */
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index a90f73e333..1c859470d4 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -1212,7 +1212,7 @@ int ScummEngine::convertMessageToString(const byte *msg, byte *dst, int dstSize)
byte lastChr = 0;
const byte *src;
byte *end;
- byte transBuf[384];
+ byte transBuf[2048];
assert(dst);
end = dst + dstSize;
@@ -1222,7 +1222,7 @@ int ScummEngine::convertMessageToString(const byte *msg, byte *dst, int dstSize)
return 0;
}
- if (_game.version >= 7) {
+ if (_game.version >= 7 || isScummvmKorTarget()) {
translateText(msg, transBuf);
src = transBuf;
} else {
@@ -1620,6 +1620,11 @@ static int indexCompare(const void *p1, const void *p2) {
// Create an index of the language file.
void ScummEngine_v7::loadLanguageBundle() {
+ if (isScummvmKorTarget()) {
+ // Support language bundle for FT
+ ScummEngine::loadLanguageBundle();
+ return;
+ }
ScummFile file;
int32 size;
@@ -1796,6 +1801,11 @@ void ScummEngine_v7::playSpeech(const byte *ptr) {
}
void ScummEngine_v7::translateText(const byte *text, byte *trans_buff) {
+ if (isScummvmKorTarget()) {
+ // Support language bundle for FT
+ ScummEngine::translateText(text, trans_buff);
+ return;
+ }
LangIndexNode target;
LangIndexNode *found = NULL;
int i;
@@ -1901,7 +1911,185 @@ void ScummEngine_v7::translateText(const byte *text, byte *trans_buff) {
#endif
+void ScummEngine::loadLanguageBundle() {
+ if (!isScummvmKorTarget()) {
+ _existLanguageFile = false;
+ return;
+ }
+
+ ScummFile file;
+ openFile(file, "korean.trs");
+
+ if (!file.isOpen()) {
+ _existLanguageFile = false;
+ return;
+ }
+
+ _existLanguageFile = true;
+
+ int size = file.size();
+
+ uint32 magic1 = file.readUint32BE();
+ uint32 magic2 = file.readUint32BE();
+
+ if (magic1 != MKTAG('S', 'C', 'V', 'M') || magic2 != MKTAG('T', 'R', 'S', ' ')) {
+ _existLanguageFile = false;
+ return;
+ }
+
+ _numTranslatedLines = file.readUint16LE();
+ _translatedLines = new TranslatedLine[_numTranslatedLines];
+ _languageLineIndex = new uint16[_numTranslatedLines];
+
+ // sanity check
+ for (int i = 0; i < _numTranslatedLines; i++) {
+ _languageLineIndex[i] = 0xffff;
+ }
+
+ for (int i = 0; i < _numTranslatedLines; i++) {
+ int idx = file.readUint16LE();
+ assert(idx < _numTranslatedLines);
+ _languageLineIndex[idx] = i;
+ _translatedLines[i].originalTextOffset = file.readUint32LE();
+ _translatedLines[i].translatedTextOffset = file.readUint32LE();
+ }
+
+ // sanity check
+ for (int i = 0; i < _numTranslatedLines; i++) {
+ if (_languageLineIndex[i] == 0xffff) {
+ error("Invalid language bundle file");
+ }
+ }
+
+ // Room
+ byte numTranslatedRoom = file.readByte();
+ for (uint32 i = 0; i < numTranslatedRoom; i++) {
+ byte roomId = file.readByte();
+
+ TranslationRoom &room = _roomIndex.getVal(roomId);
+
+ uint16 numScript = file.readUint16LE();
+ for (int sc = 0; sc < numScript; sc++) {
+ uint32 scrpKey = file.readUint32LE();
+ uint16 scrpLeft = file.readUint16LE();
+ uint16 scrpRight = file.readUint16LE();
+
+ room.scriptRanges.setVal(scrpKey, TranslationRange(scrpLeft, scrpRight));
+ }
+ }
+
+ int bodyPos = file.pos();
+
+ for (int i = 0; i < _numTranslatedLines; i++) {
+ _translatedLines[i].originalTextOffset -= bodyPos;
+ _translatedLines[i].translatedTextOffset -= bodyPos;
+ }
+ _languageBuffer = new byte[size - bodyPos];
+ file.read(_languageBuffer, size - bodyPos);
+ file.close();
+
+ debug(2, "loadLanguageBundle: Loaded %d entries", _numTranslatedLines);
+}
+
+const byte *ScummEngine::searchTranslatedLine(const byte *text, const TranslationRange &range, bool useIndex) {
+ int textLen = resStrLen(text);
+
+ int left = range.left;
+ int right = range.right;
+
+ int dbgIterationCount = 0;
+
+ while (left <= right) {
+ dbgIterationCount++;
+ debug(8, "searchTranslatedLine: Range: %d - %d", left, right);
+ int mid = (left + right) / 2;
+ int idx = useIndex ? _languageLineIndex[mid] : mid;
+ const byte *originalText = &_languageBuffer[_translatedLines[idx].originalTextOffset];
+ int originalLen = resStrLen(originalText);
+ int compare = memcmp(text, originalText, MIN(textLen + 1, originalLen + 1));
+ if (compare == 0) {
+ debug(8, "searchTranslatedLine: Found in %d iteration", dbgIterationCount);
+ const byte *translatedText = &_languageBuffer[_translatedLines[idx].translatedTextOffset];
+ return translatedText;
+ } else if (compare < 0) {
+ right = mid - 1;
+ } else if (compare > 0) {
+ left = mid + 1;
+ }
+ }
+
+ debug(8, "searchTranslatedLine: Not found in %d iteration", dbgIterationCount);
+
+ return nullptr;
+}
+
void ScummEngine::translateText(const byte *text, byte *trans_buff) {
+ if (_existLanguageFile) {
+ int textLen = resStrLen(text);
+
+ if (_currentScript == 0xff) {
+ // used in drawVerb(), etc
+ debug(7, "translateText: Room=%d, CurrentScript == 0xff", _currentRoom);
+ } else {
+ // Use series of heuristics to preserve "the context of the conversation",
+ // since one English text can be translated differently depending on the context.
+ ScriptSlot *slot = &vm.slot[_currentScript];
+ debug(7, "translateText: Room=%d, Script=%d, WIO=%d", _currentRoom, slot->number, slot->where);
+
+ byte roomKey = 0;
+ if (slot->where != WIO_GLOBAL) {
+ roomKey = _currentRoom;
+ }
+
+ uint32 scriptKey = slot->where << 16 | slot->number;
+ if (slot->where == WIO_ROOM) {
+ scriptKey = slot->where << 16;
+ }
+
+ // First search by _currentRoom and _currentScript
+ Common::HashMap<byte, TranslationRoom>::const_iterator iterator = _roomIndex.find(roomKey);
+ if (iterator != _roomIndex.end()) {
+ const TranslationRoom &room = iterator->_value;
+ TranslationRange scrpRange;
+ if (room.scriptRanges.tryGetVal(scriptKey, scrpRange)) {
+ const byte *translatedText = searchTranslatedLine(text, scrpRange, true);
+ if (translatedText) {
+ debug(7, "translateText: Found by heuristic #1");
+ memcpy(trans_buff, translatedText, resStrLen(translatedText) + 1);
+ return;
+ }
+ }
+ }
+
+ // If not found, search for current room
+ roomKey = _currentRoom;
+ scriptKey = WIO_ROOM << 16;
+ iterator = _roomIndex.find(roomKey);
+ if (iterator != _roomIndex.end()) {
+ const TranslationRoom &room = iterator->_value;
+ TranslationRange scrpRange;
+ if (room.scriptRanges.tryGetVal(scriptKey, scrpRange)) {
+ const byte *translatedText = searchTranslatedLine(text, scrpRange, true);
+ if (translatedText) {
+ debug(7, "translateText: Found by heuristic #2");
+ memcpy(trans_buff, translatedText, resStrLen(translatedText) + 1);
+ return;
+ }
+ }
+ }
+ }
+
+ // Try full search
+ const byte *translatedText = searchTranslatedLine(text, TranslationRange(0, _numTranslatedLines - 1), false);
+ if (translatedText) {
+ debug(7, "translateText: Found by full search");
+ memcpy(trans_buff, translatedText, resStrLen(translatedText) + 1);
+ return;
+ }
+
+ debug(7, "translateText: Not found");
+ }
+
// Default: just copy the string
memcpy(trans_buff, text, resStrLen(text) + 1);
}
More information about the Scummvm-git-logs
mailing list