/* 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 . * */ // Based on the Xentax Wiki documentation: // http://wiki.xentax.com/index.php?title=The_Longest_Journey_XARC #include "engines/stark/formats/xarc.h" #include "engines/stark/debug.h" #include "common/debug.h" #include "common/file.h" #include "common/substream.h" namespace Stark { namespace Formats { // ARCHIVE MEMBER class XARCMember : public Common::ArchiveMember { public: XARCMember(const XARCArchive *xarc, Common::ReadStream &stream, uint32 offset); Common::SeekableReadStream *createReadStream() const override; Common::SeekableReadStream *createReadStreamForAltStream(Common::AltStreamType altStreamType) const override; Common::String getName() const override { return _name.baseName(); } Common::Path getPathInArchive() const override { return _name; } Common::String getFileName() const override { return _name.baseName(); } uint32 getLength() const { return _length; } uint32 getOffset() const { return _offset; } private: const XARCArchive *_xarc; Common::Path _name; uint32 _offset; uint32 _length; // Helper function Common::String readString(Common::ReadStream &stream); }; XARCMember::XARCMember(const XARCArchive *xarc, Common::ReadStream &stream, uint32 offset) { _xarc = xarc; // Read the information about this archive member _name = Common::Path(readString(stream)); _offset = offset; _length = stream.readUint32LE(); debugC(20, kDebugArchive, "Stark::XARC Member: \"%s\" starts at offset=%d and has length=%d", _name.toString().c_str(), _offset, _length); // Unknown value. English: 0, others: 1 uint32 unknown = stream.readUint32LE(); debugC(kDebugUnknown, "Stark::XARC Member: \"%s\" has unknown=%d", _name.toString().c_str(), unknown); if (unknown != 0 && unknown != 1) { warning("Stark::XARC Member: \"%s\" has unknown=%d with unknown meaning", _name.toString().c_str(), unknown); } } Common::SeekableReadStream *XARCMember::createReadStream() const { return _xarc->createReadStreamForMember(this); } Common::SeekableReadStream *XARCMember::createReadStreamForAltStream(Common::AltStreamType altStreamType) const { return nullptr; } Common::String XARCMember::readString(Common::ReadStream &stream) { Common::String str; // Read until we find a 0 char c = 1; while (c != 0) { c = stream.readByte(); if (stream.eos()) { c = 0; } if (c != 0) { str += c; } } return str; } // ARCHIVE bool XARCArchive::open(const Common::Path &filename) { Common::File stream; if (!stream.open(filename)) { return false; } _filename = filename; // Unknown: always 1? version? uint32 unknown = stream.readUint32LE(); debugC(kDebugUnknown, "Stark::XARC: \"%s\" has unknown=%d", _filename.toString(Common::Path::kNativeSeparator).c_str(), unknown); if (unknown != 1) { warning("Stark::XARC: \"%s\" has unknown=%d with unknown meaning", _filename.toString(Common::Path::kNativeSeparator).c_str(), unknown); } // Read the number of contained files uint32 numFiles = stream.readUint32LE(); debugC(20, kDebugArchive, "Stark::XARC: \"%s\" contains %d files", _filename.toString(Common::Path::kNativeSeparator).c_str(), numFiles); // Read the offset to the contents of the first file uint32 offset = stream.readUint32LE(); debugC(20, kDebugArchive, "Stark::XARC: \"%s\"'s first file has offset=%d", _filename.toString(Common::Path::kNativeSeparator).c_str(), offset); for (uint32 i = 0; i < numFiles; i++) { XARCMember *member = new XARCMember(this, stream, offset); _members.push_back(Common::ArchiveMemberPtr(member)); // Set the offset to the next member offset += member->getLength(); } return true; } Common::Path XARCArchive::getFilename() const { return _filename; } bool XARCArchive::hasFile(const Common::Path &path) const { Common::String name = path.toString(); for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) { if ((*it)->getName() == name) { // Found it return true; } } // Not found return false; } int XARCArchive::listMatchingMembers(Common::ArchiveMemberList &list, const Common::Path &pattern, bool matchPathComponents) const { Common::String patternString = pattern.toString(); int matches = 0; for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) { if ((*it)->getName().matchString(patternString)) { // This file matches, add it list.push_back(*it); matches++; } } return matches; } int XARCArchive::listMembers(Common::ArchiveMemberList &list) const { int files = 0; for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) { // Add all the members to the list list.push_back(*it); files++; } return files; } const Common::ArchiveMemberPtr XARCArchive::getMember(const Common::Path &path) const { Common::String name = path.toString(); for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) { if ((*it)->getName() == name) { // Found it return *it; } } // Not found, return an empty ptr return Common::ArchiveMemberPtr(); } Common::SeekableReadStream *XARCArchive::createReadStreamForMember(const Common::Path &path) const { Common::String name = path.toString(); for (Common::ArchiveMemberList::const_iterator it = _members.begin(); it != _members.end(); ++it) { if ((*it)->getName() == name) { // Found it return createReadStreamForMember((const XARCMember *)it->get()); } } // Not found return 0; } Common::SeekableReadStream *XARCArchive::createReadStreamForMember(const XARCMember *member) const { // Open the xarc file Common::File *f = new Common::File; if (!f) return NULL; if (!f->open(_filename)) { delete f; return NULL; } // Return the substream that contains the archive member uint32 offset = member->getOffset(); uint32 length = member->getLength(); return new Common::SeekableSubReadStream(f, offset, offset + length, DisposeAfterUse::YES); // Different approach: keep the archive open and read full resources to memory //f.seek(member->getOffset()); //return f.readStream(member->getLength()); } } // End of namespace Formats } // End of namespace Stark