mirror of
https://github.com/libretro/scummvm.git
synced 2026-11-07 12:38:04 -10:00
312 lines
11 KiB
C++
312 lines
11 KiB
C++
/* 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
|