mirror of
https://github.com/libretro/scummvm.git
synced 2026-11-07 04:26:05 -10:00
HAROLD: BCB: Load executable text and Japanese and Korean fonts
This commit is contained in:
1 parent
c459bdf1a4
commit
5474e8358a
5 files changed
+379
-3
No files matched your search
@@ -38,6 +38,36 @@ namespace BCB {
|
||||
static const int kCaptionTextNativeCharacterHeight = 18;
|
||||
static const int kOwnerDrawUiTextNativeCharacterHeight = 16;
|
||||
|
||||
static Graphics::Font *loadLocalizedCjkFont(Common::Language language, int size, Graphics::TTFSizeMode sizeMode) {
|
||||
if (language == Common::KO_KOR) {
|
||||
Graphics::Font *font = Graphics::loadTTFFontFromArchive("NotoSansKR-Regular.otf", size, sizeMode, 0, 0, Graphics::kTTFRenderModeNormal);
|
||||
if (font)
|
||||
return font;
|
||||
font = Graphics::loadTTFFontFromArchive("NotoSansJP-Regular.otf", size, sizeMode, 0, 0, Graphics::kTTFRenderModeNormal);
|
||||
if (font)
|
||||
return font;
|
||||
return Graphics::loadTTFFontFromArchive("VL-Gothic-Regular.ttf", size, sizeMode, 0, 0, Graphics::kTTFRenderModeNormal);
|
||||
}
|
||||
|
||||
Graphics::Font *font = Graphics::loadTTFFontFromArchive("VL-Gothic-Regular.ttf", size, sizeMode, 0, 0, Graphics::kTTFRenderModeNormal);
|
||||
if (font)
|
||||
return font;
|
||||
return Graphics::loadTTFFontFromArchive("NotoSansJP-Regular.otf", size, sizeMode, 0, 0, Graphics::kTTFRenderModeNormal);
|
||||
}
|
||||
|
||||
static Graphics::Font *loadLocalizedOwnerDrawFont(Common::Language language) {
|
||||
if (language == Common::KO_KOR) {
|
||||
Graphics::Font *font = Graphics::loadTTFFontFromArchive("NotoSansKR-Regular.otf", kOwnerDrawUiTextNativeCharacterHeight, Graphics::kTTFSizeModeCharacter, 0, 0, Graphics::kTTFRenderModeNormal);
|
||||
if (font)
|
||||
return font;
|
||||
}
|
||||
|
||||
Graphics::Font *font = Graphics::loadTTFFontFromArchive("NotoSansJP-Regular.otf", kOwnerDrawUiTextNativeCharacterHeight, Graphics::kTTFSizeModeCharacter, 0, 0, Graphics::kTTFRenderModeNormal);
|
||||
if (font)
|
||||
return font;
|
||||
return Graphics::loadTTFFontFromArchive("VL-Gothic-Regular.ttf", kOwnerDrawUiTextNativeCharacterHeight, Graphics::kTTFSizeModeCharacter, 0, 0, Graphics::kTTFRenderModeNormal);
|
||||
}
|
||||
|
||||
BCBEngine::BCBEngine(OSystem *syst, const HaroldGameDescription *desc)
|
||||
: Engine(syst), _gameDescription(desc) {
|
||||
}
|
||||
@@ -51,6 +81,10 @@ BCBEngine::~BCBEngine() {
|
||||
_chipBitmap->free();
|
||||
delete _chipBitmap;
|
||||
}
|
||||
delete _sjisFont;
|
||||
delete _captionTextFont;
|
||||
delete _gameOverTextFont;
|
||||
delete _ownerDrawUiTextFont;
|
||||
}
|
||||
|
||||
Common::Error BCBEngine::run() {
|
||||
@@ -65,10 +99,19 @@ Common::Error BCBEngine::run() {
|
||||
CursorMan.showMouse(true);
|
||||
g_system->updateScreen();
|
||||
_lastCursorRefreshMillis = g_system->getMillis();
|
||||
_sjisFont = Graphics::FontSJIS::createFont();
|
||||
_captionTextFont = loadLocalizedCjkFont(_gameDescription->desc.language, kCaptionTextNativeCharacterHeight, Graphics::kTTFSizeModeCharacter);
|
||||
_gameOverTextFont = loadLocalizedCjkFont(_gameDescription->desc.language, 20, Graphics::kTTFSizeModeCell);
|
||||
_ownerDrawUiTextFont = loadLocalizedOwnerDrawFont(_gameDescription->desc.language);
|
||||
if (!_sjisFont && !_captionTextFont && !_gameOverTextFont && !_ownerDrawUiTextFont)
|
||||
warning("BCBEngine::run(): No CJK font available; captions and choice text will not render");
|
||||
|
||||
if (!loadScriptData())
|
||||
return Common::kReadingFailed;
|
||||
|
||||
if (!loadExecutableDataTables())
|
||||
return Common::kReadingFailed;
|
||||
|
||||
resetGameState();
|
||||
|
||||
mainLoop();
|
||||
|
||||
@@ -67,6 +67,7 @@ private:
|
||||
bool switchActiveMediaDisc(int discNumber);
|
||||
void resetGameState();
|
||||
bool loadScriptData();
|
||||
bool loadExecutableDataTables();
|
||||
bool loadByteFile(const char *fileName, Common::Array<byte> &data) const;
|
||||
void executeCurrentScriptSequence();
|
||||
uint32 executeScriptExpression(uint32 &scriptPc, int &branchTokenSeen);
|
||||
@@ -93,6 +94,7 @@ private:
|
||||
void serviceCursorRefresh(bool screenUpdated = false);
|
||||
void delayWithEventPolling(uint32 millis);
|
||||
void delayWithEventPolling(uint32 millis, bool handleClicks);
|
||||
Common::U32String decodeExecutableTextForDisplay(const Common::String &text) const;
|
||||
void mainLoop();
|
||||
|
||||
const HaroldGameDescription *_gameDescription;
|
||||
@@ -103,6 +105,25 @@ private:
|
||||
Common::Array<byte> _locationTimingData;
|
||||
Common::Array<byte> _allBinData;
|
||||
Common::Array<byte> _timedCaptionRecordData;
|
||||
Common::Array<Common::Array<Common::U32String> > _sceneChoiceText;
|
||||
Common::CodePage _executableTextEncoding = Common::kWindows932;
|
||||
Common::String _sceneChoiceMoveTextSjis;
|
||||
Common::U32String _sceneChoiceMoveText;
|
||||
Common::Array<Common::String> _mapButtonTextSjis;
|
||||
Common::Array<Common::U32String> _mapButtonText;
|
||||
Common::Array<Common::U32String> _gameOverText;
|
||||
Common::U32String _titleExitConfirmText;
|
||||
Common::String _saveSlotOccupiedTextFormatSjis;
|
||||
Common::String _saveSlotEmptyTextFormatSjis;
|
||||
Common::String _loadSlotConfirmTextFormatSjis;
|
||||
Common::String _saveSlotOverwriteConfirmTextFormatSjis;
|
||||
Common::String _saveSlotEmptyConfirmTextFormatSjis;
|
||||
Common::String _noSaveDataMessageSjis;
|
||||
Common::String _returnToTitleConfirmTextSjis;
|
||||
Graphics::FontSJIS *_sjisFont = nullptr;
|
||||
const Graphics::Font *_captionTextFont = nullptr;
|
||||
const Graphics::Font *_gameOverTextFont = nullptr;
|
||||
const Graphics::Font *_ownerDrawUiTextFont = nullptr;
|
||||
Graphics::Surface *_uiBitmap = nullptr;
|
||||
Common::String _uiBitmapName;
|
||||
Graphics::Surface *_chipBitmap = nullptr;
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/algorithm.h"
|
||||
#include "common/file.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "harold/bcb/bcb.h"
|
||||
|
||||
namespace BCB {
|
||||
|
||||
namespace {
|
||||
|
||||
struct PeSection {
|
||||
uint32 virtualAddress;
|
||||
uint32 size;
|
||||
uint32 rawOffset;
|
||||
uint32 rawSize;
|
||||
};
|
||||
|
||||
struct ExecutableDataProfile {
|
||||
Common::Language language;
|
||||
const char *executableName;
|
||||
Common::CodePage encoding;
|
||||
uint32 sceneChoiceTextTableVa;
|
||||
uint32 sceneChoiceTextTableEndVa;
|
||||
uint32 mapButtonTextPointerTableVa;
|
||||
uint32 gameOverTextPointerTableVa;
|
||||
uint32 saveSlotOccupiedTextFormatVa;
|
||||
uint32 saveSlotEmptyTextFormatVa;
|
||||
uint32 noSaveDataMessageVa;
|
||||
uint32 sceneChoiceMoveTextVa;
|
||||
uint32 loadSlotConfirmTextFormatVa;
|
||||
uint32 saveSlotOverwriteConfirmTextFormatVa;
|
||||
uint32 saveSlotEmptyConfirmTextFormatVa;
|
||||
uint32 returnToTitleConfirmTextVa;
|
||||
uint32 titleExitConfirmTextVa;
|
||||
};
|
||||
|
||||
static const ExecutableDataProfile kJapaneseExecutableDataProfile = {
|
||||
Common::JA_JPN,
|
||||
"bcb.exe",
|
||||
Common::kWindows932,
|
||||
0x00412000,
|
||||
0x00412d20,
|
||||
0x00412cd4,
|
||||
0x00413648,
|
||||
0x00416394,
|
||||
0x004163b4,
|
||||
0x004163bb,
|
||||
0x004163d0,
|
||||
0x004163d8,
|
||||
0x00416418,
|
||||
0x00416444,
|
||||
0x00416460,
|
||||
0x00416474};
|
||||
|
||||
static const ExecutableDataProfile kKoreanExecutableDataProfile = {
|
||||
Common::KO_KOR,
|
||||
"bcb.exe",
|
||||
Common::kWindows949,
|
||||
0x00421878,
|
||||
0x00422598,
|
||||
0x0041e404,
|
||||
0x0041f2a8,
|
||||
0x00423a10,
|
||||
0x004239f4,
|
||||
0x00423980,
|
||||
0x004239dc,
|
||||
0x004239a0,
|
||||
0x0042394c,
|
||||
0x00423934,
|
||||
0x004238fc,
|
||||
0x004238cc};
|
||||
|
||||
static const ExecutableDataProfile *getExecutableDataProfile(Common::Language language) {
|
||||
if (language == Common::KO_KOR)
|
||||
return &kKoreanExecutableDataProfile;
|
||||
|
||||
return &kJapaneseExecutableDataProfile;
|
||||
}
|
||||
|
||||
static const uint kSceneChoiceTextSlotsPerSet = 8;
|
||||
static const uint kMapButtonTextCount = 19;
|
||||
static const uint kGameOverTextCount = 10;
|
||||
|
||||
static bool readExecutableBytes(Common::SeekableReadStream *stream, Common::Array<byte> &data) {
|
||||
Common::ScopedPtr<Common::SeekableReadStream> executableStream(stream);
|
||||
if (!executableStream)
|
||||
return false;
|
||||
|
||||
const int32 size = executableStream->size();
|
||||
if (size <= 0)
|
||||
return false;
|
||||
|
||||
data.resize(size);
|
||||
return executableStream->read(&data[0], size) == (uint32)size;
|
||||
}
|
||||
|
||||
static uint16 readUint16LE(const Common::Array<byte> &data, uint offset) {
|
||||
return data[offset] | (data[offset + 1] << 8);
|
||||
}
|
||||
|
||||
static uint32 readUint32LE(const Common::Array<byte> &data, uint offset) {
|
||||
return data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24);
|
||||
}
|
||||
|
||||
static bool virtualAddressToFileOffset(uint32 virtualAddress, uint32 imageBase, const Common::Array<PeSection> §ions, uint &fileOffset) {
|
||||
const uint32 relativeAddress = virtualAddress - imageBase;
|
||||
for (uint i = 0; i < sections.size(); ++i) {
|
||||
const PeSection §ion = sections[i];
|
||||
if (relativeAddress < section.virtualAddress || relativeAddress >= section.virtualAddress + section.size)
|
||||
continue;
|
||||
|
||||
fileOffset = section.rawOffset + relativeAddress - section.virtualAddress;
|
||||
return fileOffset < section.rawOffset + section.rawSize;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool readExecutableString(const Common::Array<byte> &executableData, uint32 virtualAddress, uint32 imageBase, const Common::Array<PeSection> §ions, Common::String &sjisText) {
|
||||
uint textOffset = 0;
|
||||
if (!virtualAddressToFileOffset(virtualAddress, imageBase, sections, textOffset))
|
||||
return false;
|
||||
|
||||
uint textEnd = textOffset;
|
||||
while (textEnd < executableData.size() && executableData[textEnd] != 0)
|
||||
++textEnd;
|
||||
|
||||
if (textEnd == textOffset)
|
||||
return false;
|
||||
|
||||
sjisText = Common::String((const char *)&executableData[textOffset], textEnd - textOffset);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readExecutableString(const Common::Array<byte> &executableData, uint32 virtualAddress, uint32 imageBase, const Common::Array<PeSection> §ions, Common::CodePage encoding, Common::U32String &text) {
|
||||
Common::String sjisText;
|
||||
if (!readExecutableString(executableData, virtualAddress, imageBase, sections, sjisText))
|
||||
return false;
|
||||
|
||||
text = Common::U32String(sjisText.c_str(), sjisText.size(), encoding);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
bool BCBEngine::loadExecutableDataTables() {
|
||||
const ExecutableDataProfile &profile = *getExecutableDataProfile(_gameDescription->desc.language);
|
||||
_executableTextEncoding = profile.encoding;
|
||||
Common::Array<byte> executableData;
|
||||
if (!readExecutableBytes(createProgramReadStream(profile.executableName), executableData))
|
||||
return false;
|
||||
|
||||
if (executableData.size() < 0x40 || executableData[0] != 'M' || executableData[1] != 'Z')
|
||||
return false;
|
||||
|
||||
const uint32 peHeaderOffset = readUint32LE(executableData, 0x3c);
|
||||
if (peHeaderOffset + 0x18 >= executableData.size() || executableData[peHeaderOffset] != 'P' || executableData[peHeaderOffset + 1] != 'E')
|
||||
return false;
|
||||
|
||||
const uint sectionCount = readUint16LE(executableData, peHeaderOffset + 6);
|
||||
const uint optionalHeaderSize = readUint16LE(executableData, peHeaderOffset + 20);
|
||||
const uint optionalHeaderOffset = peHeaderOffset + 24;
|
||||
const uint sectionTableOffset = optionalHeaderOffset + optionalHeaderSize;
|
||||
if (sectionTableOffset + sectionCount * 40 > executableData.size())
|
||||
return false;
|
||||
|
||||
const uint32 imageBase = readUint32LE(executableData, optionalHeaderOffset + 28);
|
||||
Common::Array<PeSection> sections;
|
||||
for (uint i = 0; i < sectionCount; ++i) {
|
||||
const uint sectionOffset = sectionTableOffset + i * 40;
|
||||
PeSection section;
|
||||
const uint32 virtualSize = readUint32LE(executableData, sectionOffset + 8);
|
||||
section.virtualAddress = readUint32LE(executableData, sectionOffset + 12);
|
||||
section.rawSize = readUint32LE(executableData, sectionOffset + 16);
|
||||
section.rawOffset = readUint32LE(executableData, sectionOffset + 20);
|
||||
section.size = MAX<uint32>(virtualSize, section.rawSize);
|
||||
sections.push_back(section);
|
||||
}
|
||||
|
||||
uint tableOffset = 0;
|
||||
uint tableEndOffset = 0;
|
||||
if (!virtualAddressToFileOffset(profile.sceneChoiceTextTableVa, imageBase, sections, tableOffset))
|
||||
return false;
|
||||
if (!virtualAddressToFileOffset(profile.sceneChoiceTextTableEndVa, imageBase, sections, tableEndOffset))
|
||||
return false;
|
||||
|
||||
const uint setCount = (tableEndOffset - tableOffset) / (kSceneChoiceTextSlotsPerSet * 4);
|
||||
_sceneChoiceText.clear();
|
||||
_sceneChoiceText.resize(setCount + 1);
|
||||
|
||||
for (uint setId = 1; setId <= setCount; ++setId) {
|
||||
for (uint slot = 0; slot < kSceneChoiceTextSlotsPerSet; ++slot) {
|
||||
const uint pointerOffset = tableOffset + ((setId - 1) * kSceneChoiceTextSlotsPerSet + slot) * 4;
|
||||
const uint32 textVa = readUint32LE(executableData, pointerOffset);
|
||||
uint textOffset = 0;
|
||||
if (textVa == 0 || !virtualAddressToFileOffset(textVa, imageBase, sections, textOffset))
|
||||
break;
|
||||
|
||||
uint textEnd = textOffset;
|
||||
while (textEnd < executableData.size() && executableData[textEnd] != 0)
|
||||
++textEnd;
|
||||
if (textEnd == textOffset)
|
||||
break;
|
||||
|
||||
const char *textStart = (const char *)&executableData[textOffset];
|
||||
_sceneChoiceText[setId].push_back(Common::U32String(textStart, textEnd - textOffset, profile.encoding));
|
||||
}
|
||||
}
|
||||
|
||||
if (!readExecutableString(executableData, profile.sceneChoiceMoveTextVa, imageBase, sections, _sceneChoiceMoveTextSjis))
|
||||
return false;
|
||||
_sceneChoiceMoveText = Common::U32String(_sceneChoiceMoveTextSjis.c_str(), _sceneChoiceMoveTextSjis.size(), profile.encoding);
|
||||
|
||||
uint mapTextTableOffset = 0;
|
||||
if (!virtualAddressToFileOffset(profile.mapButtonTextPointerTableVa, imageBase, sections, mapTextTableOffset))
|
||||
return false;
|
||||
|
||||
_mapButtonTextSjis.clear();
|
||||
_mapButtonText.clear();
|
||||
_mapButtonTextSjis.resize(kMapButtonTextCount);
|
||||
_mapButtonText.resize(kMapButtonTextCount);
|
||||
for (uint i = 0; i < kMapButtonTextCount; ++i) {
|
||||
const uint pointerOffset = mapTextTableOffset + i * 4;
|
||||
const uint32 textVa = readUint32LE(executableData, pointerOffset);
|
||||
uint textOffset = 0;
|
||||
if (textVa == 0 || !virtualAddressToFileOffset(textVa, imageBase, sections, textOffset))
|
||||
continue;
|
||||
|
||||
uint textEnd = textOffset;
|
||||
while (textEnd < executableData.size() && executableData[textEnd] != 0)
|
||||
++textEnd;
|
||||
|
||||
if (textEnd == textOffset)
|
||||
continue;
|
||||
|
||||
const char *textStart = (const char *)&executableData[textOffset];
|
||||
_mapButtonTextSjis[i] = Common::String(textStart, textEnd - textOffset);
|
||||
_mapButtonText[i] = Common::U32String(textStart, textEnd - textOffset, profile.encoding);
|
||||
}
|
||||
|
||||
uint gameOverTextTableOffset = 0;
|
||||
if (!virtualAddressToFileOffset(profile.gameOverTextPointerTableVa, imageBase, sections, gameOverTextTableOffset))
|
||||
return false;
|
||||
|
||||
_gameOverText.clear();
|
||||
_gameOverText.resize(kGameOverTextCount);
|
||||
for (uint i = 0; i < kGameOverTextCount; ++i) {
|
||||
const uint pointerOffset = gameOverTextTableOffset + i * 4;
|
||||
const uint32 textVa = readUint32LE(executableData, pointerOffset);
|
||||
uint textOffset = 0;
|
||||
if (textVa == 0 || !virtualAddressToFileOffset(textVa, imageBase, sections, textOffset))
|
||||
continue;
|
||||
|
||||
uint textEnd = textOffset;
|
||||
while (textEnd < executableData.size() && executableData[textEnd] != 0)
|
||||
++textEnd;
|
||||
|
||||
if (textEnd == textOffset)
|
||||
continue;
|
||||
|
||||
const char *textStart = (const char *)&executableData[textOffset];
|
||||
_gameOverText[i] = Common::U32String(textStart, textEnd - textOffset, profile.encoding);
|
||||
}
|
||||
|
||||
if (!readExecutableString(executableData, profile.titleExitConfirmTextVa, imageBase, sections, profile.encoding, _titleExitConfirmText))
|
||||
return false;
|
||||
if (!readExecutableString(executableData, profile.saveSlotOccupiedTextFormatVa, imageBase, sections, _saveSlotOccupiedTextFormatSjis))
|
||||
return false;
|
||||
if (!readExecutableString(executableData, profile.saveSlotEmptyTextFormatVa, imageBase, sections, _saveSlotEmptyTextFormatSjis))
|
||||
return false;
|
||||
if (!readExecutableString(executableData, profile.loadSlotConfirmTextFormatVa, imageBase, sections, _loadSlotConfirmTextFormatSjis))
|
||||
return false;
|
||||
if (!readExecutableString(executableData, profile.saveSlotOverwriteConfirmTextFormatVa, imageBase, sections, _saveSlotOverwriteConfirmTextFormatSjis))
|
||||
return false;
|
||||
if (!readExecutableString(executableData, profile.saveSlotEmptyConfirmTextFormatVa, imageBase, sections, _saveSlotEmptyConfirmTextFormatSjis))
|
||||
return false;
|
||||
if (!readExecutableString(executableData, profile.noSaveDataMessageVa, imageBase, sections, _noSaveDataMessageSjis))
|
||||
return false;
|
||||
if (!readExecutableString(executableData, profile.returnToTitleConfirmTextVa, imageBase, sections, _returnToTitleConfirmTextSjis))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Common::U32String BCBEngine::decodeExecutableTextForDisplay(const Common::String &text) const {
|
||||
return Common::U32String(text.c_str(), text.size(), _executableTextEncoding);
|
||||
}
|
||||
|
||||
} // End of namespace BCB
|
||||
@@ -6,6 +6,7 @@ MODULE_OBJS := \
|
||||
bcb/scene.o \
|
||||
bcb/input.o \
|
||||
bcb/script.o \
|
||||
bcb/executable_data.o \
|
||||
bcb/static_data.o \
|
||||
metaengine.o
|
||||
|
||||
|
||||
+3
-3
@@ -19,13 +19,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
// The code in this file is currently used in KYRA, SCI, SCUMM, SAGA,
|
||||
// and AGOS. If none of those are enabled, we will skip compiling it.
|
||||
// The code in this file is currently used in BCB, KYRA, SCI, SCUMM,
|
||||
// SAGA, and AGOS. If none of those are enabled, we will skip compiling it.
|
||||
// We also enable this code for ScummVM builds including support
|
||||
// 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(ENABLE_SAGA) || defined(ENABLE_AGOS) || defined(DYNAMIC_MODULES))
|
||||
#if !(defined(ENABLE_BCB) || defined(ENABLE_KYRA) || defined(ENABLE_SCI) || defined(ENABLE_SCUMM) || defined(ENABLE_SAGA) || defined(ENABLE_AGOS) || defined(DYNAMIC_MODULES))
|
||||
|
||||
// If neither of the above mentioned is enabled, do not include the SJIS code.
|
||||
|
||||
|
||||
Reference in new issue
Block a user