aboutsummaryrefslogtreecommitdiff
path: root/engines/cryomni3d/versailles/documentation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/cryomni3d/versailles/documentation.cpp')
-rw-r--r--engines/cryomni3d/versailles/documentation.cpp2070
1 files changed, 2070 insertions, 0 deletions
diff --git a/engines/cryomni3d/versailles/documentation.cpp b/engines/cryomni3d/versailles/documentation.cpp
new file mode 100644
index 0000000000..bbc924290c
--- /dev/null
+++ b/engines/cryomni3d/versailles/documentation.cpp
@@ -0,0 +1,2070 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/file.h"
+#include "common/system.h"
+#include "graphics/managed_surface.h"
+#include "image/bmp.h"
+
+#include "cryomni3d/font_manager.h"
+#include "cryomni3d/mouse_boxes.h"
+#include "cryomni3d/sprites.h"
+
+#include "cryomni3d/versailles/documentation.h"
+#include "cryomni3d/versailles/engine.h"
+
+namespace CryOmni3D {
+namespace Versailles {
+
+const char *Versailles_Documentation::kAllDocsFile = "tous_doc.txt";
+const char *Versailles_Documentation::kLinksDocsFile = "lien_doc.txt";
+const Versailles_Documentation::TimelineEntry Versailles_Documentation::kTimelineEntries[] = {
+ { "1638", 340, 15 },
+ { "1643", 470, 30 },
+ { "1648", 380, 45 },
+ { "1649", 500, 60 },
+ { "1650", 420, 75 },
+ { "1651", 520, 90 },
+ { "1652", 450, 105 },
+ { "1654", 540, 120 },
+ { "1658", 470, 135 },
+ { "1659", 550, 150 },
+ { "1660", 480, 165 },
+ { "1661", 560, 180 },
+ { "1662", 490, 195 },
+ { "1663", 560, 210 },
+ { "1664", 490, 225 },
+ { "1665", 560, 240 },
+ { "1666", 490, 255 },
+ { "1667", 560, 270 },
+ { "1668", 490, 285 },
+ { "1670", 550, 300 },
+ { "1671", 480, 315 },
+ { "1672", 540, 330 },
+ { "1673", 470, 345 },
+ { "1674", 530, 360 },
+ { "1675", 460, 375 },
+ { "1678", 510, 390 },
+ { "1680", 430, 405 },
+ { "1681", 490, 420 },
+ { "1682", 400, 435 },
+ { "1683", 450, 450 },
+ { "1684", 156, 444 },
+ { "1685", 81, 439 },
+ { "1686", 140, 422 },
+ { "1687", 73, 413 },
+ { "1689", 128, 401 },
+ { "1697", 62, 389 },
+ { "1700", 121, 378 },
+ { "1701", 62, 366 },
+ { "1702", 121, 355 },
+ { "1709", 62, 344 },
+ { "1710", 121, 333 },
+ { "1712", 67, 322 },
+ { "1715", 128, 311 },
+};
+
+void Versailles_Documentation::init(const Sprites *sprites, FontManager *fontManager,
+ const Common::StringArray *messages, CryOmni3DEngine *engine) {
+ _sprites = sprites;
+ _fontManager = fontManager;
+ _messages = messages;
+ _engine = engine;
+
+ // Build list of records
+ Common::File allDocsFile;
+
+ if (!allDocsFile.open(kAllDocsFile)) {
+ error("Can't open %s", kAllDocsFile);
+ }
+
+ unsigned int allDocsSize = allDocsFile.size();
+ char *allDocs = new char[allDocsSize + 1];
+ char *end = allDocs + allDocsSize;
+ allDocsFile.read(allDocs, allDocsSize);
+ allDocs[allDocsSize] = '\0';
+ allDocsFile.close();
+
+ const char *patterns[] = { "FICH=", nullptr };
+ RecordInfo record;
+
+ char *currentPos = allDocs;
+ char *lastRecordName;
+ bool first = true;
+
+ while (true) {
+ currentPos = getDocPartAddress(currentPos, end, patterns);
+ if (!currentPos) {
+ break;
+ }
+ currentPos -= 5;
+ if (first) {
+ record.position = currentPos - allDocs;
+ record.id = 0;
+
+ lastRecordName = currentPos + 5;
+
+ first = false;
+ } else {
+ record.size = (currentPos - allDocs) - record.position;
+ //debug("Found record %s", lastRecordName);
+ _records[lastRecordName] = record;
+ _recordsOrdered.push_back(lastRecordName);
+
+ record.id++;
+ record.position = currentPos - allDocs;
+
+ lastRecordName = currentPos + 5;
+ }
+ // Next line
+ currentPos += strlen(currentPos) + 1;
+ }
+ record.size = allDocsSize - record.position;
+ _records[lastRecordName] = record;
+ _recordsOrdered.push_back(lastRecordName);
+
+ delete[] allDocs;
+}
+
+void Versailles_Documentation::handleDocArea() {
+ g_system->showMouse(false);
+
+ // Load all links lazily and free them at the end to not waste memory
+ // Maybe it's not really useful
+ getLinks("ALL00", _allLinks);
+
+ bool end = false;
+ while (!end) {
+ Common::String selectedRecord = docAreaHandleSummary();
+ if (selectedRecord == "") {
+ end = true;
+ } else if (selectedRecord == "VT00") {
+ selectedRecord = docAreaHandleTimeline();
+ if (selectedRecord != "") {
+ if (docAreaHandleRecords(selectedRecord) == 2) {
+ end = true;
+ }
+ }
+ } else {
+ if (docAreaHandleRecords(selectedRecord) == 2) {
+ end = true;
+ }
+ }
+ }
+
+ _allLinks.clear();
+
+ g_system->showMouse(true);
+}
+
+void Versailles_Documentation::handleDocInGame(const Common::String &record) {
+ _visitTrace.clear();
+ _currentRecord = record;
+
+ Graphics::ManagedSurface docSurface;
+ Common::String nextRecord;
+ MouseBoxes boxes(3);
+
+ g_system->showMouse(false);
+ bool end = false;
+ while (!end) {
+ inGamePrepareRecord(docSurface, boxes);
+ unsigned int action = inGameHandleRecord(docSurface, boxes, nextRecord);
+ switch (action) {
+ case 0:
+ // Back
+ if (!_visitTrace.empty()) {
+ _currentRecord = _visitTrace.back();
+ _visitTrace.pop_back();
+ break;
+ }
+ // No previous record, like a quit
+ // fall through
+ case 1:
+ // Quit
+ end = true;
+ break;
+ case 2:
+ // Follow hyperlink keeping trace
+ _visitTrace.push_back(_currentRecord);
+ _currentRecord = nextRecord;
+ break;
+ default:
+ error("Invalid case %d when displaying doc record", action);
+ }
+ }
+ g_system->showMouse(true);
+}
+
+Common::String Versailles_Documentation::docAreaHandleSummary() {
+ struct Category {
+ const char *record;
+ const char *bmp;
+ Common::Point imgPos;
+ Common::Rect linesPos;
+ const Common::String *title;
+ Graphics::Surface highlightedImg;
+
+ Category(const char *record_, const char *bmp_, const Common::Point &imgPos_,
+ const Common::Rect &linesPos_, const Common::String *title_) :
+ record(record_), bmp(bmp_), imgPos(imgPos_), linesPos(linesPos_), title(title_) { }
+ } categories[8] = {
+ Category(
+ "VA00",
+ "VA.bmp",
+ Common::Point(142, 402),
+ Common::Rect(174, 372, 284, 372),
+ &(*_messages)[68]),
+ Category(
+ "VR00",
+ "VR.bmp",
+ Common::Point(82, 187),
+ Common::Rect(89, 187, 217, 212),
+ &(*_messages)[69]),
+ Category(
+ "VC00",
+ "VC.bmp",
+ Common::Point(176, 105),
+ Common::Rect(177, 107, 292, 130),
+ &(*_messages)[70]),
+ Category(
+ "VT00",
+ "VH.bmp", //sic
+ Common::Point(283, 467),
+ Common::Rect(311, 451, 466, 451),
+ &(*_messages)[73]),
+ Category(
+ "VV00",
+ "VV.bmp",
+ Common::Point(68, 305),
+ Common::Rect(94, 292, 300, 292),
+ &(*_messages)[71]),
+ Category(
+ "VS00",
+ "VS.bmp",
+ Common::Point(321, 70),
+ Common::Rect(322, 71, 540, 94),
+ &(*_messages)[72]),
+ Category(
+ nullptr,
+ nullptr,
+ Common::Point(256, 212),
+ Common::Rect(),
+ nullptr),
+ Category(
+ nullptr,
+ nullptr,
+ Common::Point(600, 450),
+ Common::Rect(),
+ nullptr)
+ };
+ Image::BitmapDecoder bmpDecoder;
+ Common::File file;
+
+ for (unsigned int i = 0; i < ARRAYSIZE(categories); i++) {
+ if (!categories[i].bmp) {
+ // No BMP to load
+ continue;
+ }
+ if (!file.open(categories[i].bmp)) {
+ error("Failed to open BMP file: %s", categories[i].bmp);
+ }
+ if (!bmpDecoder.loadStream(file)) {
+ error("Failed to load BMP file: %s", categories[i].bmp);
+ }
+ categories[i].highlightedImg.copyFrom(*bmpDecoder.getSurface());
+ bmpDecoder.destroy();
+ file.close();
+ }
+
+ Image::ImageDecoder *imageDecoder = _engine->loadHLZ("SOM1.HLZ");
+ if (!imageDecoder) {
+ return "";
+ }
+ const Graphics::Surface *bgFrame = imageDecoder->getSurface();
+
+ Graphics::ManagedSurface docSurface;
+ docSurface.create(bgFrame->w, bgFrame->h, bgFrame->format);
+ docSurface.blitFrom(*bgFrame);
+
+ _fontManager->setCurrentFont(0);
+ _fontManager->setTransparentBackground(true);
+ _fontManager->setLineHeight(14);
+ _fontManager->setSpaceWidth(0);
+ _fontManager->setCharSpacing(1);
+ _fontManager->setSurface(&docSurface);
+
+ MouseBoxes boxes(8);
+ boxes.setupBox(0, 104, 335, 177, 408);
+ boxes.setupBox(1, 46, 122, 119, 195);
+ boxes.setupBox(2, 140, 40, 213, 113);
+ boxes.setupBox(3, 247, 402, 320, 475);
+ boxes.setupBox(4, 32, 240, 105, 313);
+ boxes.setupBox(5, 285, 5, 358, 78);
+ // No box for 6
+ boxes.setupBox(7, 0, 480 - _sprites->getCursor(225).getHeight(), 640, 480);
+
+ _engine->setupPalette(imageDecoder->getPalette(), imageDecoder->getPaletteStartIndex(),
+ imageDecoder->getPaletteColorCount());
+
+ _engine->setCursor(181);
+ g_system->showMouse(true);
+
+ bool redraw = true;
+ unsigned int hoveredBox = -1;
+ unsigned int selectedBox = -1;
+
+ while (selectedBox == -1u) {
+ if (redraw) {
+ // Draw without worrying of already modified areas, that's handled when recomputing hoveredBox
+ for (unsigned int i = 0; i < ARRAYSIZE(categories); i++) {
+ unsigned int foreColor = 243;
+ if (i == hoveredBox) {
+ foreColor = 241;
+ if (categories[hoveredBox].highlightedImg.getPixels() != nullptr) {
+ docSurface.transBlitFrom(categories[i].highlightedImg, categories[i].imgPos - Common::Point(36,
+ 65));
+ }
+ }
+ _fontManager->setForeColor(foreColor);
+ if (categories[i].title) {
+ unsigned int x = categories[i].linesPos.right - _fontManager->getStrWidth(*categories[i].title);
+ unsigned int y = categories[i].linesPos.bottom - _fontManager->getFontMaxHeight() - 5;
+ _fontManager->displayStr(x, y, *categories[i].title);
+
+ // Draw line to text
+ docSurface.vLine(categories[i].linesPos.left, categories[i].linesPos.top,
+ categories[i].linesPos.bottom, foreColor);
+ docSurface.hLine(categories[i].linesPos.left, categories[i].linesPos.bottom,
+ categories[i].linesPos.right - 1, foreColor); // minus 1 because hLine draws inclusive
+ }
+ }
+ docSurface.transBlitFrom(_sprites->getSurface(225), boxes.getBoxOrigin(7),
+ _sprites->getKeyColor(225));
+
+ g_system->copyRectToScreen(docSurface.getPixels(), docSurface.pitch, 0, 0, docSurface.w,
+ docSurface.h);
+
+ redraw = false;
+ }
+ g_system->updateScreen();
+
+ if (_engine->pollEvents()) {
+ if (!_engine->getCurrentMouseButton()) {
+ // Don't change highlighted icon when clicking
+ Common::Point mouse = _engine->getMousePos();
+ bool foundBox = false;
+ for (unsigned int i = 0; i < ARRAYSIZE(categories); i++) {
+ if (boxes.hitTest(i, mouse)) {
+ foundBox = true;
+ if (i != hoveredBox) {
+ hoveredBox = i;
+ redraw = true;
+ }
+ }
+ }
+ if (!foundBox && hoveredBox != -1u) {
+ if (categories[hoveredBox].highlightedImg.getPixels() != nullptr) {
+ // Restore original icon
+ const Common::Point &imgPos = categories[hoveredBox].imgPos;
+ docSurface.blitFrom(*bgFrame, Common::Rect(
+ imgPos.x - 36, imgPos.y - 65, imgPos.x + 37, imgPos.y + 8),
+ Common::Point(imgPos.x - 36, imgPos.y - 65));
+ }
+ hoveredBox = -1;
+ redraw = true;
+ }
+ }
+ if (_engine->getDragStatus() == kDragStatus_Finished) {
+ if (hoveredBox != -1u) {
+ selectedBox = hoveredBox;
+ }
+ }
+ if (_engine->checkKeysPressed(1, Common::KEYCODE_ESCAPE)) {
+ selectedBox = 7;
+ }
+ }
+ if (g_engine->shouldQuit()) {
+ selectedBox = 7;
+ }
+ }
+
+ g_system->showMouse(false);
+
+ delete imageDecoder;
+
+ if (selectedBox == 7) {
+ return "";
+ } else {
+ return categories[selectedBox].record;
+ }
+}
+
+Common::String Versailles_Documentation::docAreaHandleTimeline() {
+ Image::ImageDecoder *imageDecoder = _engine->loadHLZ("chrono1.HLZ");
+ if (!imageDecoder) {
+ return "";
+ }
+ const Graphics::Surface *bgFrame = imageDecoder->getSurface();
+
+ Graphics::ManagedSurface docSurface;
+ docSurface.create(bgFrame->w, bgFrame->h, bgFrame->format);
+ docSurface.blitFrom(*bgFrame);
+
+ _fontManager->setCurrentFont(1);
+ _fontManager->setTransparentBackground(true);
+ _fontManager->setLineHeight(14);
+ _fontManager->setSpaceWidth(0);
+ _fontManager->setCharSpacing(1);
+ _fontManager->setSurface(&docSurface);
+
+ _engine->setupPalette(imageDecoder->getPalette(), imageDecoder->getPaletteStartIndex(),
+ imageDecoder->getPaletteColorCount());
+
+ _fontManager->displayStr(78, 10, (*_messages)[73]);
+ docSurface.hLine(0, 39, 171, 241); // minus 1 because hLine draws inclusive
+
+ _fontManager->setCurrentFont(0);
+
+ MouseBoxes boxes(ARRAYSIZE(kTimelineEntries) + 1);
+ for (unsigned int box_id = 0; box_id < ARRAYSIZE(kTimelineEntries); box_id++) {
+ boxes.setupBox(box_id, kTimelineEntries[box_id].x, kTimelineEntries[box_id].y,
+ kTimelineEntries[box_id].x + 30, kTimelineEntries[box_id].y + 20);
+ }
+ const unsigned int leaveBoxId = ARRAYSIZE(kTimelineEntries);
+ boxes.setupBox(leaveBoxId, 639 - _sprites->getCursor(105).getWidth(),
+ 479 - _sprites->getCursor(105).getHeight(), 640, 480);
+
+ _engine->setCursor(181);
+ g_system->showMouse(true);
+
+ bool redraw = true;
+ unsigned int hoveredBox = -1;
+ unsigned int selectedBox = -1;
+
+ while (selectedBox == -1u) {
+ if (redraw) {
+ // Draw without worrying of already modified areas, that's handled when recomputing hoveredBox
+ for (unsigned int i = 0; i < ARRAYSIZE(kTimelineEntries); i++) {
+ _fontManager->setForeColor(i == hoveredBox ? 241 : 243);
+ _fontManager->displayStr(kTimelineEntries[i].x, kTimelineEntries[i].y, kTimelineEntries[i].year);
+ }
+ docSurface.transBlitFrom(_sprites->getSurface(105), boxes.getBoxOrigin(leaveBoxId),
+ _sprites->getKeyColor(105));
+
+ g_system->copyRectToScreen(docSurface.getPixels(), docSurface.pitch, 0, 0,
+ docSurface.w, docSurface.h);
+ redraw = false;
+ }
+ g_system->updateScreen();
+
+ if (_engine->pollEvents()) {
+ Common::Point mouse = _engine->getMousePos();
+ if (!_engine->getCurrentMouseButton()) {
+ // Don't change highlighted date when clicking
+ bool foundBox = false;
+ for (unsigned int i = 0; i < ARRAYSIZE(kTimelineEntries); i++) {
+ if (boxes.hitTest(i, mouse)) {
+ foundBox = true;
+ if (i != hoveredBox) {
+ hoveredBox = i;
+ redraw = true;
+ }
+ }
+ }
+ if (!foundBox && hoveredBox != -1u) {
+ hoveredBox = -1;
+ redraw = true;
+ }
+ }
+ if (_engine->getDragStatus() == kDragStatus_Finished) {
+ if (hoveredBox != -1u) {
+ selectedBox = hoveredBox;
+ }
+ if (boxes.hitTest(leaveBoxId, mouse)) {
+ selectedBox = leaveBoxId;
+ }
+ }
+ if (_engine->checkKeysPressed(1, Common::KEYCODE_ESCAPE)) {
+ selectedBox = leaveBoxId;
+ }
+ }
+ if (g_engine->shouldQuit()) {
+ selectedBox = leaveBoxId;
+ }
+ }
+
+ g_system->showMouse(false);
+
+ delete imageDecoder;
+
+ if (selectedBox == leaveBoxId) {
+ return "";
+ } else {
+ Common::String ret = "VT";
+ ret += kTimelineEntries[selectedBox].year;
+ return ret;
+ }
+}
+
+unsigned int Versailles_Documentation::docAreaHandleRecords(const Common::String &record) {
+ unsigned int action = -1;
+
+ _currentRecord = record;
+ _visitTrace.clear();
+
+ Graphics::ManagedSurface docSurface;
+ Common::String nextRecord;
+ MouseBoxes boxes(10 + ARRAYSIZE(kTimelineEntries));
+
+ while (true) {
+ if (action == -1u) {
+ _currentRecord.toUppercase();
+
+ //debug("Displaying %s", _currentRecord.c_str());
+ docAreaPrepareNavigation();
+ docAreaPrepareRecord(docSurface, boxes);
+ action = docAreaHandleRecord(docSurface, boxes, nextRecord);
+ }
+
+ switch (action) {
+ case 0:
+ action = -1;
+ // Back
+ if (!_visitTrace.empty()) {
+ _currentRecord = _visitTrace.back();
+ _visitTrace.pop_back();
+ break;
+ }
+ // No previous record, like a back to root
+ // fall through
+ case 1:
+ // Back to root
+ return 1;
+ case 2:
+ action = -1;
+ // Follow hyperlink keeping trace
+ _visitTrace.push_back(_currentRecord);
+ _currentRecord = nextRecord;
+ break;
+ case 3:
+ action = -1;
+ // Follow hyperlink losing trace
+ _visitTrace.clear();
+ _currentRecord = nextRecord;
+ break;
+ case 6:
+ // Quit
+ return 2;
+ case 7:
+ action = -1;
+ // General map
+ _visitTrace.clear();
+ nextRecord = docAreaHandleGeneralMap();
+ if (nextRecord == "") {
+ // Go back to current record
+ break;
+ } else if (nextRecord != "VS00") {
+ _currentRecord = nextRecord;
+ break;
+ }
+ // castle has been selected, display its map
+ // fall through
+ case 8:
+ action = -1;
+ // Castle map
+ _visitTrace.clear();
+ nextRecord = docAreaHandleCastleMap();
+ if (nextRecord == "") {
+ // Go back to current record
+ break;
+ } else if (nextRecord != "planG") {
+ _currentRecord = nextRecord;
+ break;
+ } else {
+ // We can't go up to previous case, so let's do a round
+ action = 7;
+ break;
+ }
+ case 9:
+ action = -1;
+ // Start of category
+ _currentRecord = _categoryStartRecord;
+ break;
+ default:
+ error("Invalid case %d when displaying doc record", action);
+ }
+ }
+ error("shouldn't be there");
+}
+
+void Versailles_Documentation::docAreaPrepareNavigation() {
+ _currentInTimeline = false;
+ _currentMapLayout = false;
+ _currentHasMap = false;
+ _currentLinks.clear();
+
+ if (_currentRecord.hasPrefix("VA")) {
+ _categoryStartRecord = "VA00";
+ _categoryEndRecord = "VA15";
+ _categoryTitle = (*_messages)[68];
+ } else if (_currentRecord.hasPrefix("VC")) {
+ _categoryStartRecord = "VC00";
+ _categoryEndRecord = "VC26";
+ _categoryTitle = (*_messages)[70];
+ } else if (_currentRecord.hasPrefix("VR")) {
+ _categoryStartRecord = "VR00";
+ _categoryEndRecord = "VR14";
+ _categoryTitle = (*_messages)[69];
+ } else if (_currentRecord.hasPrefix("VS")) {
+ _categoryStartRecord = "VS00";
+ _categoryEndRecord = "VS37";
+ _categoryTitle = (*_messages)[72];
+ unsigned int id = atoi(_currentRecord.c_str() + 2);
+ if (id >= 16 && id <= 40) {
+ _currentMapLayout = true;
+ }
+ if ((id >= 16 && id <= 31) ||
+ (id >= 35 && id <= 39)) {
+ _currentHasMap = true;
+ }
+ } else if (_currentRecord.hasPrefix("VT")) {
+ _categoryStartRecord = "VT00";
+ _categoryEndRecord = "VT1715";
+ _categoryTitle = (*_messages)[73];
+ _currentInTimeline = true;
+ } else if (_currentRecord.hasPrefix("VV")) {
+ _categoryStartRecord = "VV00";
+ _categoryEndRecord = "VV15";
+ _categoryTitle = (*_messages)[71];
+ }
+ getLinks(_currentRecord, _currentLinks);
+}
+
+void Versailles_Documentation::docAreaPrepareRecord(Graphics::ManagedSurface &surface,
+ MouseBoxes &boxes) {
+ boxes.reset();
+
+ setupRecordBoxes(true, boxes);
+
+ Common::String title, subtitle, caption;
+ Common::StringArray hyperlinks;
+ Common::String text = getRecordData(_currentRecord, title, subtitle, caption, hyperlinks);
+
+ drawRecordData(surface, text, title, subtitle, caption);
+
+ if (_currentInTimeline) {
+ surface.hLine(0, 39, 171, 241); // minus 1 because hLine draws inclusive
+ _fontManager->setCurrentFont(0);
+ _fontManager->setTransparentBackground(true);
+ _fontManager->setLineHeight(14);
+ _fontManager->setSpaceWidth(0);
+ _fontManager->setCharSpacing(1);
+ _fontManager->setSurface(&surface);
+ _fontManager->setForeColor(243);
+ for (unsigned int box_id = 10; box_id < ARRAYSIZE(kTimelineEntries) + 10; box_id++) {
+ boxes.display(box_id, *_fontManager);
+ }
+ }
+
+ drawRecordBoxes(surface, true, boxes);
+}
+
+unsigned int Versailles_Documentation::docAreaHandleRecord(Graphics::ManagedSurface &surface,
+ MouseBoxes &boxes, Common::String &nextRecord) {
+ // Hovering is only handled for timeline entries
+ _engine->setCursor(181);
+ g_system->showMouse(true);
+
+ bool first = true;
+ bool redraw = true;
+ unsigned int hoveredBox = -1;
+ unsigned int action = -1;
+
+ while (action == -1u) {
+ if (redraw) {
+ g_system->copyRectToScreen(surface.getPixels(), surface.pitch, 0, 0, surface.w, surface.h);
+ redraw = false;
+ }
+ g_system->updateScreen();
+
+ if (_engine->pollEvents() || first) {
+ first = false;
+ if (g_engine->shouldQuit()) {
+ // Fake the quit
+ action = 6;
+ }
+ Common::Point mouse = _engine->getMousePos();
+ if (_currentInTimeline) {
+ bool foundBox = false;
+ for (unsigned int i = 10; i < 10 + ARRAYSIZE(kTimelineEntries); i++) {
+ if (boxes.hitTest(i, mouse)) {
+ foundBox = true;
+ if (i != hoveredBox) {
+ _fontManager->setCurrentFont(0);
+ _fontManager->setTransparentBackground(true);
+ _fontManager->setSurface(&surface);
+ if (hoveredBox != -1u) {
+ // Restore the previous entry hovered
+ _fontManager->setForeColor(243);
+ boxes.display(hoveredBox, *_fontManager);
+ }
+ hoveredBox = i;
+ _fontManager->setForeColor(241);
+ boxes.display(hoveredBox, *_fontManager);
+ redraw = true;
+ }
+ }
+ }
+ if (!foundBox && hoveredBox != -1u) {
+ // Restore the previous entry hovered
+ _fontManager->setForeColor(243);
+ boxes.display(hoveredBox, *_fontManager);
+ hoveredBox = -1;
+ redraw = true;
+ }
+ } else if (_currentHasMap) { // Mutually exclusive with timeline
+ // No clash is possible for hoveredBox between timeline and map
+ if (boxes.hitTest(8, mouse)) {
+ if (hoveredBox != 8) {
+ _engine->setCursor(145);
+ hoveredBox = 8;
+ }
+ } else {
+ if (hoveredBox == 8) {
+ _engine->setCursor(181);
+ hoveredBox = -1;
+ }
+ }
+ }
+ if (_engine->getDragStatus() == kDragStatus_Pressed) {
+ if (boxes.hitTest(2, mouse) && _currentLinks.size()) {
+ Common::StringArray items;
+ for (Common::Array<LinkInfo>::const_iterator it = _currentLinks.begin(); it != _currentLinks.end();
+ it++) {
+ items.push_back(it->title);
+ }
+ Common::Rect iconRect = boxes.getBoxRect(2);
+ unsigned int selectedItem = handlePopupMenu(surface, Common::Point(iconRect.right, iconRect.top),
+ true, 20, items);
+ if (selectedItem != -1u) {
+ nextRecord = _currentLinks[selectedItem].record;
+ action = 2;
+ }
+ } else if (boxes.hitTest(3, mouse)) {
+ Common::StringArray items;
+ for (Common::Array<LinkInfo>::const_iterator it = _allLinks.begin(); it != _allLinks.end(); it++) {
+ items.push_back(it->title);
+ }
+ Common::Rect iconRect = boxes.getBoxRect(3);
+ unsigned int selectedItem = handlePopupMenu(surface, Common::Point(iconRect.right, iconRect.top),
+ true, 20, items);
+ if (selectedItem != -1u) {
+ nextRecord = _allLinks[selectedItem].record;
+ action = 3;
+ }
+ }
+ } else if (_engine->getDragStatus() == kDragStatus_Finished) {
+ if (boxes.hitTest(0, mouse)) {
+ // Back in history
+ action = 0;
+ } else if (boxes.hitTest(1, mouse)) {
+ // Handle summary menu
+ Common::StringArray items;
+ items.push_back((*_messages)[61]);
+ items.push_back((*_messages)[62]);
+ unsigned int selectedItem = handlePopupMenu(surface, boxes.getBoxOrigin(1), false, 20, items);
+ if (selectedItem == 0) {
+ action = 1;
+ } else if (selectedItem == 1) {
+ action = 7;
+ }
+ } else if (boxes.hitTest(4, mouse)) {
+ // Next
+ action = 4;
+ } else if (boxes.hitTest(5, mouse)) {
+ // Previous
+ action = 5;
+ } else if (boxes.hitTest(6, mouse)) {
+ // Handle quit menu
+ Common::StringArray items;
+ items.push_back((*_messages)[60]);
+ unsigned int selectedItem = handlePopupMenu(surface, boxes.getBoxOrigin(6), false, 20, items);
+ if (selectedItem == 0) {
+ action = 6;
+ }
+ } else if (_currentHasMap && boxes.hitTest(8, mouse)) {
+ // Map
+ action = 8;
+ } else if (boxes.hitTest(9, mouse)) {
+ // Category name
+ action = 9;
+ } else if (_currentInTimeline && hoveredBox != -1u) {
+ // Clicked on a timeline entry
+ nextRecord = "VT";
+ nextRecord += kTimelineEntries[hoveredBox - 10].year;
+ // Fake a global jump
+ action = 3;
+ }
+ }
+ if (action == 4 || action == 5) {
+ if (action == 4 && _currentRecord == _categoryEndRecord) {
+ action = -1;
+ continue;
+ }
+ if (action == 5 && _currentRecord == _categoryStartRecord) {
+ action = -1;
+ continue;
+ }
+ Common::HashMap<Common::String, RecordInfo>::iterator hmIt = _records.find(_currentRecord);
+ if (hmIt == _records.end()) {
+ // Shouldn't happen
+ action = -1;
+ continue;
+ }
+ unsigned int recordId = hmIt->_value.id;
+ if (action == 4) {
+ recordId++;
+ } else if (action == 5) {
+ recordId--;
+ }
+ assert(recordId < _recordsOrdered.size());
+ nextRecord = _recordsOrdered[recordId];
+ // Fake a global jump
+ action = 3;
+ }
+ }
+ }
+
+ g_system->showMouse(false);
+ _engine->setCursor(181);
+ return action;
+}
+
+Common::String Versailles_Documentation::docAreaHandleGeneralMap() {
+ struct Area {
+ Common::Rect areaPos;
+ const char *record;
+ const char *bmp;
+ unsigned int messageId;
+ const Common::String *message;
+ Common::Point messagePos;
+ Graphics::Surface highlightedImg;
+
+ Area(const Common::Point &areaPos_, const char *bmp_, unsigned int messageId_,
+ const char *record_ = nullptr) :
+ areaPos(areaPos_.x, areaPos_.y, areaPos_.x, areaPos_.y), record(record_), bmp(bmp_),
+ messageId(messageId_), message(nullptr) { }
+ Area(const Common::Rect &areaPos_, unsigned int messageId_, const char *record_ = nullptr) :
+ areaPos(areaPos_), record(record_), bmp(nullptr), messageId(messageId_), message(nullptr) { }
+ } areas[] = {
+ Area(Common::Point(174, 181), "APL.bmp", 74),
+ Area(Common::Point(422, 129), "CHAT.bmp", 75, "VS00"),
+ Area(Common::Point(193, 204), "COLN.bmp", 76, "VS02"),
+ Area(Common::Point(327, 269), "LABY.bmp", 77, "VS33"),
+ Area(Common::Point(327, 170), "LATN.bmp", 78),
+ Area(Common::Point(396, 271), "ORG.bmp", 79, "VS32"),
+ Area(Common::Point(385, 203), "PART.bmp", 80, "VS06"),
+ Area(Common::Point(212, 193), "TAP.bmp", 81),
+ Area(Common::Rect(0, 194, 154, 211), 86, "VS09"),
+ Area(Common::Rect(396, 229, 450, 268), 87),
+ Area(Common::Rect(394, 133, 450, 177), 88),
+ Area(Common::Rect(489, 376, 592, 479), 89, "VS07"),
+ Area(Common::Rect(327, 233, 386, 266), 90),
+ Area(Common::Rect(395, 18, 451, 60), 91),
+ Area(Common::Rect(383, 381, 477, 479), 92)
+ };
+
+ _fontManager->setCurrentFont(0);
+ _fontManager->setTransparentBackground(true);
+ _fontManager->setLineHeight(14);
+ _fontManager->setSpaceWidth(0);
+ _fontManager->setCharSpacing(1);
+
+ MouseBoxes boxes(ARRAYSIZE(areas) + 1);
+
+ Image::BitmapDecoder bmpDecoder;
+ Common::File file;
+
+ for (unsigned int i = 0; i < ARRAYSIZE(areas); i++) {
+ if (areas[i].bmp) {
+ if (!file.open(areas[i].bmp)) {
+ error("Failed to open BMP file: %s", areas[i].bmp);
+ }
+ if (!bmpDecoder.loadStream(file)) {
+ error("Failed to load BMP file: %s", areas[i].bmp);
+ }
+ areas[i].highlightedImg.copyFrom(*bmpDecoder.getSurface());
+ bmpDecoder.destroy();
+ file.close();
+ areas[i].areaPos.setWidth(areas[i].highlightedImg.w);
+ areas[i].areaPos.setHeight(areas[i].highlightedImg.h);
+ }
+ areas[i].message = &(*_messages)[areas[i].messageId];
+ unsigned int lineWidth = _fontManager->getStrWidth(*areas[i].message);
+ areas[i].messagePos.x = (areas[i].areaPos.left + areas[i].areaPos.right) / 2 - lineWidth / 2;
+ areas[i].messagePos.y = areas[i].areaPos.top - 40;
+ if (areas[i].messagePos.x < 8) {
+ areas[i].messagePos.x = 8;
+ } else if (areas[i].messagePos.x + lineWidth > 627) {
+ areas[i].messagePos.x = 627 - lineWidth;
+ }
+ if (areas[i].messagePos.y < 5) {
+ areas[i].messagePos.y = 5;
+ }
+ const Common::Rect &areaPos = areas[i].areaPos;
+ boxes.setupBox(i, areaPos.left, areaPos.top, areaPos.right, areaPos.bottom);
+ }
+ boxes.setupBox(ARRAYSIZE(areas), 639 - _sprites->getCursor(105).getWidth(),
+ 479 - _sprites->getCursor(105).getHeight(), 640, 480);
+
+ Image::ImageDecoder *imageDecoder = _engine->loadHLZ("PLANGR.HLZ");
+ if (!imageDecoder) {
+ return "";
+ }
+ const Graphics::Surface *bgFrame = imageDecoder->getSurface();
+
+ Graphics::ManagedSurface mapSurface;
+ mapSurface.create(bgFrame->w, bgFrame->h, bgFrame->format);
+ mapSurface.blitFrom(*bgFrame);
+
+ _fontManager->setSurface(&mapSurface);
+
+ _engine->setupPalette(imageDecoder->getPalette(), imageDecoder->getPaletteStartIndex(),
+ imageDecoder->getPaletteColorCount());
+
+ _engine->setCursor(181);
+ g_system->showMouse(true);
+
+ bool redraw = true;
+ unsigned int hoveredBox = -1;
+ unsigned int selectedBox = -1;
+
+ while (selectedBox == -1u) {
+ if (redraw) {
+ // Draw without worrying of already modified areas, that's handled when recomputing hoveredBox
+ if (hoveredBox != -1u) {
+ if (areas[hoveredBox].highlightedImg.getPixels() != nullptr) {
+ mapSurface.transBlitFrom(areas[hoveredBox].highlightedImg,
+ Common::Point(areas[hoveredBox].areaPos.left, areas[hoveredBox].areaPos.top));
+ } else {
+ unsigned int middleX = (areas[hoveredBox].areaPos.left + areas[hoveredBox].areaPos.right) / 2;
+ unsigned int middleY = (areas[hoveredBox].areaPos.top + areas[hoveredBox].areaPos.bottom) / 2;
+ unsigned int spriteX = middleX - _sprites->getCursor(163).getWidth() / 2;
+ unsigned int spriteY = middleY - _sprites->getCursor(163).getHeight() / 2;
+ mapSurface.transBlitFrom(_sprites->getSurface(163), Common::Point(spriteX, spriteY),
+ _sprites->getKeyColor(163));
+ }
+ _fontManager->setForeColor(areas[hoveredBox].record == nullptr ? 243 : 240);
+ Graphics::Surface subSurface = mapSurface.getSubArea(Common::Rect(areas[hoveredBox].messagePos.x -
+ 3,
+ areas[hoveredBox].messagePos.y - 3,
+ areas[hoveredBox].messagePos.x + _fontManager->getStrWidth(*areas[hoveredBox].message) + 3,
+ areas[hoveredBox].messagePos.y + _fontManager->getFontMaxHeight() + 8));
+ _engine->makeTranslucent(subSurface, subSurface);
+ _fontManager->displayStr(areas[hoveredBox].messagePos.x, areas[hoveredBox].messagePos.y,
+ *areas[hoveredBox].message);
+ }
+ mapSurface.transBlitFrom(_sprites->getSurface(105), boxes.getBoxOrigin(ARRAYSIZE(areas)),
+ _sprites->getKeyColor(105));
+ /*
+ // For debugging only
+ for(unsigned int i = 0; i < ARRAYSIZE(areas); i++) {
+ mapSurface.frameRect(areas[i].areaPos, 0);
+ }
+ */
+
+ g_system->copyRectToScreen(mapSurface.getPixels(), mapSurface.pitch, 0, 0, mapSurface.w,
+ mapSurface.h);
+
+ redraw = false;
+ }
+ g_system->updateScreen();
+
+ if (_engine->pollEvents()) {
+ Common::Point mouse = _engine->getMousePos();
+ if (!_engine->getCurrentMouseButton()) {
+ // Don't change highlighted icon when clicking
+ bool foundBox = false;
+ unsigned int oldHoveredBox = hoveredBox;
+ for (unsigned int i = 0; i < ARRAYSIZE(areas); i++) {
+ if (boxes.hitTest(i, mouse)) {
+ if (i != hoveredBox) {
+ hoveredBox = i;
+ redraw = true;
+ }
+ foundBox = true;
+ break;
+ }
+ }
+ if (!foundBox && hoveredBox != -1u) {
+ hoveredBox = -1;
+ redraw = true;
+ }
+ if (hoveredBox != oldHoveredBox && oldHoveredBox != -1u) {
+ // Restore original area
+ mapSurface.blitFrom(*bgFrame, areas[oldHoveredBox].areaPos,
+ Common::Point(areas[oldHoveredBox].areaPos.left, areas[oldHoveredBox].areaPos.top));
+ Common::Rect textRect(areas[oldHoveredBox].messagePos.x - 3,
+ areas[oldHoveredBox].messagePos.y - 3,
+ areas[oldHoveredBox].messagePos.x + _fontManager->getStrWidth(*areas[oldHoveredBox].message) + 3,
+ areas[oldHoveredBox].messagePos.y + _fontManager->getFontMaxHeight() + 8);
+ mapSurface.blitFrom(*bgFrame, textRect,
+ Common::Point(textRect.left, textRect.top));
+ }
+ }
+ if (_engine->getDragStatus() == kDragStatus_Finished) {
+ if (hoveredBox != -1u && areas[hoveredBox].record) {
+ selectedBox = hoveredBox;
+ } else if (boxes.hitTest(ARRAYSIZE(areas), mouse)) {
+ selectedBox = ARRAYSIZE(areas);
+ }
+ }
+ if (_engine->checkKeysPressed(1, Common::KEYCODE_ESCAPE)) {
+ selectedBox = ARRAYSIZE(areas);
+ }
+ if (g_engine->shouldQuit()) {
+ selectedBox = ARRAYSIZE(areas);
+ }
+ }
+ }
+
+ g_system->showMouse(false);
+
+ delete imageDecoder;
+
+ if (selectedBox == ARRAYSIZE(areas)) {
+ return "";
+ } else {
+ return areas[selectedBox].record;
+ }
+}
+
+Common::String Versailles_Documentation::docAreaHandleCastleMap() {
+ struct Area {
+ Common::Rect areaPos;
+ bool fillArea;
+ const char *record;
+ unsigned int messageId;
+ Common::String message;
+ Common::Point messagePos;
+ Common::Rect areaPos1;
+ Common::Rect areaPos2;
+
+ Area(const Common::Rect &areaPos_, const char *record_, bool fillArea_ = true,
+ unsigned int messageId_ = -1) :
+ areaPos(areaPos_), record(record_), fillArea(fillArea_), messageId(messageId_) { }
+ Area(const Common::Rect &areaPos_, const Common::Rect &areaPos1_,
+ const Common::Rect &areaPos2_, const char *record_, bool fillArea_ = true,
+ unsigned int messageId_ = -1) :
+ areaPos(areaPos_), areaPos1(areaPos1_), areaPos2(areaPos2_),
+ record(record_), fillArea(fillArea_), messageId(messageId_) { }
+ } areas[] = {
+ /* 0 */
+ Area(Common::Rect(212, 134, 239, 164), "VS16"),
+ Area(Common::Rect(74, 160, 89, 173), "VS24"),
+ Area(Common::Rect(93, 160, 109, 173), "VS25"),
+ Area(Common::Rect(130, 160, 154, 173), "VS26"),
+ Area(Common::Rect(158, 160, 171, 173), "VS27"),
+ Area(Common::Rect(199, 160, 209, 171), "VS28"),
+ Area(Common::Rect(74, 177, 89, 291), "VS31"),
+ Area(Common::Rect(158, 178, 195, 193), "VS30"),
+ Area(Common::Rect(199, 175, 209, 188), "VS29"),
+ Area(Common::Rect(112, 220, 160, 249), "VS35"),
+ /* 10 */
+ Area(Common::Rect(93, 227, 106, 240), "VS23"),
+ Area(Common::Rect(93, 244, 106, 257), "VS22"),
+ Area(Common::Rect(93, 261, 106, 274), "VS20"),
+ Area(Common::Rect(110, 255, 126, 269), "VS19"),
+ Area(Common::Rect(133, 255, 155, 271), "VS18"),
+ Area(Common::Rect(93, 285, 99, 295), "VS21"),
+ Area(Common::Rect(152, 279, 173, 288), "VS17"),
+ Area(Common::Rect(336, 113, 359, 136), Common::Rect(359, 116, 448, 134), Common::Rect(449, 113, 473, 136), "VS36"),
+ Area(Common::Rect(336, 328, 359, 351), Common::Rect(359, 331, 448, 348), Common::Rect(449, 328, 473, 351), "VS36"),
+ Area(Common::Rect(563, 0, 624, 139), "planG", false, 82),
+ /* 20 */
+ Area(Common::Rect(563, 300, 624, 462), "planG", false, 83),
+ Area(Common::Rect(0, 0, 205, 152), "planG", false, 84),
+ Area(Common::Rect(0, 318, 205, 465), "planG", false, 84),
+ Area(Common::Rect(160, 210, 329, 267), "VS40", false),
+ Area(Common::Rect(330, 158, 561, 315), "planG", false, 85),
+ };
+
+ _fontManager->setCurrentFont(0);
+ _fontManager->setTransparentBackground(true);
+ _fontManager->setLineHeight(14);
+ _fontManager->setSpaceWidth(0);
+ _fontManager->setCharSpacing(1);
+
+ MouseBoxes boxes(ARRAYSIZE(areas) + 1);
+
+ for (unsigned int i = 0; i < ARRAYSIZE(areas); i++) {
+ if (areas[i].messageId != -1u) {
+ areas[i].message = (*_messages)[areas[i].messageId];
+ } else {
+ areas[i].message = getRecordTitle(areas[i].record);
+ }
+ unsigned int lineWidth = _fontManager->getStrWidth(areas[i].message);
+ unsigned int right;
+ if (areas[i].areaPos2.right) {
+ right = areas[i].areaPos2.right;
+ } else {
+ right = areas[i].areaPos.right;
+ }
+ areas[i].messagePos.x = (areas[i].areaPos.left + right) / 2 - lineWidth / 2;
+ if (areas[i].fillArea) {
+ areas[i].messagePos.y = areas[i].areaPos.top - 30;
+ } else {
+ areas[i].messagePos.y = (areas[i].areaPos.top + areas[i].areaPos.bottom) / 2 - 50;
+ }
+ if (areas[i].messagePos.x < 5) {
+ areas[i].messagePos.x = 5;
+ } else if (areas[i].messagePos.x + lineWidth > 630) {
+ areas[i].messagePos.x = 630 - lineWidth;
+ }
+ if (areas[i].messagePos.y < 2) {
+ areas[i].messagePos.y = 2;
+ }
+ Common::Rect areaPos = areas[i].areaPos;
+ if (areas[i].areaPos2.right) {
+ areaPos.right = areas[i].areaPos2.right;
+ areaPos.bottom = areas[i].areaPos2.bottom;
+ }
+ boxes.setupBox(i, areaPos.left, areaPos.top, areaPos.right, areaPos.bottom);
+ }
+ boxes.setupBox(ARRAYSIZE(areas), 639 - _sprites->getCursor(105).getWidth(),
+ 479 - _sprites->getCursor(105).getHeight(), 640, 480);
+
+ Image::ImageDecoder *imageDecoder = _engine->loadHLZ("PLAN.HLZ");
+ if (!imageDecoder) {
+ return "";
+ }
+ const Graphics::Surface *bgFrame = imageDecoder->getSurface();
+
+ Graphics::ManagedSurface mapSurface;
+ mapSurface.create(bgFrame->w, bgFrame->h, bgFrame->format);
+ mapSurface.blitFrom(*bgFrame);
+
+ _fontManager->setSurface(&mapSurface);
+
+ _engine->setupPalette(imageDecoder->getPalette(), imageDecoder->getPaletteStartIndex(),
+ imageDecoder->getPaletteColorCount());
+
+ _engine->setCursor(181);
+ g_system->showMouse(true);
+
+ bool redraw = true;
+ unsigned int hoveredBox = -1;
+ unsigned int selectedBox = -1;
+
+ while (selectedBox == -1u) {
+ if (redraw) {
+ // Draw without worrying of already modified areas, that's handled when recomputing hoveredBox
+ if (hoveredBox != -1u) {
+ if (areas[hoveredBox].fillArea) {
+ Common::Rect rect(areas[hoveredBox].areaPos);
+ rect.bottom += 1; // fillRect needs to fill including the limit
+ rect.right += 1;
+ mapSurface.fillRect(rect, 243);
+ if (areas[hoveredBox].areaPos1.right) {
+ rect = Common::Rect(areas[hoveredBox].areaPos1);
+ rect.bottom += 1; // fillRect needs to fill including the limit
+ rect.right += 1;
+ mapSurface.fillRect(rect, 243);
+ }
+ if (areas[hoveredBox].areaPos2.right) {
+ rect = Common::Rect(areas[hoveredBox].areaPos2);
+ rect.bottom += 1; // fillRect needs to fill including the limit
+ rect.right += 1;
+ mapSurface.fillRect(rect, 243);
+ }
+ } else {
+ unsigned int middleX = (areas[hoveredBox].areaPos.left + areas[hoveredBox].areaPos.right) / 2;
+ unsigned int middleY = (areas[hoveredBox].areaPos.top + areas[hoveredBox].areaPos.bottom) / 2;
+ unsigned int spriteX = middleX - _sprites->getCursor(163).getWidth() / 2;
+ unsigned int spriteY = middleY - _sprites->getCursor(163).getHeight() / 2;
+ mapSurface.transBlitFrom(_sprites->getSurface(163), Common::Point(spriteX, spriteY),
+ _sprites->getKeyColor(163));
+ }
+ Common::Rect textRect(areas[hoveredBox].messagePos.x - 4,
+ areas[hoveredBox].messagePos.y,
+ areas[hoveredBox].messagePos.x + _fontManager->getStrWidth(areas[hoveredBox].message) + 5,
+ areas[hoveredBox].messagePos.y + _fontManager->getFontMaxHeight() + 5);
+ mapSurface.fillRect(textRect, 247);
+ _fontManager->setForeColor(strcmp(areas[hoveredBox].record, "planG") == 0 ? 243 : 241);
+ _fontManager->displayStr(areas[hoveredBox].messagePos.x, areas[hoveredBox].messagePos.y,
+ areas[hoveredBox].message);
+ }
+ mapSurface.transBlitFrom(_sprites->getSurface(105), boxes.getBoxOrigin(ARRAYSIZE(areas)),
+ _sprites->getKeyColor(105));
+ /*
+ // For debugging only
+ for(unsigned int i = 0; i < ARRAYSIZE(areas); i++) {
+ mapSurface.frameRect(areas[i].areaPos, 0);
+ if (areas[i].areaPos1.right) {
+ mapSurface.frameRect(areas[i].areaPos1, 0);
+ }
+ if (areas[i].areaPos2.right) {
+ mapSurface.frameRect(areas[i].areaPos2, 0);
+ }
+ }
+ */
+
+ g_system->copyRectToScreen(mapSurface.getPixels(), mapSurface.pitch, 0, 0,
+ mapSurface.w, mapSurface.h);
+
+ redraw = false;
+ }
+ g_system->updateScreen();
+
+ if (_engine->pollEvents()) {
+ Common::Point mouse = _engine->getMousePos();
+ if (!_engine->getCurrentMouseButton()) {
+ // Don't change highlighted icon when clicking
+ bool foundBox = false;
+ unsigned int oldHoveredBox = hoveredBox;
+ for (unsigned int i = 0; i < ARRAYSIZE(areas); i++) {
+ if (boxes.hitTest(i, mouse)) {
+ if (i != hoveredBox) {
+ hoveredBox = i;
+ redraw = true;
+ }
+ foundBox = true;
+ break;
+ }
+ }
+ if (!foundBox && hoveredBox != -1u) {
+ hoveredBox = -1;
+ redraw = true;
+ }
+ if (hoveredBox != oldHoveredBox && oldHoveredBox != -1u) {
+ // Restore original area
+ Common::Rect areaPos = areas[oldHoveredBox].areaPos;
+ if (areas[oldHoveredBox].areaPos2.right) {
+ areaPos.right = areas[oldHoveredBox].areaPos2.right;
+ areaPos.bottom = areas[oldHoveredBox].areaPos2.bottom;
+ }
+ areaPos.right += 1;
+ areaPos.bottom += 1;
+ mapSurface.blitFrom(*bgFrame, areaPos,
+ Common::Point(areaPos.left, areaPos.top));
+ Common::Rect textRect(areas[oldHoveredBox].messagePos.x - 4,
+ areas[oldHoveredBox].messagePos.y,
+ areas[oldHoveredBox].messagePos.x + _fontManager->getStrWidth(areas[oldHoveredBox].message) + 5,
+ areas[oldHoveredBox].messagePos.y + _fontManager->getFontMaxHeight() + 5);
+ mapSurface.blitFrom(*bgFrame, textRect,
+ Common::Point(textRect.left, textRect.top));
+ }
+ }
+ if (_engine->getDragStatus() == kDragStatus_Finished) {
+ if (hoveredBox != -1u && areas[hoveredBox].record) {
+ selectedBox = hoveredBox;
+ } else if (boxes.hitTest(ARRAYSIZE(areas), mouse)) {
+ selectedBox = ARRAYSIZE(areas);
+ }
+ }
+ if (_engine->checkKeysPressed(1, Common::KEYCODE_ESCAPE)) {
+ selectedBox = ARRAYSIZE(areas);
+ }
+ if (g_engine->shouldQuit()) {
+ selectedBox = ARRAYSIZE(areas);
+ }
+ }
+ }
+
+ g_system->showMouse(false);
+
+ delete imageDecoder;
+
+ if (selectedBox == ARRAYSIZE(areas)) {
+ return "";
+ } else {
+ return areas[selectedBox].record;
+ }
+}
+
+void Versailles_Documentation::inGamePrepareRecord(Graphics::ManagedSurface &surface,
+ MouseBoxes &boxes) {
+ _categoryStartRecord = "";
+ _categoryEndRecord = "";
+ _categoryTitle = "";
+ _currentLinks.clear();
+ _currentInTimeline = false;
+ _currentMapLayout = false;
+ _currentHasMap = false;
+
+ if (_currentRecord.hasPrefix("VS")) {
+ unsigned int id = atoi(_currentRecord.c_str() + 2);
+ if (id >= 16 && id <= 40) {
+ _currentMapLayout = true;
+ }
+ } else if (_currentRecord.hasPrefix("VT")) {
+ error("There shouldn't be the timeline in game");
+ }
+
+ boxes.reset();
+
+ setupRecordBoxes(false, boxes);
+
+ Common::String title, subtitle, caption;
+ Common::StringArray hyperlinks;
+ Common::String text = getRecordData(_currentRecord, title, subtitle, caption, hyperlinks);
+ convertHyperlinks(hyperlinks, _currentLinks);
+
+ drawRecordData(surface, text, title, subtitle, caption);
+ drawRecordBoxes(surface, false, boxes);
+}
+
+unsigned int Versailles_Documentation::inGameHandleRecord(Graphics::ManagedSurface &surface,
+ MouseBoxes &boxes, Common::String &nextRecord) {
+ _engine->setCursor(181);
+ g_system->showMouse(true);
+
+ unsigned int action = -1;
+
+ g_system->copyRectToScreen(surface.getPixels(), surface.pitch, 0, 0, surface.w, surface.h);
+
+ while (action == -1u) {
+ g_system->updateScreen();
+
+ if (_engine->pollEvents()) {
+ if (g_engine->shouldQuit()) {
+ // Fake the quit
+ action = 6;
+ }
+ Common::Point mouse = _engine->getMousePos();
+ if (_engine->getDragStatus() == kDragStatus_Pressed) {
+ if (boxes.hitTest(2, mouse) && _currentLinks.size()) {
+ Common::StringArray items;
+ for (Common::Array<LinkInfo>::const_iterator it = _currentLinks.begin(); it != _currentLinks.end();
+ it++) {
+ items.push_back(it->title);
+ }
+ Common::Rect iconRect = boxes.getBoxRect(2);
+ unsigned int selectedItem = handlePopupMenu(surface, Common::Point(iconRect.right, iconRect.top),
+ true, 20, items);
+ if (selectedItem != -1u) {
+ nextRecord = _currentLinks[selectedItem].record;
+ action = 2;
+ }
+ }
+ } else if (_engine->getDragStatus() == kDragStatus_Finished) {
+ if (boxes.hitTest(0, mouse)) {
+ // Back in history
+ action = 0;
+ } else if (boxes.hitTest(1, mouse)) {
+ // Quit
+ action = 1;
+ }
+ }
+ }
+ }
+
+ g_system->showMouse(false);
+ _engine->setCursor(181);
+ return action;
+}
+
+void Versailles_Documentation::drawRecordData(Graphics::ManagedSurface &surface,
+ const Common::String &text, const Common::String &title,
+ const Common::String &subtitle, const Common::String &caption) {
+ bool displayMap = false;
+ unsigned char foreColor = 247;
+ Common::String background;
+ Common::Rect blockTitle;
+ Common::Rect blockHLine;
+ Common::Rect blockSubTitle;
+ Common::Rect blockCaption;
+ Common::Rect blockContent1;
+ Common::Rect blockContent2;
+
+ if (_currentMapLayout) {
+ blockTitle = Common::Rect(30, 8, 361, 38);
+ blockHLine = Common::Rect(60, 35, 286, 35);
+ blockSubTitle = Common::Rect(60, 40, 361, 70);
+ blockCaption = Common::Rect(378, 293, 630, 344);
+ blockContent1 = Common::Rect(60, 60, 272, 295);
+ blockContent2 = Common::Rect(60, 295, 383, 437);
+ } else if (_currentInTimeline) {
+ blockTitle = Common::Rect(78, 10, 170, 33);
+ //blockHLine = Common::Rect();
+ blockSubTitle = Common::Rect(60, 40, 361, 70);
+ blockCaption = Common::Rect(378, 293, 630, 344);
+ blockContent1 = Common::Rect(47, 70, 420, 306);
+ blockContent2 = Common::Rect(174, 306, 414, 411);
+ } else if (_currentRecord == "VC02" ||
+ _currentRecord == "VC03" ||
+ _currentRecord == "VV01") {
+ blockTitle = Common::Rect(30, 8, 361, 38);
+ blockHLine = Common::Rect(60, 35, 378, 35);
+ blockSubTitle = Common::Rect(60, 40, 361, 70);
+ blockCaption = Common::Rect(378, 293, 630, 360);
+ blockContent1 = Common::Rect(60, 80, 351, 355);
+ blockContent2 = Common::Rect(60, 355, 605, 437);
+ } else if (_currentRecord == "VV13" ||
+ _currentRecord == "VV08") {
+ blockTitle = Common::Rect(30, 8, 361, 38);
+ blockHLine = Common::Rect(60, 35, 286, 35);
+ blockSubTitle = Common::Rect(60, 40, 361, 70);
+ blockCaption = Common::Rect(378, 422, 630, 480);
+ blockContent1 = Common::Rect(60, 60, 378, 285);
+ blockContent2 = Common::Rect(60, 285, 378, 437);
+ } else {
+ blockTitle = Common::Rect(30, 8, 361, 38);
+ blockHLine = Common::Rect(60, 35, 378, 35);
+ blockSubTitle = Common::Rect(60, 40, 361, 70);
+ blockCaption = Common::Rect(378, 293, 630, 360);
+ blockContent1 = Common::Rect(60, 80, 351, 345);
+ blockContent2 = Common::Rect(60, 345, 605, 437);
+ }
+ if (_currentInTimeline) {
+ background = "CHRONO1";
+ foreColor = 241;
+ } else {
+ background = _currentRecord;
+ }
+ background += ".HLZ";
+ Common::File backgroundFl;
+ if (!backgroundFl.open(background)) {
+ background = displayMap ? "pas_fonP.hlz" : "pas_fond.hlz";
+ } else {
+ backgroundFl.close();
+ }
+
+ Image::ImageDecoder *imageDecoder = _engine->loadHLZ(background);
+ const Graphics::Surface *bgFrame = imageDecoder->getSurface();
+
+ _engine->setupPalette(imageDecoder->getPalette(), imageDecoder->getPaletteStartIndex(),
+ imageDecoder->getPaletteColorCount());
+
+ surface.create(bgFrame->w, bgFrame->h, bgFrame->format);
+ surface.blitFrom(*bgFrame);
+
+ /*Common::String title, subtitle, caption;
+ Common::StringArray hyperlinks;
+
+ Common::String text = getRecordData(_currentRecord, title, subtitle, caption, hyperlinks);*/
+
+ unsigned int lineHeight = 21;
+ _fontManager->setCurrentFont(4);
+ _fontManager->setTransparentBackground(true);
+ _fontManager->setSpaceWidth(1);
+ _fontManager->setCharSpacing(1);
+ _fontManager->setForeColor(foreColor);
+ _fontManager->setSurface(&surface);
+
+ /*
+ surface.frameRect(blockContent1, foreColor);
+ surface.frameRect(blockContent2, foreColor);
+ surface.frameRect(blockTitle, foreColor);
+ surface.frameRect(blockSubTitle, foreColor);
+ surface.frameRect(blockCaption, foreColor);
+ */
+
+ Graphics::ManagedSurface backupSurface;
+ backupSurface.copyFrom(surface);
+
+ // This loop tries to adapt the interline space to make all the text fit in the blocks
+ while (true) {
+ _fontManager->setLineHeight(lineHeight);
+ _fontManager->setupBlock(blockContent1);
+ if (!_fontManager->displayBlockText(text)) {
+ // All text was drawn
+ break;
+ }
+
+ // Setup second zone
+ blockContent2.top = _fontManager->blockTextLastPos().y + lineHeight;
+ _fontManager->setupBlock(blockContent2);
+
+ if (!_fontManager->displayBlockText(text, _fontManager->blockTextRemaining())) {
+ // All text was drawn
+ break;
+ }
+
+ // Not all text could be drawn: shrink everything, restore image and do it again
+ lineHeight--;
+ surface.copyFrom(backupSurface);
+ }
+
+ _fontManager->setForeColor(foreColor);
+ _fontManager->setCurrentFont(0);
+ _fontManager->setTransparentBackground(true);
+ _fontManager->setLineHeight(20);
+ _fontManager->setCharSpacing(0);
+ _fontManager->setSpaceWidth(2);
+
+ //debug("Title: %s", title.c_str());
+ _fontManager->setupBlock(blockTitle);
+ _fontManager->displayBlockText(title);
+
+ _fontManager->setCurrentFont(6);
+ _fontManager->setLineHeight(14);
+ _fontManager->setSpaceWidth(1);
+
+ //debug("Subtitle: %s", subtitle.c_str());
+ _fontManager->setupBlock(blockSubTitle);
+ _fontManager->displayBlockText(subtitle);
+
+ if (!_currentInTimeline) {
+ surface.hLine(blockHLine.left, blockHLine.top, blockHLine.right - 1,
+ foreColor); // minus 1 because hLine draws inclusive
+ }
+
+ _fontManager->setSpaceWidth(0);
+
+ _fontManager->setupBlock(blockCaption);
+ _fontManager->displayBlockText(caption);
+}
+
+void Versailles_Documentation::setupRecordBoxes(bool inDocArea, MouseBoxes &boxes) {
+ // Layout of bar in doc area is Quit | Back | | Previous | Category | Next | | Trace | Hyperlinks | All records
+ // Layout of bar in game is ==> Trace | Hyperlinks | Quit
+ unsigned int allRecordsX = 640 - _sprites->getCursor(19).getWidth();
+ unsigned int hyperlinksX = allRecordsX - _sprites->getCursor(242).getWidth() - 10;
+ unsigned int traceX = hyperlinksX - _sprites->getCursor(105).getWidth() - 10;
+
+ if (_visitTrace.size()) {
+ boxes.setupBox(0, traceX, 480 - _sprites->getCursor(105).getHeight() - 3,
+ traceX + _sprites->getCursor(105).getWidth(), 480);
+ }
+ if (inDocArea) {
+ unsigned int backX = _sprites->getCursor(225).getWidth() + 10; //Right to quit button
+
+ _fontManager->setCurrentFont(0);
+ _fontManager->setTransparentBackground(true);
+ _fontManager->setSpaceWidth(0);
+ _fontManager->setCharSpacing(1);
+ unsigned int categoryHalfWidth = _fontManager->getStrWidth(_categoryTitle) / 2;
+ unsigned nextX = 320 + categoryHalfWidth + 20;
+ unsigned prevX = 320 - categoryHalfWidth - 20 - _sprites->getCursor(76).getWidth();
+
+ boxes.setupBox(3, allRecordsX, 480 - _sprites->getCursor(19).getHeight(),
+ allRecordsX + _sprites->getCursor(19).getWidth(), 480);
+ boxes.setupBox(1, backX, 480 - _sprites->getCursor(227).getHeight(),
+ backX + _sprites->getCursor(227).getWidth(), 480);
+ boxes.setupBox(9, 320 - categoryHalfWidth - 5, 480 - _sprites->getCursor(227).getHeight(),
+ 320 + categoryHalfWidth + 5, 480);
+ boxes.setupBox(4, nextX, 476 - _sprites->getCursor(72).getHeight(),
+ nextX + _sprites->getCursor(72).getWidth(), 476);
+ boxes.setupBox(5, prevX, 476 - _sprites->getCursor(76).getHeight(),
+ prevX + _sprites->getCursor(76).getWidth(), 476);
+ // Quit button
+ boxes.setupBox(6, 0, 480 - _sprites->getCursor(225).getHeight(),
+ _sprites->getCursor(225).getWidth(), 480);
+ // Map
+ boxes.setupBox(8, 403, 305, 622, 428);
+ if (_currentInTimeline) {
+ for (unsigned int box_id = 0; box_id < ARRAYSIZE(kTimelineEntries); box_id++) {
+ boxes.setupBox(10 + box_id, kTimelineEntries[box_id].x, kTimelineEntries[box_id].y,
+ kTimelineEntries[box_id].x + 30, kTimelineEntries[box_id].y + 15, kTimelineEntries[box_id].year);
+ }
+ }
+ } else {
+ unsigned int quitInGameX = 640 - _sprites->getCursor(105).getWidth();
+ boxes.setupBox(1, quitInGameX, 480 - _sprites->getCursor(105).getHeight(),
+ quitInGameX + _sprites->getCursor(105).getWidth(), 480);
+ }
+ boxes.setupBox(2, hyperlinksX, 480 - _sprites->getCursor(242).getHeight(),
+ hyperlinksX + _sprites->getCursor(242).getWidth(), 480);
+}
+
+void Versailles_Documentation::drawRecordBoxes(Graphics::ManagedSurface &surface, bool inDocArea,
+ MouseBoxes &boxes) {
+ if (_visitTrace.size()) {
+ surface.transBlitFrom(_sprites->getSurface(105), boxes.getBoxOrigin(0), _sprites->getKeyColor(105));
+ }
+ if (inDocArea) {
+ surface.transBlitFrom(_sprites->getSurface(19), boxes.getBoxOrigin(3), _sprites->getKeyColor(19));
+ surface.transBlitFrom(_sprites->getSurface(227), boxes.getBoxOrigin(1), _sprites->getKeyColor(227));
+
+ surface.fillRect(boxes.getBoxRect(9), 243);
+
+ _fontManager->setCurrentFont(0);
+ _fontManager->setTransparentBackground(true);
+ _fontManager->setSpaceWidth(0);
+ _fontManager->setCharSpacing(1);
+ _fontManager->setForeColor(240);
+ Common::Point catPos = boxes.getBoxOrigin(9);
+ catPos += Common::Point(5, 3);
+ _fontManager->displayStr(catPos.x, catPos.y, _categoryTitle);
+
+ if (_currentRecord == _categoryEndRecord) {
+ surface.transBlitFrom(_sprites->getSurface(75), boxes.getBoxOrigin(4), _sprites->getKeyColor(75));
+ } else {
+ surface.transBlitFrom(_sprites->getSurface(72), boxes.getBoxOrigin(4), _sprites->getKeyColor(72));
+ }
+ if (_currentRecord == _categoryStartRecord) {
+ surface.transBlitFrom(_sprites->getSurface(77), boxes.getBoxOrigin(5), _sprites->getKeyColor(77));
+ } else {
+ surface.transBlitFrom(_sprites->getSurface(76), boxes.getBoxOrigin(5), _sprites->getKeyColor(76));
+ }
+ surface.transBlitFrom(_sprites->getSurface(225), boxes.getBoxOrigin(6), _sprites->getKeyColor(225));
+ } else {
+ surface.transBlitFrom(_sprites->getSurface(105), boxes.getBoxOrigin(1), _sprites->getKeyColor(105));
+ }
+ if (_currentLinks.size()) {
+ surface.transBlitFrom(_sprites->getSurface(242), boxes.getBoxOrigin(2), _sprites->getKeyColor(242));
+ } else {
+ surface.transBlitFrom(_sprites->getSurface(244), boxes.getBoxOrigin(2), _sprites->getKeyColor(244));
+ }
+}
+
+unsigned int Versailles_Documentation::handlePopupMenu(const Graphics::ManagedSurface
+ &originalSurface,
+ const Common::Point &anchor, bool rightAligned, unsigned int itemHeight,
+ const Common::StringArray &items) {
+
+ unsigned int maxTextWidth = 0;
+
+ _fontManager->setCurrentFont(4);
+ _fontManager->setTransparentBackground(true);
+ _fontManager->setCharSpacing(1);
+
+ for (Common::StringArray::const_iterator it = items.begin(); it != items.end(); it++) {
+ unsigned int width = _fontManager->getStrWidth(*it);
+ if (width > maxTextWidth) {
+ maxTextWidth = width;
+ }
+ }
+
+ unsigned int width = maxTextWidth + 2 * kPopupMenuMargin;
+ unsigned int height = itemHeight * items.size() + 2 * kPopupMenuMargin;
+
+ unsigned int hiddenItems = 0;
+ int top = anchor.y - height;
+ while (top < 0) {
+ hiddenItems++;
+ top += itemHeight;
+ }
+ unsigned shownItems = items.size() - hiddenItems;
+
+ Common::Rect popupRect;
+ if (rightAligned) {
+ popupRect = Common::Rect(anchor.x - width, top, anchor.x, anchor.y);
+ } else {
+ popupRect = Common::Rect(anchor.x, top, anchor.x + width, anchor.y);
+ }
+
+ Graphics::ManagedSurface surface;
+ surface.copyFrom(originalSurface);
+
+ MouseBoxes boxes(shownItems);
+ for (unsigned int i = 0; i < shownItems; i++) {
+ boxes.setupBox(i, popupRect.left + kPopupMenuMargin,
+ popupRect.top + kPopupMenuMargin + i * itemHeight,
+ popupRect.right - kPopupMenuMargin,
+ popupRect.top + kPopupMenuMargin + (i + 1) * itemHeight);
+ }
+
+ _fontManager->setSurface(&surface);
+
+ bool fullRedraw = true;
+ bool redraw = true;
+ unsigned int hoveredBox = -1;
+ unsigned int action = -1;
+ unsigned int lastShownItem = items.size() - 1;
+ unsigned int firstShownItem = lastShownItem - shownItems + 1;
+
+ unsigned int slowScrollNextEvent = g_system->getMillis() + 250;
+
+ Common::Point mouse;
+
+ while (action == -1u) {
+ if (redraw) {
+ if (fullRedraw) {
+ surface.fillRect(popupRect, 247);
+ fullRedraw = false;
+ }
+ for (unsigned int i = 0; i < shownItems; i++) {
+ if (i == 0 && firstShownItem != 0) {
+ // There are items before the first one: display an arrow
+ surface.transBlitFrom(_sprites->getSurface(162),
+ Common::Point(popupRect.left + kPopupMenuMargin,
+ popupRect.top + kPopupMenuMargin + i * itemHeight + 3),
+ _sprites->getKeyColor(162));
+ } else if (i == shownItems - 1 && lastShownItem != items.size() - 1) {
+ // There are items after the last one: display an arrow
+ surface.transBlitFrom(_sprites->getSurface(185),
+ Common::Point(popupRect.left + kPopupMenuMargin,
+ popupRect.top + kPopupMenuMargin + i * itemHeight + 3),
+ _sprites->getKeyColor(185));
+ } else {
+ // Display the item text
+ _fontManager->setForeColor(i == hoveredBox ? 241 : 243);
+ _fontManager->displayStr(popupRect.left + kPopupMenuMargin,
+ popupRect.top + kPopupMenuMargin + i * itemHeight + 3, items[firstShownItem + i]);
+ }
+ }
+ g_system->copyRectToScreen(surface.getPixels(), surface.pitch, 0, 0, surface.w, surface.h);
+ redraw = false;
+ }
+ g_system->updateScreen();
+
+ if (_engine->pollEvents()) {
+ if (g_engine->shouldQuit()) {
+ // Fake the quit
+ break;
+ }
+ mouse = _engine->getMousePos();
+
+ unsigned int newHovered = -1;
+ for (unsigned int i = 0; i < shownItems; i++) {
+ if (boxes.hitTest(i, mouse)) {
+ newHovered = i;
+ break;
+ }
+ }
+
+ if (newHovered != hoveredBox) {
+ hoveredBox = newHovered;
+ redraw = true;
+ }
+ }
+
+ DragStatus dragStatus = _engine->getDragStatus();
+
+ if (hoveredBox == -1u) {
+ if (dragStatus == kDragStatus_Pressed) {
+ break;
+ } else {
+ continue;
+ }
+ }
+
+ // From there we only act if there is something hovered
+ if (hoveredBox == 0 && firstShownItem > 0) {
+ // Scroll up fast
+ firstShownItem--;
+ lastShownItem--;
+ slowScrollNextEvent = g_system->getMillis() + 250;
+ fullRedraw = true;
+ redraw = true;
+ } else if (hoveredBox == 1 && firstShownItem > 0) {
+ // Scroll up slow
+ if (g_system->getMillis() > slowScrollNextEvent) {
+ firstShownItem--;
+ lastShownItem--;
+ slowScrollNextEvent = g_system->getMillis() + 250;
+ fullRedraw = true;
+ redraw = true;
+ }
+ } else if (hoveredBox == shownItems - 2 && lastShownItem < items.size() - 1) {
+ // Scroll down slow
+ if (g_system->getMillis() > slowScrollNextEvent) {
+ firstShownItem++;
+ lastShownItem++;
+ slowScrollNextEvent = g_system->getMillis() + 250;
+ fullRedraw = true;
+ redraw = true;
+ }
+ } else if (hoveredBox == shownItems - 1 && lastShownItem < items.size() - 1) {
+ // Scroll down fast
+ firstShownItem++;
+ lastShownItem++;
+ slowScrollNextEvent = g_system->getMillis() + 250;
+ fullRedraw = true;
+ redraw = true;
+ } else if (dragStatus == kDragStatus_Finished) {
+ action = hoveredBox + firstShownItem;
+ continue;
+ }
+ }
+
+ // Restore original surface
+ g_system->copyRectToScreen(originalSurface.getPixels(), originalSurface.pitch, 0, 0,
+ originalSurface.w, originalSurface.h);
+ g_system->updateScreen();
+
+ _engine->waitMouseRelease();
+
+ return action;
+}
+
+/* Below is documentation files parsing */
+
+char *Versailles_Documentation::getDocPartAddress(char *start, char *end, const char *patterns[]) {
+ if (!start) {
+ return nullptr;
+ }
+ char *foundPos = nullptr;
+ const char *pattern;
+ unsigned int patternLen;
+ for (const char **patternP = patterns; *patternP && !foundPos; patternP++) {
+ pattern = *patternP;
+ patternLen = strlen(pattern);
+ /*debug("Matching %.10s", pattern);*/
+ for (char *p = start; p < end - patternLen - 1; p++) {
+ /*if (p == start || *p == '\r' || *p == '\0') {
+ debug("Line %.10s", p == start ? start : p+1);
+ }*/
+ if (p == start && !strncmp(p, pattern, patternLen)) {
+ foundPos = p;
+ break;
+ } else if ((*p == '\r' || *p == '\0') && !strncmp(p + 1, pattern, patternLen)) {
+ foundPos = p + 1;
+ break;
+ }
+ }
+ }
+ if (!foundPos) {
+ return nullptr;
+ }
+ /*debug("Matched %.10s", foundPos);*/
+ foundPos += patternLen;
+ char *eol = foundPos;
+ for (; *eol != '\r' && *eol != '\0'; eol++) {}
+ *eol = '\0';
+ return foundPos;
+}
+
+static bool hasEqualInLine(const char *text, const char *end) {
+ for (; text < end && *text && *text != '\r' && *text != '='; text++) { }
+ return text < end && *text == '=';
+}
+
+static const char *nextLine(const char *text, const char *end) {
+ for (; text < end && *text && *text != '\r'; text++) { }
+ return text < end ? text + 1 : end;
+}
+
+const char *Versailles_Documentation::getDocTextAddress(char *start, char *end) {
+ if (!start) {
+ return nullptr;
+ }
+ const char *foundPos = nullptr;
+ const char *p = start;
+ while (p < end) {
+ if (hasEqualInLine(p, end)) {
+ p = nextLine(p, end);
+ if (p < end && !hasEqualInLine(p, end)) {
+ // Only return the text that is after the last =
+ foundPos = p;
+ }
+ } else {
+ p = nextLine(p, end);
+ }
+ }
+ return foundPos;
+}
+
+const char *Versailles_Documentation::getRecordCaption(char *start, char *end) {
+ const char *patterns[] = { "LEGENDE=", "LEGENDE =", nullptr };
+ const char *ret = getDocPartAddress(start, end, patterns);
+ return ret;
+}
+
+const char *Versailles_Documentation::getRecordTitle(char *start, char *end) {
+ const char *patterns[] = { "TITRE=", "TITRE =", nullptr };
+ const char *ret = getDocPartAddress(start, end, patterns);
+ return ret;
+}
+
+const char *Versailles_Documentation::getRecordSubtitle(char *start, char *end) {
+ const char *patterns[] = { "SOUS-TITRE=", "SOUS_TITRE=", "SOUS-TITRE =", "SOUS_TITRE =", "SOUS TITRE=", nullptr };
+ char *ret = getDocPartAddress(start, end, patterns);
+ if (!ret) {
+ return nullptr;
+ }
+
+ unsigned int ln = strlen(ret);
+ char *p = ret + ln + 1; // Got to end of line and check next line
+ for (; p < end && *p && *p != '\r' && *p != '=' ; p++) { }
+ if (*p == '=') {
+ // Next line has a =, so it's not multiline
+ return ret;
+ }
+
+ if (*p == '\r') {
+ *p = '\0';
+ }
+ ret[ln] = '\r';
+
+ return ret;
+}
+
+void Versailles_Documentation::getRecordHyperlinks(char *start, char *end,
+ Common::StringArray &hyperlinks) {
+ const char *const hyperlinksPatterns[] = { "SAVOIR-PLUS 1=", "SAVOIR-PLUS 2=", "SAVOIR-PLUS 3=" };
+
+ hyperlinks.clear();
+ for (unsigned int hyperlinkId = 0; hyperlinkId < ARRAYSIZE(hyperlinksPatterns); hyperlinkId++) {
+ const char *patterns[] = { hyperlinksPatterns[hyperlinkId], nullptr };
+ const char *ret = getDocPartAddress(start, end, patterns);
+ if (ret) {
+ hyperlinks.push_back(ret);
+ }
+ }
+}
+
+Common::String Versailles_Documentation::getRecordTitle(const Common::String &record) {
+ Common::HashMap<Common::String, RecordInfo>::iterator it = _records.find(record);
+ if (it == _records.end()) {
+ return "";
+ }
+
+ const RecordInfo &recordInfo = it->_value;
+ Common::File allDocsFile;
+
+ if (!allDocsFile.open(kAllDocsFile)) {
+ error("Can't open %s", kAllDocsFile);
+ }
+ allDocsFile.seek(recordInfo.position);
+
+ char *recordData = new char[recordInfo.size + 1];
+ allDocsFile.read(recordData, recordInfo.size);
+ recordData[recordInfo.size] = '\0';
+ char *recordDataEnd = recordData + recordInfo.size + 1;
+
+ Common::String title = getRecordTitle(recordData, recordDataEnd);
+
+ delete[] recordData;
+
+ return title;
+}
+
+Common::String Versailles_Documentation::getRecordData(const Common::String &record,
+ Common::String &title, Common::String &subtitle, Common::String &caption,
+ Common::StringArray &hyperlinks) {
+ Common::HashMap<Common::String, RecordInfo>::iterator it = _records.find(record);
+ if (it == _records.end()) {
+ warning("Can't find %s record data", record.c_str());
+ return "";
+ }
+
+ const RecordInfo &recordInfo = it->_value;
+ Common::File allDocsFile;
+
+ if (!allDocsFile.open(kAllDocsFile)) {
+ error("Can't open %s", kAllDocsFile);
+ }
+ allDocsFile.seek(recordInfo.position);
+
+ char *recordData = new char[recordInfo.size + 1];
+ allDocsFile.read(recordData, recordInfo.size);
+ recordData[recordInfo.size] = '\0';
+ char *recordDataEnd = recordData + recordInfo.size + 1;
+
+ const char *titleP = getRecordTitle(recordData, recordDataEnd);
+ /*debug("Title: %s", titleP);*/
+ title = titleP ? titleP : "";
+ const char *subtitleP = getRecordSubtitle(recordData, recordDataEnd);
+ /*debug("SubTitle: %s", subtitleP);*/
+ subtitle = subtitleP ? subtitleP : "";
+ const char *captionP = getRecordCaption(recordData, recordDataEnd);
+ /*debug("Caption: %s", captionP);*/
+ caption = captionP ? captionP : "";
+ getRecordHyperlinks(recordData, recordDataEnd, hyperlinks);
+
+ Common::String text(getDocTextAddress(recordData, recordDataEnd));
+
+ delete[] recordData;
+
+ return text;
+}
+
+void Versailles_Documentation::convertHyperlinks(const Common::StringArray &hyperlinks,
+ Common::Array<LinkInfo> &links) {
+ for (Common::StringArray::const_iterator it = hyperlinks.begin(); it != hyperlinks.end(); it++) {
+ LinkInfo link;
+ link.record = *it;
+ link.record.toUppercase();
+ link.title = getRecordTitle(link.record);
+ links.push_back(link);
+ }
+}
+
+void Versailles_Documentation::loadLinksFile() {
+ if (_linksData) {
+ return;
+ }
+
+ Common::File linksFile;
+ if (!linksFile.open(kLinksDocsFile)) {
+ error("Can't open links file: %s", kLinksDocsFile);
+ }
+
+ _linksSize = linksFile.size();
+ _linksData = new char[_linksSize + 1];
+
+ linksFile.read(_linksData, _linksSize);
+ _linksData[_linksSize] = '\0';
+}
+
+void Versailles_Documentation::getLinks(const Common::String &record,
+ Common::Array<LinkInfo> &links) {
+ loadLinksFile();
+
+ links.clear();
+
+ Common::String pattern = "\r";
+ pattern += record;
+
+ const char *recordStart = strstr(_linksData, pattern.c_str());
+ if (!recordStart) {
+ return;
+ }
+
+ const char *p = recordStart + pattern.size(); // Go beyond the record name
+ for (; *p != '\r' && *p != '\0'; p++) { } // Goto next line
+ if (!*p) {
+ return;
+ }
+ p++;
+
+ bool finished = false;
+ while (!finished) {
+ if (!scumm_strnicmp(p, "REM=", 4)) {
+ // Comment: goto next line
+ for (; *p != '\r' && *p != '\0'; p++) { }
+ } else if (!scumm_strnicmp(p, "LIEN=", 5)) {
+ // Link: read it
+ const char *linkStart = p + 5;
+ const char *linkEnd = linkStart;
+ for (; *linkEnd != '\r' && *linkEnd != ' ' && *linkEnd != '\0'; linkEnd++) { }
+ LinkInfo link;
+ link.record = Common::String(linkStart, linkEnd);
+ link.record.toUppercase();
+ link.title = getRecordTitle(link.record);
+ links.push_back(link);
+ // Advance to end of link and finish the line
+ p = linkEnd;
+ for (; *p != '\r' && *p != '\0'; p++) { }
+ //debug("Link %s/%s", link.record.c_str(), link.title.c_str());
+ } else {
+ // Something else: we expect a blank line to continue, else we are on a new record
+ for (; *p != '\r' && *p != '\0'; p++) {
+ if (*p != ' ' && *p != '\t' && *p != '\n') {
+ finished = true;
+ break;
+ }
+ }
+ }
+ if (*p == '\0') {
+ break;
+ }
+ p++;
+ }
+}
+
+} // End of namespace Versailles
+} // End of namespace CryOmni3D