/* 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/debug.h" #include "common/ustr.h" #include "sludge/allfiles.h" #include "sludge/moreio.h" #include "sludge/newfatal.h" #include "sludge/sludge.h" #include "sludge/version.h" namespace Sludge { ResourceManager::ResourceManager() { init(); } ResourceManager::~ResourceManager() { kill(); } void ResourceManager::init() { _sliceBusy = true; _bigDataFile = nullptr; _startOfDataIndex = 0; _startOfTextIndex = 0; _startOfSubIndex = 0; _startOfObjectIndex = 0; _startIndex = 0; _allResourceNames.clear(); } void ResourceManager::kill() { if (_bigDataFile) { delete _bigDataFile; _bigDataFile = nullptr; } _allResourceNames.clear(); } bool ResourceManager::openSubSlice(int num) { if (_sliceBusy) { fatal("Can't read from data file", "I'm already reading something"); return false; } _bigDataFile->seek(_startOfSubIndex + (num << 2), 0); _bigDataFile->seek(_bigDataFile->readUint32LE(), 0); return _sliceBusy = true; } bool ResourceManager::openObjectSlice(int num) { if (_sliceBusy) { fatal("Can't read from data file", "I'm already reading something"); return false; } _bigDataFile->seek(_startOfObjectIndex + (num << 2), 0); _bigDataFile->seek(_bigDataFile->readUint32LE(), 0); return _sliceBusy = true; } uint ResourceManager::openFileFromNum(int num) { if (_sliceBusy) { fatal("Can't read from data file", "I'm already reading something"); return 0; } _bigDataFile->seek(_startOfDataIndex + (num << 2), 0); _bigDataFile->seek(_bigDataFile->readUint32LE(), 1); _sliceBusy = true; return _bigDataFile->readUint32LE(); } uint32 ResourceManager::_cp1250ToUTF32[128] = { /* 0x80 */ 0x20ac, 0xfffd, 0x201a, 0xfffd, 0x201e, 0x2026, 0x2020, 0x2021, 0xfffd, 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179, /* 0x90 */ 0xfffd, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0xfffd, 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a, /* 0xa0 */ 0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b, /* 0xb0 */ 0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c, /* 0xc0 */ 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, /* 0xd0 */ 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, /* 0xe0 */ 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, /* 0xf0 */ 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, }; // Converts a string from ISO8859-2 or CP1250 to UTF8. // This is needed for old games. Common::String ResourceManager::convertString(const Common::String &s) { Common::String res; Common::U32String tmp; // Convert CP1250 to UTF32 for (uint i = 0; i < s.size(); ++i) { const byte c = s[i]; if (c < 0x80) { tmp += c; } else { uint32 utf32 = _cp1250ToUTF32[c - 0x80]; if (utf32) { tmp += utf32; } else { // It's an invalid CP1250 character... return s; } } } // Convert UTF32 to UTF8 for (uint i = 0; i < tmp.size(); ++i) { uint32 wc = tmp[i]; int count; if (wc < 0x80) count = 1; else if (wc < 0x800) count = 2; else if (wc < 0x10000) { if (wc < 0xd800 || wc >= 0xe000) { count = 3; } else { // It's an invalid UTF32 character... return s; } } else if (wc < 0x110000) { count = 4; } else { // It's an invalid UTF32 character... return s; } Common::String r = ""; switch (count) { case 4: r = (char)(0x80 | (wc & 0x3f)) + r; wc = wc >> 6; wc |= 0x10000; // falls through case 3: r = (char)(0x80 | (wc & 0x3f)) + r; wc = wc >> 6; wc |= 0x800; // falls through case 2: r = (char)(0x80 | (wc & 0x3f)) + r; wc = wc >> 6; wc |= 0xc0; // falls through case 1: r = wc + r; break; default: break; } res += r; } return res; } Common::String ResourceManager::getNumberedString(int value) { if (_sliceBusy) { fatal("Can't read from data file", "I'm already reading something"); return NULL; } _bigDataFile->seek((value << 2) + _startOfTextIndex, 0); value = _bigDataFile->readUint32LE(); _bigDataFile->seek(value, 0); Common::String s = readString(_bigDataFile); if (gameVersion < VERSION(2, 2)) { // This is an older game - We need to convert the string to UTF-8 s = convertString(s); } return s; } bool ResourceManager::startAccess() { int wasBusy = _sliceBusy; _sliceBusy = true; return wasBusy; } void ResourceManager::finishAccess() { _sliceBusy = false; } void ResourceManager::readResourceNames(Common::SeekableReadStream *readStream) { int numResourceNames = readStream->readUint16BE(); debugC(2, kSludgeDebugDataLoad, "numResourceNames %i", numResourceNames); _allResourceNames.reserve(numResourceNames); for (int fn = 0; fn < numResourceNames; fn++) { _allResourceNames.push_back(readString(readStream)); debugC(2, kSludgeDebugDataLoad, "Resource %i: %s", fn, _allResourceNames[fn].c_str()); } } const Common::String ResourceManager::resourceNameFromNum(int i) { if (i == -1) return ""; if (_allResourceNames.empty()) return "RESOURCE"; if (i < (int)_allResourceNames.size()) return _allResourceNames[i]; return "Unknown resource"; } void ResourceManager::setData(Common::File *fp) { _bigDataFile = fp; _startIndex = fp->pos(); } void ResourceManager::setFileIndices(uint numLanguages, uint skipBefore) { _bigDataFile->seek(_startIndex, SEEK_SET); _sliceBusy = false; if (skipBefore > numLanguages) { warning("Not a valid language ID! Using default instead."); skipBefore = 0; } // STRINGS int skipAfter = numLanguages - skipBefore; while (skipBefore) { _bigDataFile->seek(_bigDataFile->readUint32LE(), SEEK_SET); skipBefore--; } _startOfTextIndex = _bigDataFile->pos() + 4; debugC(2, kSludgeDebugDataLoad, "startOfTextIndex: %i", _startOfTextIndex); _bigDataFile->seek(_bigDataFile->readUint32LE(), SEEK_SET); while (skipAfter) { _bigDataFile->seek(_bigDataFile->readUint32LE(), SEEK_SET); skipAfter--; } _startOfSubIndex = _bigDataFile->pos() + 4; _bigDataFile->seek(_bigDataFile->readUint32LE(), SEEK_CUR); debugC(2, kSludgeDebugDataLoad, "startOfSubIndex: %i", _startOfSubIndex); _startOfObjectIndex = _bigDataFile->pos() + 4; _bigDataFile->seek(_bigDataFile->readUint32LE(), SEEK_CUR); debugC(2, kSludgeDebugDataLoad, "startOfObjectIndex: %i", _startOfObjectIndex); // Remember that the data section starts here _startOfDataIndex = _bigDataFile->pos(); debugC(2, kSludgeDebugDataLoad, "startOfDataIndex: %i", _startOfDataIndex); } } // End of namespace Sludge