ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

macresman.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/scummsys.h"
00024 #include "common/debug.h"
00025 #include "common/util.h"
00026 #include "common/file.h"
00027 #include "common/fs.h"
00028 #include "common/macresman.h"
00029 #include "common/md5.h"
00030 #include "common/substream.h"
00031 #include "common/textconsole.h"
00032 #include "common/archive.h"
00033 
00034 #ifdef MACOSX
00035 #include "common/config-manager.h"
00036 #endif
00037 
00038 namespace Common {
00039 
00040 #define MBI_INFOHDR 128
00041 #define MBI_ZERO1 0
00042 #define MBI_NAMELEN 1
00043 #define MBI_ZERO2 74
00044 #define MBI_ZERO3 82
00045 #define MBI_DFLEN 83
00046 #define MBI_RFLEN 87
00047 #define MAXNAMELEN 63
00048 
00049 MacResManager::MacResManager() {
00050     memset(this, 0, sizeof(MacResManager));
00051     close();
00052 }
00053 
00054 MacResManager::~MacResManager() {
00055     close();
00056 }
00057 
00058 void MacResManager::close() {
00059     _resForkOffset = -1;
00060     _mode = kResForkNone;
00061 
00062     for (int i = 0; i < _resMap.numTypes; i++) {
00063         for (int j = 0; j < _resTypes[i].items; j++)
00064             if (_resLists[i][j].nameOffset != -1)
00065                 delete[] _resLists[i][j].name;
00066 
00067         delete[] _resLists[i];
00068     }
00069 
00070     delete[] _resLists; _resLists = nullptr;
00071     delete[] _resTypes; _resTypes = nullptr;
00072     delete _stream; _stream = nullptr;
00073     _resMap.numTypes = 0;
00074 }
00075 
00076 bool MacResManager::hasDataFork() const {
00077     return !_baseFileName.empty();
00078 }
00079 
00080 bool MacResManager::hasResFork() const {
00081     return !_baseFileName.empty() && _mode != kResForkNone;
00082 }
00083 
00084 uint32 MacResManager::getResForkDataSize() const {
00085     if (!hasResFork())
00086         return 0;
00087 
00088     _stream->seek(_resForkOffset + 4);
00089     return _stream->readUint32BE();
00090 }
00091 
00092 String MacResManager::computeResForkMD5AsString(uint32 length) const {
00093     if (!hasResFork())
00094         return String();
00095 
00096     _stream->seek(_resForkOffset);
00097     uint32 dataOffset = _stream->readUint32BE() + _resForkOffset;
00098     /* uint32 mapOffset = */ _stream->readUint32BE();
00099     uint32 dataLength = _stream->readUint32BE();
00100 
00101 
00102     SeekableSubReadStream resForkStream(_stream, dataOffset, dataOffset + dataLength);
00103     return computeStreamMD5AsString(resForkStream, MIN<uint32>(length, _resForkSize));
00104 }
00105 
00106 bool MacResManager::open(const String &fileName) {
00107     close();
00108 
00109 #ifdef MACOSX
00110     // Check the actual fork on a Mac computer
00111     String fullPath = ConfMan.get("path") + "/" + fileName + "/..namedfork/rsrc";
00112     FSNode resFsNode = FSNode(fullPath);
00113     if (resFsNode.exists()) {
00114         SeekableReadStream *macResForkRawStream = resFsNode.createReadStream();
00115 
00116         if (macResForkRawStream && loadFromRawFork(*macResForkRawStream)) {
00117             _baseFileName = fileName;
00118             return true;
00119         }
00120 
00121         delete macResForkRawStream;
00122     }
00123 #endif
00124 
00125     File *file = new File();
00126 
00127     // Prefer standalone files first, starting with raw forks
00128     if (file->open(fileName + ".rsrc") && loadFromRawFork(*file)) {
00129         _baseFileName = fileName;
00130         return true;
00131     }
00132     file->close();
00133 
00134     // Then try for AppleDouble using Apple's naming
00135     if (file->open(constructAppleDoubleName(fileName)) && loadFromAppleDouble(*file)) {
00136         _baseFileName = fileName;
00137         return true;
00138     }
00139     file->close();
00140 
00141     // Check .bin for MacBinary next
00142     if (file->open(fileName + ".bin") && loadFromMacBinary(*file)) {
00143         _baseFileName = fileName;
00144         return true;
00145     }
00146     file->close();
00147 
00148     // As a last resort, see if just the data fork exists
00149     if (file->open(fileName)) {
00150         _baseFileName = fileName;
00151 
00152         // FIXME: Is this really needed?
00153         if (isMacBinary(*file)) {
00154             file->seek(0);
00155             if (loadFromMacBinary(*file))
00156                 return true;
00157         }
00158 
00159         file->seek(0);
00160         _stream = file;
00161         return true;
00162     }
00163 
00164     delete file;
00165 
00166     // The file doesn't exist
00167     return false;
00168 }
00169 
00170 bool MacResManager::open(const FSNode &path, const String &fileName) {
00171     close();
00172 
00173 #ifdef MACOSX
00174     // Check the actual fork on a Mac computer
00175     String fullPath = path.getPath() + "/" + fileName + "/..namedfork/rsrc";
00176     FSNode resFsNode = FSNode(fullPath);
00177     if (resFsNode.exists()) {
00178         SeekableReadStream *macResForkRawStream = resFsNode.createReadStream();
00179 
00180         if (macResForkRawStream && loadFromRawFork(*macResForkRawStream)) {
00181             _baseFileName = fileName;
00182             return true;
00183         }
00184 
00185         delete macResForkRawStream;
00186     }
00187 #endif
00188 
00189     // Prefer standalone files first, starting with raw forks
00190     FSNode fsNode = path.getChild(fileName + ".rsrc");
00191     if (fsNode.exists() && !fsNode.isDirectory()) {
00192         SeekableReadStream *stream = fsNode.createReadStream();
00193         if (loadFromRawFork(*stream)) {
00194             _baseFileName = fileName;
00195             return true;
00196         }
00197         delete stream;
00198     }
00199 
00200     // Then try for AppleDouble using Apple's naming
00201     fsNode = path.getChild(constructAppleDoubleName(fileName));
00202     if (fsNode.exists() && !fsNode.isDirectory()) {
00203         SeekableReadStream *stream = fsNode.createReadStream();
00204         if (loadFromAppleDouble(*stream)) {
00205             _baseFileName = fileName;
00206             return true;
00207         }
00208         delete stream;
00209     }
00210 
00211     // Check .bin for MacBinary next
00212     fsNode = path.getChild(fileName + ".bin");
00213     if (fsNode.exists() && !fsNode.isDirectory()) {
00214         SeekableReadStream *stream = fsNode.createReadStream();
00215         if (loadFromMacBinary(*stream)) {
00216             _baseFileName = fileName;
00217             return true;
00218         }
00219         delete stream;
00220     }
00221 
00222     // As a last resort, see if just the data fork exists
00223     fsNode = path.getChild(fileName);
00224     if (fsNode.exists() && !fsNode.isDirectory()) {
00225         SeekableReadStream *stream = fsNode.createReadStream();
00226         _baseFileName = fileName;
00227 
00228         // FIXME: Is this really needed?
00229         if (isMacBinary(*stream)) {
00230             stream->seek(0);
00231             if (loadFromMacBinary(*stream))
00232                 return true;
00233         }
00234 
00235         stream->seek(0);
00236         _stream = stream;
00237         return true;
00238     }
00239 
00240     // The file doesn't exist
00241     return false;
00242 }
00243 
00244 bool MacResManager::exists(const String &fileName) {
00245     // Try the file name by itself
00246     if (File::exists(fileName))
00247         return true;
00248 
00249     // Try the .rsrc extension
00250     if (File::exists(fileName + ".rsrc"))
00251         return true;
00252 
00253     // Check if we have a MacBinary file
00254     File tempFile;
00255     if (tempFile.open(fileName + ".bin") && isMacBinary(tempFile))
00256         return true;
00257 
00258     // Check if we have an AppleDouble file
00259     if (tempFile.open(constructAppleDoubleName(fileName)) && tempFile.readUint32BE() == 0x00051607)
00260         return true;
00261 
00262     return false;
00263 }
00264 
00265 void MacResManager::listFiles(StringArray &files, const String &pattern) {
00266     // Base names discovered so far.
00267     typedef HashMap<String, bool, IgnoreCase_Hash, IgnoreCase_EqualTo> BaseNameSet;
00268     BaseNameSet baseNames;
00269 
00270     // List files itself.
00271     ArchiveMemberList memberList;
00272     SearchMan.listMatchingMembers(memberList, pattern);
00273     SearchMan.listMatchingMembers(memberList, pattern + ".rsrc");
00274     SearchMan.listMatchingMembers(memberList, pattern + ".bin");
00275     SearchMan.listMatchingMembers(memberList, constructAppleDoubleName(pattern));
00276 
00277     for (ArchiveMemberList::const_iterator i = memberList.begin(), end = memberList.end(); i != end; ++i) {
00278         String filename = (*i)->getName();
00279 
00280         // For raw resource forks and MacBinary files we strip the extension
00281         // here to obtain a valid base name.
00282         int lastDotPos = filename.size() - 1;
00283         for (; lastDotPos >= 0; --lastDotPos) {
00284             if (filename[lastDotPos] == '.') {
00285                 break;
00286             }
00287         }
00288 
00289         if (lastDotPos != -1) {
00290             const char *extension = filename.c_str() + lastDotPos + 1;
00291             bool removeExtension = false;
00292 
00293             // TODO: Should we really keep filenames suggesting raw resource
00294             // forks or MacBinary files but not being such around? This might
00295             // depend on the pattern the client requests...
00296             if (!scumm_stricmp(extension, "rsrc")) {
00297                 SeekableReadStream *stream = (*i)->createReadStream();
00298                 removeExtension = stream && isRawFork(*stream);
00299                 delete stream;
00300             } else if (!scumm_stricmp(extension, "bin")) {
00301                 SeekableReadStream *stream = (*i)->createReadStream();
00302                 removeExtension = stream && isMacBinary(*stream);
00303                 delete stream;
00304             }
00305 
00306             if (removeExtension) {
00307                 filename.erase(lastDotPos);
00308             }
00309         }
00310 
00311         // Strip AppleDouble '._' prefix if applicable.
00312         bool isAppleDoubleName = false;
00313         const String filenameAppleDoubleStripped = disassembleAppleDoubleName(filename, &isAppleDoubleName);
00314 
00315         if (isAppleDoubleName) {
00316             SeekableReadStream *stream = (*i)->createReadStream();
00317             if (stream->readUint32BE() == 0x00051607) {
00318                 filename = filenameAppleDoubleStripped;
00319             }
00320             // TODO: Should we really keep filenames suggesting AppleDouble
00321             // but not being AppleDouble around? This might depend on the
00322             // pattern the client requests...
00323             delete stream;
00324         }
00325 
00326         baseNames[filename] = true;
00327     }
00328 
00329     // Append resulting base names to list to indicate found files.
00330     for (BaseNameSet::const_iterator i = baseNames.begin(), end = baseNames.end(); i != end; ++i) {
00331         files.push_back(i->_key);
00332     }
00333 }
00334 
00335 bool MacResManager::loadFromAppleDouble(SeekableReadStream &stream) {
00336     if (stream.readUint32BE() != 0x00051607) // tag
00337         return false;
00338 
00339     stream.skip(20); // version + home file system
00340 
00341     uint16 entryCount = stream.readUint16BE();
00342 
00343     for (uint16 i = 0; i < entryCount; i++) {
00344         uint32 id = stream.readUint32BE();
00345         uint32 offset = stream.readUint32BE();
00346         uint32 length = stream.readUint32BE(); // length
00347 
00348         if (id == 2) {
00349             // Found the resource fork!
00350             _resForkOffset = offset;
00351             _mode = kResForkAppleDouble;
00352             _resForkSize = length;
00353             return load(stream);
00354         }
00355     }
00356 
00357     return false;
00358 }
00359 
00360 bool MacResManager::isMacBinary(SeekableReadStream &stream) {
00361     byte infoHeader[MBI_INFOHDR];
00362     int resForkOffset = -1;
00363 
00364     stream.read(infoHeader, MBI_INFOHDR);
00365 
00366     if (infoHeader[MBI_ZERO1] == 0 && infoHeader[MBI_ZERO2] == 0 &&
00367         infoHeader[MBI_ZERO3] == 0 && infoHeader[MBI_NAMELEN] <= MAXNAMELEN) {
00368 
00369         // Pull out fork lengths
00370         uint32 dataSize = READ_BE_UINT32(infoHeader + MBI_DFLEN);
00371         uint32 rsrcSize = READ_BE_UINT32(infoHeader + MBI_RFLEN);
00372 
00373         uint32 dataSizePad = (((dataSize + 127) >> 7) << 7);
00374         uint32 rsrcSizePad = (((rsrcSize + 127) >> 7) << 7);
00375 
00376         // Length check
00377         if (MBI_INFOHDR + dataSizePad + rsrcSizePad == (uint32)stream.size()) {
00378             resForkOffset = MBI_INFOHDR + dataSizePad;
00379         }
00380     }
00381 
00382     if (resForkOffset < 0)
00383         return false;
00384 
00385     return true;
00386 }
00387 
00388 bool MacResManager::isRawFork(SeekableReadStream &stream) {
00389     // TODO: Is there a better way to detect whether this is a raw fork?
00390     const uint32 dataOffset = stream.readUint32BE();
00391     const uint32 mapOffset = stream.readUint32BE();
00392     const uint32 dataLength = stream.readUint32BE();
00393     const uint32 mapLength = stream.readUint32BE();
00394 
00395     return    !stream.eos() && !stream.err()
00396            && dataOffset < (uint32)stream.size() && dataOffset + dataLength <= (uint32)stream.size()
00397            && mapOffset < (uint32)stream.size() && mapOffset + mapLength <= (uint32)stream.size();
00398 }
00399 
00400 bool MacResManager::loadFromMacBinary(SeekableReadStream &stream) {
00401     byte infoHeader[MBI_INFOHDR];
00402     stream.read(infoHeader, MBI_INFOHDR);
00403 
00404     // Maybe we have MacBinary?
00405     if (infoHeader[MBI_ZERO1] == 0 && infoHeader[MBI_ZERO2] == 0 &&
00406         infoHeader[MBI_ZERO3] == 0 && infoHeader[MBI_NAMELEN] <= MAXNAMELEN) {
00407 
00408         // Pull out fork lengths
00409         uint32 dataSize = READ_BE_UINT32(infoHeader + MBI_DFLEN);
00410         uint32 rsrcSize = READ_BE_UINT32(infoHeader + MBI_RFLEN);
00411 
00412         uint32 dataSizePad = (((dataSize + 127) >> 7) << 7);
00413         uint32 rsrcSizePad = (((rsrcSize + 127) >> 7) << 7);
00414 
00415         // Length check
00416         if (MBI_INFOHDR + dataSizePad + rsrcSizePad == (uint32)stream.size()) {
00417             _resForkOffset = MBI_INFOHDR + dataSizePad;
00418             _resForkSize = rsrcSize;
00419         }
00420     }
00421 
00422     if (_resForkOffset < 0)
00423         return false;
00424 
00425     _mode = kResForkMacBinary;
00426     return load(stream);
00427 }
00428 
00429 bool MacResManager::loadFromRawFork(SeekableReadStream &stream) {
00430     _mode = kResForkRaw;
00431     _resForkOffset = 0;
00432     _resForkSize = stream.size();
00433     return load(stream);
00434 }
00435 
00436 bool MacResManager::load(SeekableReadStream &stream) {
00437     if (_mode == kResForkNone)
00438         return false;
00439 
00440     stream.seek(_resForkOffset);
00441 
00442     _dataOffset = stream.readUint32BE() + _resForkOffset;
00443     _mapOffset = stream.readUint32BE() + _resForkOffset;
00444     _dataLength = stream.readUint32BE();
00445     _mapLength = stream.readUint32BE();
00446 
00447     // do sanity check
00448     if (stream.eos() || _dataOffset >= (uint32)stream.size() || _mapOffset >= (uint32)stream.size() ||
00449             _dataLength + _mapLength  > (uint32)stream.size()) {
00450         _resForkOffset = -1;
00451         _mode = kResForkNone;
00452         return false;
00453     }
00454 
00455     debug(7, "got header: data %d [%d] map %d [%d]",
00456         _dataOffset, _dataLength, _mapOffset, _mapLength);
00457 
00458     _stream = &stream;
00459 
00460     readMap();
00461     return true;
00462 }
00463 
00464 SeekableReadStream *MacResManager::getDataFork() {
00465     if (!_stream)
00466         return nullptr;
00467 
00468     if (_mode == kResForkMacBinary) {
00469         _stream->seek(MBI_DFLEN);
00470         uint32 dataSize = _stream->readUint32BE();
00471         return new SeekableSubReadStream(_stream, MBI_INFOHDR, MBI_INFOHDR + dataSize);
00472     }
00473 
00474     File *file = new File();
00475     if (file->open(_baseFileName))
00476         return file;
00477     delete file;
00478 
00479     return nullptr;
00480 }
00481 
00482 MacResIDArray MacResManager::getResIDArray(uint32 typeID) {
00483     int typeNum = -1;
00484     MacResIDArray res;
00485 
00486     for (int i = 0; i < _resMap.numTypes; i++)
00487         if (_resTypes[i].id == typeID) {
00488             typeNum = i;
00489             break;
00490         }
00491 
00492     if (typeNum == -1)
00493         return res;
00494 
00495     res.resize(_resTypes[typeNum].items);
00496 
00497     for (int i = 0; i < _resTypes[typeNum].items; i++)
00498         res[i] = _resLists[typeNum][i].id;
00499 
00500     return res;
00501 }
00502 
00503 MacResTagArray MacResManager::getResTagArray() {
00504     MacResTagArray tagArray;
00505 
00506     if (!hasResFork())
00507         return tagArray;
00508 
00509     tagArray.resize(_resMap.numTypes);
00510 
00511     for (uint32 i = 0; i < _resMap.numTypes; i++)
00512         tagArray[i] = _resTypes[i].id;
00513 
00514     return tagArray;
00515 }
00516 
00517 String MacResManager::getResName(uint32 typeID, uint16 resID) const {
00518     int typeNum = -1;
00519 
00520     for (int i = 0; i < _resMap.numTypes; i++)
00521         if (_resTypes[i].id == typeID) {
00522             typeNum = i;
00523             break;
00524         }
00525 
00526     if (typeNum == -1)
00527         return "";
00528 
00529     for (int i = 0; i < _resTypes[typeNum].items; i++)
00530         if (_resLists[typeNum][i].id == resID)
00531             return _resLists[typeNum][i].name;
00532 
00533     return "";
00534 }
00535 
00536 SeekableReadStream *MacResManager::getResource(uint32 typeID, uint16 resID) {
00537     int typeNum = -1;
00538     int resNum = -1;
00539 
00540     for (int i = 0; i < _resMap.numTypes; i++)
00541         if (_resTypes[i].id == typeID) {
00542             typeNum = i;
00543             break;
00544         }
00545 
00546     if (typeNum == -1)
00547         return nullptr;
00548 
00549     for (int i = 0; i < _resTypes[typeNum].items; i++)
00550         if (_resLists[typeNum][i].id == resID) {
00551             resNum = i;
00552             break;
00553         }
00554 
00555     if (resNum == -1)
00556         return nullptr;
00557 
00558     _stream->seek(_dataOffset + _resLists[typeNum][resNum].dataOffset);
00559     uint32 len = _stream->readUint32BE();
00560 
00561     // Ignore resources with 0 length
00562     if (!len)
00563         return nullptr;
00564 
00565     return _stream->readStream(len);
00566 }
00567 
00568 SeekableReadStream *MacResManager::getResource(const String &fileName) {
00569     for (uint32 i = 0; i < _resMap.numTypes; i++) {
00570         for (uint32 j = 0; j < _resTypes[i].items; j++) {
00571             if (_resLists[i][j].nameOffset != -1 && fileName.equalsIgnoreCase(_resLists[i][j].name)) {
00572                 _stream->seek(_dataOffset + _resLists[i][j].dataOffset);
00573                 uint32 len = _stream->readUint32BE();
00574 
00575                 // Ignore resources with 0 length
00576                 if (!len)
00577                     return nullptr;
00578 
00579                 return _stream->readStream(len);
00580             }
00581         }
00582     }
00583 
00584     return nullptr;
00585 }
00586 
00587 SeekableReadStream *MacResManager::getResource(uint32 typeID, const String &fileName) {
00588     for (uint32 i = 0; i < _resMap.numTypes; i++) {
00589         if (_resTypes[i].id != typeID)
00590             continue;
00591 
00592         for (uint32 j = 0; j < _resTypes[i].items; j++) {
00593             if (_resLists[i][j].nameOffset != -1 && fileName.equalsIgnoreCase(_resLists[i][j].name)) {
00594                 _stream->seek(_dataOffset + _resLists[i][j].dataOffset);
00595                 uint32 len = _stream->readUint32BE();
00596 
00597                 // Ignore resources with 0 length
00598                 if (!len)
00599                     return nullptr;
00600 
00601                 return _stream->readStream(len);
00602             }
00603         }
00604     }
00605 
00606     return nullptr;
00607 }
00608 
00609 void MacResManager::readMap() {
00610     _stream->seek(_mapOffset + 22);
00611 
00612     _resMap.resAttr = _stream->readUint16BE();
00613     _resMap.typeOffset = _stream->readUint16BE();
00614     _resMap.nameOffset = _stream->readUint16BE();
00615     _resMap.numTypes = _stream->readUint16BE();
00616     _resMap.numTypes++;
00617 
00618     _stream->seek(_mapOffset + _resMap.typeOffset + 2);
00619     _resTypes = new ResType[_resMap.numTypes];
00620 
00621     for (int i = 0; i < _resMap.numTypes; i++) {
00622         _resTypes[i].id = _stream->readUint32BE();
00623         _resTypes[i].items = _stream->readUint16BE();
00624         _resTypes[i].offset = _stream->readUint16BE();
00625         _resTypes[i].items++;
00626 
00627         debug(8, "resType: <%s> items: %d offset: %d (0x%x)", tag2str(_resTypes[i].id), _resTypes[i].items,  _resTypes[i].offset, _resTypes[i].offset);
00628     }
00629 
00630     _resLists = new ResPtr[_resMap.numTypes];
00631 
00632     for (int i = 0; i < _resMap.numTypes; i++) {
00633         _resLists[i] = new Resource[_resTypes[i].items];
00634         _stream->seek(_resTypes[i].offset + _mapOffset + _resMap.typeOffset);
00635 
00636         for (int j = 0; j < _resTypes[i].items; j++) {
00637             ResPtr resPtr = _resLists[i] + j;
00638 
00639             resPtr->id = _stream->readUint16BE();
00640             resPtr->nameOffset = _stream->readUint16BE();
00641             resPtr->dataOffset = _stream->readUint32BE();
00642             _stream->readUint32BE();
00643             resPtr->name = nullptr;
00644 
00645             resPtr->attr = resPtr->dataOffset >> 24;
00646             resPtr->dataOffset &= 0xFFFFFF;
00647         }
00648 
00649         for (int j = 0; j < _resTypes[i].items; j++) {
00650             if (_resLists[i][j].nameOffset != -1) {
00651                 _stream->seek(_resLists[i][j].nameOffset + _mapOffset + _resMap.nameOffset);
00652 
00653                 byte len = _stream->readByte();
00654                 _resLists[i][j].name = new char[len + 1];
00655                 _resLists[i][j].name[len] = 0;
00656                 _stream->read(_resLists[i][j].name, len);
00657             }
00658         }
00659     }
00660 }
00661 
00662 String MacResManager::constructAppleDoubleName(String name) {
00663     // Insert "._" before the last portion of a path name
00664     for (int i = name.size() - 1; i >= 0; i--) {
00665         if (i == 0) {
00666             name.insertChar('_', 0);
00667             name.insertChar('.', 0);
00668         } else if (name[i] == '/') {
00669             name.insertChar('_', i + 1);
00670             name.insertChar('.', i + 1);
00671             break;
00672         }
00673     }
00674 
00675     return name;
00676 }
00677 
00678 String MacResManager::disassembleAppleDoubleName(String name, bool *isAppleDouble) {
00679     if (isAppleDouble) {
00680         *isAppleDouble = false;
00681     }
00682 
00683     // Remove "._" before the last portion of a path name.
00684     for (int i = name.size() - 1; i >= 0; --i) {
00685         if (i == 0) {
00686             if (name.size() > 2 && name[0] == '.' && name[1] == '_') {
00687                 name.erase(0, 2);
00688                 if (isAppleDouble) {
00689                     *isAppleDouble = true;
00690                 }
00691             }
00692         } else if (name[i] == '/') {
00693             if ((uint)(i + 2) < name.size() && name[i + 1] == '.' && name[i + 2] == '_') {
00694                 name.erase(i + 1, 2);
00695                 if (isAppleDouble) {
00696                     *isAppleDouble = true;
00697                 }
00698             }
00699             break;
00700         }
00701     }
00702 
00703     return name;
00704 }
00705 
00706 } // End of namespace Common


Generated on Sat Apr 20 2019 05:03:10 for ResidualVM by doxygen 1.7.1
curved edge   curved edge