mirror of
https://github.com/scummvm/scummvm.git
synced 2026-05-21 05:40:43 +00:00
KYRA: Add Korean fan translation support for Hand of Fate
This commit is contained in:
@@ -96,6 +96,7 @@ const Game kyra2Games[] = {
|
||||
{ kKyra2, kPlatformDOS, kTalkieVersion, ES_ESP },
|
||||
{ kKyra2, kPlatformDOS, kTalkieVersion, HE_ISR },
|
||||
{ kKyra2, kPlatformDOS, kTalkieVersion, CS_CZE },
|
||||
{ kKyra2, kPlatformDOS, kTalkieVersion, KO_KOR },
|
||||
|
||||
{ kKyra2, kPlatformFMTowns, kNoSpecial, EN_ANY },
|
||||
{ kKyra2, kPlatformFMTowns, kNoSpecial, JA_JPN },
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
#include "resources/hof_dos_cd_spanish.h"
|
||||
#include "resources/hof_dos_cd_hebrew.h"
|
||||
#include "resources/hof_dos_cd_czech.h"
|
||||
#include "resources/hof_dos_cd_korean.h"
|
||||
|
||||
#include "resources/hof_fmtowns.h"
|
||||
#include "resources/hof_fmtowns_english.h"
|
||||
@@ -1374,6 +1375,8 @@ static const ResourceProvider resourceProviders[] = {
|
||||
{ k2SeqplayTlkFiles, kKyra2, kPlatformDOS, kTalkieVersion, ES_ESP, &k2SeqplayTlkFilesDOSCDSpanishProvider },
|
||||
{ k2SeqplayStrings, kKyra2, kPlatformDOS, kTalkieVersion, CS_CZE, &k2SeqplayStringsDOSCDCzechProvider },
|
||||
{ k2SeqplayTlkFiles, kKyra2, kPlatformDOS, kTalkieVersion, CS_CZE, &k2SeqplayTlkFilesDOSCDCzechProvider },
|
||||
{ k2SeqplayStrings, kKyra2, kPlatformDOS, kTalkieVersion, KO_KOR, &k2SeqplayStringsDOSCDKoreanProvider },
|
||||
{ k2SeqplayTlkFiles, kKyra2, kPlatformDOS, kTalkieVersion, KO_KOR, &k2SeqplayTlkFilesDOSCDKoreanProvider },
|
||||
{ k2SeqplayPakFiles, kKyra2, kPlatformFMTowns, kNoSpecial, UNK_LANG, &k2SeqplayPakFilesFMTownsProvider },
|
||||
{ k2SeqplayStrings, kKyra2, kPlatformFMTowns, kNoSpecial, EN_ANY, &k2SeqplayStringsFMTownsEnglishProvider },
|
||||
{ k2SeqplaySfxFiles, kKyra2, kPlatformFMTowns, kNoSpecial, UNK_LANG, &k2SeqplaySfxFilesFMTownsProvider },
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
static const char *const k2SeqplayStringsDOSCDKorean[104] = {
|
||||
"\xC5\xB0\xB6\xF5\xB5\xF0\xBE\xC6\xB0\xA1 \xBB\xE7\xB6\xF3\xC1\xF6\xB0\xED \xC0\xD6\xB4\xD9!", // "키란디아가 사라지고 있다!"
|
||||
"\xB9\xD9\xC0\xA7 \xC7\xCF\xB3\xAA\xC7\xCF\xB3\xAA...", // "바위 하나하나..."
|
||||
"...\xB1\xD7\xB8\xAE\xB0\xED \xB3\xAA\xB9\xAB \xC7\xD1 \xB1\xD7\xB7\xE7 \xC7\xD1 \xB1\xD7\xB7\xE7.", // "...그리고 나무 한 그루 한 그루."
|
||||
"\xC5\xB0\xB6\xF5\xB5\xF0\xBE\xC6\xB0\xA1 \xBC\xD2\xB8\xEA\xC7\xCF\xB0\xED \xC0\xD6\xB4\xD9!", // "키란디아가 소멸하고 있다!"
|
||||
"\xBF\xD5\xB1\xC3 \xB8\xB6\xB9\xFD\xBB\xE7\xB5\xE9\xC0\xCC \xB4\xE7\xC8\xB2\xC7\xCF\xB0\xED \xC0\xD6\xB4\xD9.", // "왕궁 마법사들이 당황하고 있다."
|
||||
"\xB8\xF0\xB5\xE7 \xC2\xFC\xB0\xED\xB9\xAE\xC7\xE5\xC0\xBB \xB4\xD9 \xC3\xA3\xBE\xC6\xBA\xC3\xB4\xD9.", // "모든 참고문헌을 다 찾아봤다."
|
||||
"\xB8\xB6\xB8\xA3\xC4\xDA\xBF\xCD \xB1\xD7\xC0\xC7 \xBB\xF5 \xBD\xC3\xC1\xBE\xB5\xB5 \xC8\xB8\xC0\xC7\xBF\xA1 \xC2\xFC\xBC\xAE\xC0\xCC \xC7\xE3\xB6\xF4\xB5\xC7\xBE\xFA\xB4\xD9.", // "마르코와 그의 새 시종도 회의에 참석이 허락되었다."
|
||||
"\xB4\xD9\xC7\xE0\xC8\xF7 \xBF\xEE\xB8\xED\xC0\xC7 \xBC\xD5\xC0\xBA \xC0\xCC\xB7\xB1 \xB9\xAE\xC1\xA6\xBF\xA1 \xB0\xE6\xC7\xE8\xC0\xCC \xC0\xD6\xBE\xFA\xB4\xD9.", // "다행히 운명의 손은 이런 문제에 경험이 있었다."
|
||||
"\xB1\xD7\xB8\xAE\xB0\xED \xB8\xB6\xC4\xA7\xB3\xBB \xB0\xE8\xC8\xB9\xC0\xCC \xBD\xC2\xC0\xCE\xB5\xC7\xBE\xFA\xB4\xD9...", // "그리고 마침내 계획이 승인되었다..."
|
||||
"...\xB8\xB6\xB9\xFD\xC0\xC7 \xB4\xE9\xB5\xB9\xC0\xBB...", // "...마법의 닻돌을..."
|
||||
"...\xBC\xBC\xBB\xF3\xC0\xC7 \xC1\xDF\xBD\xC9\xBF\xA1\xBC\xAD \xC3\xA3\xBE\xC6\xBF\xCD\xBE\xDF \xC7\xDF\xB4\xD9.", // "...세상의 중심에서 찾아와야 했다."
|
||||
"\xC5\xB0\xB6\xF5\xB5\xF0\xBE\xC6 \xB8\xB6\xB9\xFD\xBB\xE7 \xC1\xDF \xB0\xA1\xC0\xE5 \xC0\xFE\xC0\xBA \xC0\xDC\xC6\xBC\xBE\xC6\xB0\xA1 \xB5\xB9\xC0\xBB \xC3\xA3\xB4\xC2 \xC0\xD3\xB9\xAB\xBF\xA1 \xBC\xB1\xC5\xC3\xB5\xC7\xBE\xFA\xB4\xD9.", // "키란디아 마법사 중 가장 젊은 잔티아가 돌을 찾는 임무에 선택되었다."
|
||||
"\xBF\xEE\xB8\xED\xC0\xC7 \xBC\xD5\xC0\xBB \xC7\xC3\xB7\xB9\xC0\xCC\xC7\xD8 \xC1\xD6\xBC\xC5\xBC\xAD \xB0\xA8\xBB\xE7\xC7\xD5\xB4\xCF\xB4\xD9.", // "운명의 손을 플레이해 주셔서 감사합니다."
|
||||
"\xC0\xCC \xC1\xA4\xB5\xB5 \xBA\xED\xB7\xE7\xBA\xA3\xB8\xAE\xB8\xE9 \xBC\xBC\xBB\xF3\xC0\xC7 \xC1\xDF\xBD\xC9\xC0\xB8\xB7\xCE \xB0\xA1\xB4\xC2 \xC6\xF7\xC5\xD0\xC0\xBB \xBF\xAD \xBC\xF6 \xC0\xD6\xC0\xBB \xB0\xC5\xBE\xDF.", // "이 정도 블루베리면 세상의 중심으로 가는 포털을 열 수 있을 거야."
|
||||
" DUMMY STRING... ",
|
||||
" DUMMY STRING... ",
|
||||
"\xC0\xCC\xB7\xB1! \xB3\xBB \xC0\xE5\xBA\xF1\xB0\xA1 \xB4\xD9 \xB5\xB5\xB5\xCF\xB8\xC2\xBE\xD2\xBE\xEE!", // "이런! 내 장비가 다 도둑맞았어!"
|
||||
" DUMMY STRING... ",
|
||||
"\xB3\xBB\xB0\xA1 \xC0\xFA \xBE\xC6\xB7\xA1\xB1\xEE\xC1\xF6 \xB0\xC9\xBE\xEE\xB0\xA5 \xB0\xC5\xB6\xF3\xB0\xED \xBB\xFD\xB0\xA2\xC7\xCF\xB8\xE9, \xB9\xCC\xC4\xA5 \xB0\xC5\xBE\xDF!", // "내가 저 아래까지 걸어갈 거라고 생각하면, 미칠 거야!"
|
||||
" DUMMY STRING... ",
|
||||
" DUMMY STRING... ",
|
||||
"\xBC\xAD\xB5\xD1\xB7\xAF \xC6\xC4\xBF\xEE!", // "서둘러 파운!"
|
||||
"\xBE\xDF, \xC5\xAB\xC0\xCF \xB3\xAF \xBB\xB7\xC7\xDF\xB3\xD7!", // "야, 큰일 날 뻔했네!"
|
||||
"\xB8\xC2\xBE\xC6. \xB3\xAA\xB4\xC2 \xB4\xD9\xBD\xC5 \xBB\xE7\xB3\xC9\xC0\xBA \xBE\xC8 \xB0\xA5 \xB0\xC5\xBE\xDF!", // "맞아. 나는 다신 사냥은 안 갈 거야!"
|
||||
"\xB0\xB3\xB1\xBC\xB0\xB3\xB1\xBC.", // "개굴개굴."
|
||||
"\xB8\xEE \xB9\xF8\xC0\xBB \xB8\xBB\xC7\xD8\xBE\xDF \xBE\xCB\xBE\xC6\xB5\xE9\xBE\xEE? \xB3\xCA\xB4\xC2 \xB5\xCE\xB2\xA8\xBA\xF1\xB6\xF3\xB0\xED.", // "몇 번을 말해야 알아들어? 너는 두꺼비라고."
|
||||
"\xC0\xCC\xB7\xB1! \xC4\xA1\xC1\xEE\xB0\xA1 \xB4\xD9 \xB6\xB3\xBE\xEE\xC1\xB3\xBE\xEE!", // "이런! 치즈가 다 떨어졌어!"
|
||||
"\xC0\xCC \xB1\xCD\xC1\xF6\xB8\xA6 \xBD\xE1\xBA\xB8\xC0\xDA. \xC1\xD6\xC8\xB2\xBB\xF6\xC0\xCC\xB3\xD7.", // "이 귀지를 써보자. 주황색이네."
|
||||
"\xBE\xF6\xB8\xB6, \xB3\xAA\xB4\xC2 \xBE\xF0\xC1\xA6 \xB4\xE3\xC0\xEF\xC0\xCC\xB5\xA2\xB1\xBC\xC0\xBB \xB9\xDE\xBE\xC6\xBF\xE4?", // "엄마, 나는 언제 담쟁이덩굴을 받아요?"
|
||||
"\xC0\xFA\xB8\xAE \xBA\xF1\xC4\xD1, \xBD\xB5!", // "저리 비켜, 슉!"
|
||||
"\xB3\xD7\xB0\xA1 \xC0\xDA\xB8\xA3\xB0\xED, \xB3\xBB\xB0\xA1 \xB0\xED\xB8\xA6\xB0\xD4.", // "네가 자르고, 내가 고를게."
|
||||
"\xBE\xC6\xB4\xCF. \xB3\xD7\xB0\xA1 \xC0\xDA\xB8\xA3\xB0\xED \xB3\xBB\xB0\xA1 \xB0\xED\xB8\xA6\xB0\xD4.", // "아니. 네가 자르고 내가 고를게."
|
||||
"\xBE\xC6\xC1\xF7\xB5\xB5 \xBE\xE2\xC6\xC5\xC7\xD1 \xB8\xF0\xB9\xE6\xC0\xDB\xC0\xCC\xB6\xF3\xB0\xED \xBB\xFD\xB0\xA2\xC7\xD8.", // "아직도 얄팍한 모방작이라고 생각해."
|
||||
"\xBE\xEE\xC8\xDE, \xB3\xCD \xBE\xFB\xB5\xA2\xC0\xCC\xB0\xA1 \xB9\xB0\xB7\xC1\xB5\xB5 5\xC0\xBD\xBA\xB8\xB0\xDD\xC0\xCC \xB9\xBA\xC1\xF6 \xB8\xF8 \xBE\xCB\xBE\xC6\xB5\xE9\xC0\xBB \xB0\xC5\xBE\xDF!", // "어휴, 넌 엉덩이가 물려도 5음보격이 뭔지 못 알아들을 거야!"
|
||||
"Executive Producer",
|
||||
"Brett W. Sperry",
|
||||
"Direction & Design",
|
||||
"Rick Gush",
|
||||
"Lead Programmer",
|
||||
"Michael Legg",
|
||||
"Art Management",
|
||||
"Louis Castle",
|
||||
"Joseph B. Hewitt IV",
|
||||
"Lead Artist",
|
||||
"Rick Parks",
|
||||
"Additional Coding by",
|
||||
"Philip W. Gorrow",
|
||||
"Mike Grayford",
|
||||
"Mark McCubbin",
|
||||
"Artists",
|
||||
"Cameron Chun",
|
||||
"Cary Averett",
|
||||
"Cindy Chinn",
|
||||
"Elie Arabian",
|
||||
"Fei Cheng",
|
||||
"Ferby Miguel",
|
||||
"Frank Mendeola",
|
||||
"Jack Martin",
|
||||
"Jerry Moore",
|
||||
"DUMMY STRING... ",
|
||||
"Judith Peterson",
|
||||
"Larry Miller",
|
||||
"Lenny Lee",
|
||||
"Louise Sandoval",
|
||||
"Ren Olsen",
|
||||
"Music & Sounds by",
|
||||
"Paul Mudra",
|
||||
"Frank Klepacki",
|
||||
"Dwight Okahara",
|
||||
"Pat Collins",
|
||||
"Quality Assurance by",
|
||||
"Glenn Sperry",
|
||||
"Michael Lightner",
|
||||
"William Foster",
|
||||
"Jesse Clemit",
|
||||
"Jeff Fillhaber",
|
||||
"Manual, Package Design",
|
||||
"& Fulfillment",
|
||||
"Eydie Laramore",
|
||||
"Lisa Marcinko",
|
||||
"Lauren Rifkin",
|
||||
"\xC3\xE0\xC7\xCF\xC7\xD5\xB4\xCF\xB4\xD9!", // "축하합니다!"
|
||||
"\xBF\xEE\xB8\xED\xC0\xC7 \xBC\xD5\xC0\xBB \xC7\xC3\xB7\xB9\xC0\xCC\xC7\xD8 \xC1\xD6\xBC\xC5\xBC\xAD \xB0\xA8\xBB\xE7\xC7\xD5\xB4\xCF\xB4\xD9!", // "운명의 손을 플레이해 주셔서 감사합니다!"
|
||||
"Guest Coding",
|
||||
"Producer Liaison",
|
||||
"Scott Duckett",
|
||||
"Irvine Testers",
|
||||
"Chris McFarland",
|
||||
"Paul Moore",
|
||||
"Chad Soares",
|
||||
"Jared Brinkley",
|
||||
"Jon Willliams",
|
||||
"Chris Toft",
|
||||
"Joe Kucan's Hair by",
|
||||
"Theodore A. Morris",
|
||||
"\xB0\xD4\xC0\xD3 \xBA\xD2\xB7\xAF\xBF\xC0\xB1\xE2", // "게임 불러오기"
|
||||
"\xC0\xCE\xC6\xAE\xB7\xCE \xBA\xB8\xB1\xE2", // "인트로 보기"
|
||||
"\xBB\xF5 \xB0\xD4\xC0\xD3 \xBD\xC3\xC0\xDB", // "새 게임 시작"
|
||||
"\xB0\xD4\xC0\xD3 \xC1\xBE\xB7\xE1", // "게임 종료"
|
||||
"Special Thanks to",
|
||||
"Sake Joe Bostic-san",
|
||||
"Tim Fritz",
|
||||
"Kenny Dunne",
|
||||
"\xBF\xEE\xB8\xED\xC0\xC7 \xBC\xD5\xC0\xBB \xC7\xC3\xB7\xB9\xC0\xCC\xC7\xD8 \xC1\xD6\xBC\xC5\xBC\xAD \xB0\xA8\xBB\xE7\xC7\xD5\xB4\xCF\xB4\xD9.\n" // "운명의 손을 플레이해 주셔서 감사합니다.\n"
|
||||
};
|
||||
|
||||
static const StringListProvider k2SeqplayStringsDOSCDKoreanProvider = { ARRAYSIZE(k2SeqplayStringsDOSCDKorean), k2SeqplayStringsDOSCDKorean };
|
||||
|
||||
static const char *const k2SeqplayTlkFilesDOSCDKorean[14] = {
|
||||
"EINTRO1",
|
||||
"EINTRO2",
|
||||
"EINTRO3",
|
||||
"EINTRO4",
|
||||
"EINTRO5",
|
||||
"EINTRO6",
|
||||
"EINTRO7",
|
||||
"EINTRO8",
|
||||
"EINTRO9",
|
||||
"EINTRO10",
|
||||
"EINTRO11",
|
||||
"EINTRO12",
|
||||
"EGLOW",
|
||||
""
|
||||
};
|
||||
|
||||
static const StringListProvider k2SeqplayTlkFilesDOSCDKoreanProvider = { ARRAYSIZE(k2SeqplayTlkFilesDOSCDKorean), k2SeqplayTlkFilesDOSCDKorean };
|
||||
Binary file not shown.
@@ -729,6 +729,42 @@ const KYRAGameDescription adGameDescs[] = {
|
||||
KYRA2_FLOPPY_FAN_FLAGS(Common::RU_RUS, Common::EN_ANY)
|
||||
},
|
||||
|
||||
{ // Korean fan translation (based on FM-TOWNS).
|
||||
// Detection language is KO_KOR so the entry is distinguishable from the
|
||||
// base EN_ANY FM-Towns entries (all share the same WSCORE.PAK hash).
|
||||
// kyra.dat does not carry KO_KOR data; loadStaticResourceFile() falls
|
||||
// back to replacedLang (EN_ANY) automatically for fan translations.
|
||||
{
|
||||
"kyra2",
|
||||
nullptr,
|
||||
AD_ENTRY2s("WSCORE.PAK", "c44de1302b67f27d4707409987b7a685", AD_NO_SIZE,
|
||||
"KOREAN.FNT", "6b32ad695188e82b770a7739ffeb57db", 42300),
|
||||
Common::KO_KOR,
|
||||
Common::kPlatformFMTowns,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO3(GUIO_NOSPEECH, GUIO_MIDITOWNS, GUIO_RENDERFMTOWNS)
|
||||
},
|
||||
FLAGS_FAN(Common::KO_KOR, Common::EN_ANY, false, false, false, false, false, false, false, false, false, Kyra::GI_KYRA2)
|
||||
},
|
||||
|
||||
{ // Korean fan translation (based on DOS CD), English voice + Korean subtitles.
|
||||
// Detection language is KO_KOR so the entry is distinguishable from the
|
||||
// base EN_ANY CD entry (both share the same FATE.PAK hash). kyra.dat does
|
||||
// not carry KO_KOR data; loadStaticResourceFile() falls back to
|
||||
// replacedLang (EN_ANY) automatically for fan translations.
|
||||
{
|
||||
"kyra2",
|
||||
"CD",
|
||||
AD_ENTRY2s("FATE.PAK", "28cbad1c5bf06b2d3825ae57d760d032", AD_NO_SIZE,
|
||||
"KOREAN.FNT", "6b32ad695188e82b770a7739ffeb57db", 42300),
|
||||
Common::KO_KOR,
|
||||
Common::kPlatformDOS,
|
||||
ADGF_DROPLANGUAGE | ADGF_CD,
|
||||
GUIO5(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_MIDIPCSPK, GUIO_RENDERVGA)
|
||||
},
|
||||
KYRA2_CD_FAN_FLAGS(Common::KO_KOR, Common::EN_ANY)
|
||||
},
|
||||
|
||||
{ // CD version
|
||||
{
|
||||
"kyra2",
|
||||
|
||||
@@ -192,13 +192,11 @@ Common::Error KyraEngine_HoF::init() {
|
||||
KyraEngine_v1::init();
|
||||
initStaticResource();
|
||||
|
||||
_text = new TextDisplayer_HoF(this, _screen);
|
||||
assert(_text);
|
||||
_gui = new GUI_HoF(this);
|
||||
assert(_gui);
|
||||
_gui->initStaticData();
|
||||
_tim = new TIMInterpreter(this, _screen, _system);
|
||||
assert(_tim);
|
||||
// Korean fan translation: kyra.dat was loaded with EN_ANY, now switch to KO_KOR
|
||||
// so that file extensions and text handling all use Korean paths.
|
||||
// Font loading must happen before initStaticData() which calls getFontHeight().
|
||||
if (_flags.fanLang == Common::KO_KOR)
|
||||
_flags.lang = Common::KO_KOR;
|
||||
|
||||
if (_flags.isDemo && !_flags.isTalkie) {
|
||||
_screen->loadFont(_screen->FID_8_FNT, "FONT9P.FNT");
|
||||
@@ -210,8 +208,22 @@ Common::Error KyraEngine_HoF::init() {
|
||||
_screen->loadFont(_screen->FID_8_FNT, "8FAT.FNT");
|
||||
_screen->loadFont(_screen->FID_BOOKFONT_FNT, "BOOKFONT.FNT");
|
||||
}
|
||||
|
||||
if (_flags.lang == Common::KO_KOR) {
|
||||
_screen->loadFont(Screen::FID_KOREAN_FNT, "KOREAN.FNT");
|
||||
_defaultFont = Screen::FID_KOREAN_FNT;
|
||||
_bookFont = Screen::FID_KOREAN_FNT;
|
||||
}
|
||||
_screen->setFont(_defaultFont);
|
||||
|
||||
_text = new TextDisplayer_HoF(this, _screen);
|
||||
assert(_text);
|
||||
_gui = new GUI_HoF(this);
|
||||
assert(_gui);
|
||||
_gui->initStaticData();
|
||||
_tim = new TIMInterpreter(this, _screen, _system);
|
||||
assert(_tim);
|
||||
|
||||
_screen->setAnimBlockPtr(3504);
|
||||
_screen->setScreenDim(0);
|
||||
|
||||
@@ -294,6 +306,9 @@ void KyraEngine_HoF::startup() {
|
||||
_trackMap = _dosTrackMap;
|
||||
_trackMapSize = _dosTrackMapSize;
|
||||
|
||||
// Restore the default font after intro sequences may have changed it
|
||||
_screen->setFont(_defaultFont);
|
||||
|
||||
allocAnimObjects(1, 10, 30);
|
||||
|
||||
_screen->_curPage = 0;
|
||||
@@ -826,7 +841,7 @@ uint8 *KyraEngine_HoF::getTableEntry(uint8 *buffer, int id) {
|
||||
Common::String KyraEngine_HoF::getTableString(int id, uint8 *buffer, bool decode) {
|
||||
Common::String string((char *)getTableEntry(buffer, id));
|
||||
|
||||
if (decode && _flags.lang != Common::JA_JPN) {
|
||||
if (decode && _flags.lang != Common::JA_JPN && _flags.lang != Common::KO_KOR) {
|
||||
string = Util::decodeString2(Util::decodeString1(string));
|
||||
}
|
||||
|
||||
@@ -881,7 +896,7 @@ void KyraEngine_HoF::showChapterMessage(int id, int16 palIndex) {
|
||||
void KyraEngine_HoF::updateCommandLineEx(int str1, int str2, int16 palIndex) {
|
||||
Common::String str = getTableString(str1, _cCodeBuffer, true);
|
||||
|
||||
if (_flags.lang != Common::ZH_TWN && _flags.lang != Common::JA_JPN && _flags.lang != Common::HE_ISR) {
|
||||
if (_flags.lang != Common::ZH_TWN && _flags.lang != Common::JA_JPN && _flags.lang != Common::KO_KOR && _flags.lang != Common::HE_ISR) {
|
||||
if (uint32 i = (uint32)str.findFirstOf(' ') + 1) {
|
||||
str.erase(0, i);
|
||||
str.setChar(toupper(str[0]), 0);
|
||||
@@ -889,7 +904,7 @@ void KyraEngine_HoF::updateCommandLineEx(int str1, int str2, int16 palIndex) {
|
||||
}
|
||||
|
||||
if (str2 > 0) {
|
||||
if (_flags.lang != Common::ZH_TWN && _flags.lang != Common::JA_JPN && _flags.lang != Common::HE_ISR)
|
||||
if (_flags.lang != Common::ZH_TWN && _flags.lang != Common::JA_JPN && _flags.lang != Common::KO_KOR && _flags.lang != Common::HE_ISR)
|
||||
str += " ";
|
||||
if (_flags.lang == Common::HE_ISR)
|
||||
str = getTableString(str2, _cCodeBuffer, 1) + " " + str + ".";
|
||||
@@ -1008,6 +1023,12 @@ void KyraEngine_HoF::loadNPCScript() {
|
||||
filename[5] = 'J';
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Korean fan translation: no Korean NPC script exists, fall back
|
||||
// to the English one (_NPCE.EMC) which is present on DOS CD.
|
||||
filename[5] = 'E';
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
};
|
||||
@@ -1928,15 +1949,27 @@ void KyraEngine_HoF::writeSettings() {
|
||||
_flags.lang = Common::JA_JPN;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
_flags.lang = Common::KO_KOR;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
default:
|
||||
_flags.lang = _langIntern ? Common::ZH_TWN : Common::EN_ANY;
|
||||
}
|
||||
|
||||
if (_flags.lang == _flags.replacedLang && _flags.fanLang != Common::UNK_LANG)
|
||||
if (_flags.lang == _flags.replacedLang && _flags.fanLang != Common::UNK_LANG
|
||||
&& _flags.fanLang != Common::KO_KOR)
|
||||
_flags.lang = _flags.fanLang;
|
||||
|
||||
ConfMan.set("language", Common::getLanguageCode(_flags.lang));
|
||||
// Korean fan translation: the detection entry now uses KO_KOR as the
|
||||
// detection language with ADGF_DROPLANGUAGE, so we must write "ko" to
|
||||
// config. Writing "en" would cause the Advanced Detector to prefer the
|
||||
// base EN_ANY CD entry on next launch, reverting to English.
|
||||
if (_flags.fanLang == Common::KO_KOR)
|
||||
ConfMan.set("language", Common::getLanguageCode(Common::KO_KOR));
|
||||
else
|
||||
ConfMan.set("language", Common::getLanguageCode(_flags.lang));
|
||||
|
||||
KyraEngine_v1::writeSettings();
|
||||
}
|
||||
|
||||
@@ -79,8 +79,14 @@ KyraEngine_v2::KyraEngine_v2(OSystem *system, const GameFlags &flags, const Engi
|
||||
_lang = 0;
|
||||
_scriptLang = 0;
|
||||
Common::Language lang = Common::parseLanguage(ConfMan.get("language"));
|
||||
if (lang == _flags.fanLang && _flags.replacedLang != Common::UNK_LANG)
|
||||
lang = _flags.replacedLang;
|
||||
|
||||
// Korean fan translation: always use KO_KOR regardless of config value,
|
||||
// so that _lang is set to 5 and Korean file extensions are selected.
|
||||
if (_flags.fanLang == Common::KO_KOR) {
|
||||
lang = Common::KO_KOR;
|
||||
} else if (lang == _flags.fanLang && _flags.replacedLang != Common::UNK_LANG) {
|
||||
lang = _flags.replacedLang;
|
||||
}
|
||||
|
||||
if (_flags.extraLang == Common::ZH_TWN)
|
||||
_langIntern = 1;
|
||||
@@ -115,6 +121,10 @@ KyraEngine_v2::KyraEngine_v2(OSystem *system, const GameFlags &flags, const Engi
|
||||
_lang = 4;
|
||||
break;
|
||||
|
||||
case Common::KO_KOR:
|
||||
_lang = 5;
|
||||
break;
|
||||
|
||||
default:
|
||||
warning("unsupported language, switching back to English");
|
||||
_lang = 0;
|
||||
|
||||
@@ -427,7 +427,17 @@ void KyraEngine_HoF::startSceneScript(int unk1) {
|
||||
_sceneCommentString = "Undefined scene comment string!";
|
||||
_emc->init(&_sceneScriptState, &_sceneScriptData);
|
||||
|
||||
filename = Common::String(_sceneList[sceneId].filename1) + "." + _scriptLangExt[(_flags.platform == Common::kPlatformDOS && !_flags.isTalkie) ? 0 : _lang];
|
||||
// Korean fan translation uses .KMC scripts (Korean-patched EMC files).
|
||||
// Fall back to .EMC if .KMC is not available. Other langs clamp to valid range.
|
||||
if (_flags.lang == Common::KO_KOR) {
|
||||
filename = Common::String(_sceneList[sceneId].filename1) + ".KMC";
|
||||
if (!_res->exists(filename.c_str())) {
|
||||
filename = Common::String(_sceneList[sceneId].filename1) + ".EMC";
|
||||
}
|
||||
} else {
|
||||
int scriptLangIdx = (_flags.platform == Common::kPlatformDOS && !_flags.isTalkie) ? 0 : (_lang > 3 ? 0 : _lang);
|
||||
filename = Common::String(_sceneList[sceneId].filename1) + "." + _scriptLangExt[scriptLangIdx];
|
||||
}
|
||||
_res->exists(filename.c_str(), true);
|
||||
_emc->load(filename.c_str(), &_sceneScriptData, &_opcodes);
|
||||
runSceneScript7();
|
||||
|
||||
@@ -1426,8 +1426,22 @@ bool Screen::loadFont(FontId fontId, const char *filename) {
|
||||
fnt->load(str);
|
||||
}
|
||||
} else if (fontId == FID_KOREAN_FNT) {
|
||||
const uint16 *lookupTable = _vm->staticres()->loadRawDataBe16(k1TwoByteFontLookupTable, temp);
|
||||
fnt = new JohabFontLoK(_fonts[FID_8_FNT], lookupTable, temp);
|
||||
if (_vm->game() == GI_KYRA2) {
|
||||
Common::Array<Font*> *fa = new Common::Array<Font*>;
|
||||
fa->push_back(new KoreanOneByteFontHOF(SCREEN_W));
|
||||
fa->push_back(new KoreanTwoByteFontHOF(SCREEN_W));
|
||||
fnt = new MultiSubsetFont(fa);
|
||||
// Load 1-byte (ASCII) font from ENGLISH.FNT first, then
|
||||
// the main KOREAN.FNT call below will load the 2-byte glyphs.
|
||||
Common::SeekableReadStream *engFile = _vm->resource()->createReadStream("ENGLISH.FNT");
|
||||
if (engFile) {
|
||||
fnt->load(*engFile);
|
||||
delete engFile;
|
||||
}
|
||||
} else {
|
||||
const uint16 *lookupTable = _vm->staticres()->loadRawDataBe16(k1TwoByteFontLookupTable, temp);
|
||||
fnt = new JohabFontLoK(_fonts[FID_8_FNT], lookupTable, temp);
|
||||
}
|
||||
} else {
|
||||
fnt = new DOSFont();
|
||||
}
|
||||
|
||||
@@ -410,6 +410,26 @@ private:
|
||||
void processColorMap() override;
|
||||
};
|
||||
|
||||
class KoreanOneByteFontHOF final : public ChineseFont {
|
||||
public:
|
||||
KoreanOneByteFontHOF(int pitch) : ChineseFont(pitch, 8, 9, 8, 10, 0, 0) {}
|
||||
Type getType() const override { return kJohab; }
|
||||
private:
|
||||
bool hasGlyphForCharacter(uint16 c) const override { return !(c & 0x80); }
|
||||
uint32 getFontOffset(uint16 c) const override { return (c & 0x7F) * 9; }
|
||||
void processColorMap() override;
|
||||
};
|
||||
|
||||
class KoreanTwoByteFontHOF final : public ChineseFont {
|
||||
public:
|
||||
KoreanTwoByteFontHOF(int pitch) : ChineseFont(pitch, 10, 9, 10, 10, 0, 0) {}
|
||||
Type getType() const override { return kJohab; }
|
||||
private:
|
||||
bool hasGlyphForCharacter(uint16 c) const override { return (c & 0x80); }
|
||||
uint32 getFontOffset(uint16 c) const override;
|
||||
void processColorMap() override;
|
||||
};
|
||||
|
||||
class MultiSubsetFont final : public Font {
|
||||
public:
|
||||
MultiSubsetFont(Common::Array<Font*> *subsets) : Font(), _subsets(subsets) {}
|
||||
|
||||
@@ -109,4 +109,29 @@ void ChineseTwoByteFontHOF::processColorMap() {
|
||||
_pixelColorShading = !(_colorMap[1] == 207 || _colorMap[1] > 240);
|
||||
}
|
||||
|
||||
void KoreanOneByteFontHOF::processColorMap() {
|
||||
_textColor[0] = _colorMap[1];
|
||||
_textColor[1] = _colorMap[0] | (_colorMap[0] << 8);
|
||||
_pixelColorShading = false;
|
||||
}
|
||||
|
||||
uint32 KoreanTwoByteFontHOF::getFontOffset(uint16 c) const {
|
||||
// fetchChar() stores: first byte (EUC-KR high) in low 8 bits,
|
||||
// second byte (EUC-KR low) in high 8 bits. So:
|
||||
// c & 0xFF = first byte (high byte of EUC-KR, 0xB0-0xC8)
|
||||
// c >> 8 = second byte (low byte of EUC-KR, 0xA1-0xFE)
|
||||
uint8 high = c & 0xFF;
|
||||
uint8 low = (c >> 8) & 0xFF;
|
||||
if (high < 0xB0 || high > 0xC8 || low < 0xA1 || low > 0xFE)
|
||||
return 0;
|
||||
uint32 index = (high - 0xB0) * 94 + (low - 0xA1);
|
||||
return index * 18; // 10x9 pixels, 2 bytes/row, 9 rows = 18 bytes/glyph
|
||||
}
|
||||
|
||||
void KoreanTwoByteFontHOF::processColorMap() {
|
||||
_textColor[0] = _colorMap[1];
|
||||
_textColor[1] = _colorMap[0] | (_colorMap[0] << 8);
|
||||
_pixelColorShading = false;
|
||||
}
|
||||
|
||||
} // End of namespace Kyra
|
||||
|
||||
@@ -1021,6 +1021,9 @@ int GUI_HoF::gameOptionsTalkie(Button *caller) {
|
||||
|
||||
int GUI_HoF::changeLanguage(Button *caller) {
|
||||
updateMenuButton(caller);
|
||||
// Korean fan translation: language is fixed to Korean, do not cycle.
|
||||
if (_vm->gameFlags().fanLang == Common::KO_KOR)
|
||||
return 0;
|
||||
++_vm->_lang;
|
||||
_vm->_lang %= _vm->_numLang;
|
||||
setupOptionsButtons();
|
||||
@@ -1052,6 +1055,11 @@ void GUI_HoF::setupOptionsButtons() {
|
||||
_gameOptions.item[1].itemId = 33;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Korean fan translation: string 51 in OPTIONS.KOR = "한국어"
|
||||
_gameOptions.item[1].itemId = 51;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -157,7 +157,33 @@ bool StaticResource::loadStaticResourceFile() {
|
||||
if (!res->loadPakFile(staticDataFilename(), *i))
|
||||
continue;
|
||||
|
||||
if ((setLanguage(_vm->gameFlags().lang) && prefetchId(-1))) {
|
||||
// Fan translations (e.g. Korean) may provide only a subset of
|
||||
// resources in kyra.dat (e.g. intro strings). Load the base
|
||||
// (replaced) language first, then merge fan language entries on
|
||||
// top — only replacing IDs that the fan language provides.
|
||||
if (_vm->gameFlags().fanLang != Common::UNK_LANG &&
|
||||
_vm->gameFlags().replacedLang != Common::UNK_LANG) {
|
||||
if ((setLanguage(_vm->gameFlags().replacedLang) && prefetchId(-1))) {
|
||||
foundWorkingKyraDat = true;
|
||||
// Merge: load the fan language ID map WITHOUT clearing
|
||||
// existing _dataTable entries, then reload only the
|
||||
// IDs that changed.
|
||||
Common::SeekableReadStream *fanMap = loadIdMap(_vm->gameFlags().fanLang);
|
||||
if (fanMap) {
|
||||
int numIDs = fanMap->readUint16BE();
|
||||
while (numIDs--) {
|
||||
uint16 id2 = fanMap->readUint16BE();
|
||||
uint8 type = fanMap->readByte();
|
||||
uint32 filename = fanMap->readUint32BE();
|
||||
_dataTable[id2] = DataDescriptor(filename, type);
|
||||
unloadId(id2);
|
||||
prefetchId(id2);
|
||||
}
|
||||
delete fanMap;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if ((setLanguage(_vm->gameFlags().lang) && prefetchId(-1))) {
|
||||
foundWorkingKyraDat = true;
|
||||
break;
|
||||
}
|
||||
@@ -1455,7 +1481,8 @@ const char *const KyraEngine_HoF::_languageExtension[] = {
|
||||
"ITA", Italian and Spanish were never included
|
||||
"SPA"*/
|
||||
"JPN",
|
||||
"POL"
|
||||
"POL",
|
||||
"KOR"
|
||||
};
|
||||
|
||||
const char *const KyraEngine_HoF::_scriptLangExt[] = {
|
||||
|
||||
@@ -389,7 +389,7 @@ SeqPlayer_HOF::SeqPlayer_HOF(KyraEngine_v1 *vm, Screen_v2 *screen, OSystem *syst
|
||||
|
||||
memset(_hofDemoItemShapes, 0, sizeof(_hofDemoItemShapes));
|
||||
|
||||
_defaultFont = (_vm->gameFlags().lang == Common::ZH_TWN) ? Screen::FID_CHINESE_FNT : ((_vm->gameFlags().lang == Common::JA_JPN) ? Screen::FID_SJIS_FNT : Screen::FID_GOLDFONT_FNT);
|
||||
_defaultFont = (_vm->gameFlags().lang == Common::ZH_TWN) ? Screen::FID_CHINESE_FNT : ((_vm->gameFlags().lang == Common::JA_JPN) ? Screen::FID_SJIS_FNT : ((_vm->gameFlags().lang == Common::KO_KOR) ? Screen::FID_KOREAN_FNT : Screen::FID_GOLDFONT_FNT));
|
||||
_creditsFont = (_vm->gameFlags().lang == Common::ZH_TWN) ? Screen::FID_CHINESE_FNT : Screen::FID_8_FNT;
|
||||
_creditsFont2 = (_vm->gameFlags().lang == Common::ZH_TWN) ? Screen::FID_GOLDFONT_FNT : Screen::FID_8_FNT;
|
||||
|
||||
@@ -463,7 +463,9 @@ SeqPlayer_HOF::SeqPlayer_HOF(KyraEngine_v1 *vm, Screen_v2 *screen, OSystem *syst
|
||||
int8 ls = 0;
|
||||
Screen::FontId fid = Screen::FID_8_FNT;
|
||||
|
||||
if (_vm->gameFlags().lang == Common::JA_JPN) {
|
||||
if (_vm->gameFlags().lang == Common::KO_KOR) {
|
||||
fid = Screen::FID_KOREAN_FNT;
|
||||
} else if (_vm->gameFlags().lang == Common::JA_JPN) {
|
||||
fid = Screen::FID_SJIS_FNT;
|
||||
ls = 1;
|
||||
} else if (_vm->gameFlags().lang == Common::ZH_TWN) {
|
||||
@@ -504,7 +506,7 @@ SeqPlayer_HOF::~SeqPlayer_HOF() {
|
||||
delete _menu;
|
||||
|
||||
if (_vm->game() != GI_LOL)
|
||||
_screen->setFont(_vm->gameFlags().lang == Common::ZH_TWN ? Screen::FID_CHINESE_FNT : (_vm->gameFlags().lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : Screen::FID_8_FNT));
|
||||
_screen->setFont(_vm->gameFlags().lang == Common::ZH_TWN ? Screen::FID_CHINESE_FNT : (_vm->gameFlags().lang == Common::JA_JPN ? Screen::FID_SJIS_FNT : (_vm->gameFlags().lang == Common::KO_KOR ? Screen::FID_KOREAN_FNT : Screen::FID_8_FNT)));
|
||||
}
|
||||
|
||||
int SeqPlayer_HOF::play(SequenceID firstScene, SequenceID loopStartScene) {
|
||||
|
||||
@@ -86,6 +86,12 @@ char *TextDisplayer_HoF::preprocessString(const char *str) {
|
||||
if (_vm->gameFlags().lang == Common::ZH_TWN)
|
||||
return _talkBuffer;
|
||||
|
||||
// Korean fan translation: delegate to base class which already handles
|
||||
// 2-byte character width measurement with FID_KOREAN_FNT and has
|
||||
// correct maxTextWidth limits for Korean text.
|
||||
if (_vm->gameFlags().lang == Common::KO_KOR)
|
||||
return TextDisplayer::preprocessString(_talkBuffer);
|
||||
|
||||
char *p = _talkBuffer;
|
||||
while (*p) {
|
||||
if (*p == '\r')
|
||||
@@ -424,7 +430,7 @@ void KyraEngine_HoF::randomSceneChat() {
|
||||
|
||||
void KyraEngine_HoF::updateDlgBuffer() {
|
||||
static const char suffixTalkie[] = "EFG";
|
||||
static const char suffixTowns[] = "G J";
|
||||
static const char suffixTowns[] = "G J K";
|
||||
|
||||
if (_currentChapter == _npcTalkChpIndex && _mainCharacter.dlgIndex == _npcTalkDlgIndex)
|
||||
return;
|
||||
@@ -434,11 +440,18 @@ void KyraEngine_HoF::updateDlgBuffer() {
|
||||
|
||||
Common::String filename = Common::String::format("CH%.02d-S%.02d.DL", _currentChapter, _npcTalkDlgIndex);
|
||||
|
||||
const char *suffix = _flags.isTalkie ? suffixTalkie : suffixTowns;
|
||||
if (_flags.platform != Common::kPlatformDOS || _flags.isTalkie)
|
||||
filename += suffix[_lang];
|
||||
else
|
||||
filename += 'G';
|
||||
// Korean fan translation always uses .DLK regardless of talkie/platform.
|
||||
// suffixTalkie[] = "EFG" only covers _lang 0-2; _lang=5 (KO_KOR) would
|
||||
// be an out-of-bounds read, so we handle it explicitly before the lookup.
|
||||
if (_flags.lang == Common::KO_KOR) {
|
||||
filename += 'K';
|
||||
} else {
|
||||
const char *suffix = _flags.isTalkie ? suffixTalkie : suffixTowns;
|
||||
if (_flags.platform != Common::kPlatformDOS || _flags.isTalkie)
|
||||
filename += suffix[_lang];
|
||||
else
|
||||
filename += 'G';
|
||||
}
|
||||
|
||||
delete[] _dlgBuffer;
|
||||
_dlgBuffer = _res->fileData(filename.c_str(), nullptr);
|
||||
|
||||
Reference in New Issue
Block a user