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

recorderfile.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/system.h"
00024 #include "gui/EventRecorder.h"
00025 #include "common/md5.h"
00026 #include "common/recorderfile.h"
00027 #include "common/savefile.h"
00028 #include "common/bufferedstream.h"
00029 #include "graphics/thumbnail.h"
00030 #include "graphics/surface.h"
00031 #include "graphics/scaler.h"
00032 
00033 #define RECORD_VERSION 1
00034 
00035 namespace Common {
00036 
00037 PlaybackFile::PlaybackFile() : _tmpRecordFile(_tmpBuffer, kRecordBuffSize), _tmpPlaybackFile(_tmpBuffer, kRecordBuffSize) {
00038     _readStream = NULL;
00039     _writeStream = NULL;
00040     _screenshotsFile = NULL;
00041     _mode = kClosed;
00042 
00043     _recordFile = 0;
00044     _headerDumped = false;
00045     _recordCount = 0;
00046     _eventsSize = 0;
00047     memset(_tmpBuffer, 1, kRecordBuffSize);
00048 
00049     _playbackParseState = kFileStateCheckFormat;
00050 }
00051 
00052 PlaybackFile::~PlaybackFile() {
00053     close();
00054 }
00055 
00056 bool PlaybackFile::openWrite(const String &fileName) {
00057     close();
00058     _header.fileName = fileName;
00059     _writeStream = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving(fileName), 128 * 1024);
00060     _headerDumped = false;
00061     _recordCount = 0;
00062     if (_writeStream == NULL) {
00063         return false;
00064     }
00065     _mode = kWrite;
00066     return true;
00067 }
00068 
00069 bool PlaybackFile::openRead(const String &fileName) {
00070     close();
00071     _header.fileName = fileName;
00072     _eventsSize = 0;
00073     _tmpPlaybackFile.seek(0);
00074     _readStream = wrapBufferedSeekableReadStream(g_system->getSavefileManager()->openForLoading(fileName), 128 * 1024, DisposeAfterUse::YES);
00075     if (_readStream == NULL) {
00076         debugC(1, kDebugLevelEventRec, "playback:action=\"Load File\" result=fail reason=\"file %s not found\"", fileName.c_str());
00077         return false;
00078     }
00079     if (!parseHeader()) {
00080         debugC(1, kDebugLevelEventRec, "playback:action=\"Load File\" result=fail reason=\"header parsing failed\"");
00081         return false;
00082     }
00083     _screenshotsFile = wrapBufferedWriteStream(g_system->getSavefileManager()->openForSaving("screenshots.bin"), 128 * 1024);
00084     debugC(1, kDebugLevelEventRec, "playback:action=\"Load File\" result=success");
00085     _mode = kRead;
00086     return true;
00087 }
00088 
00089 void PlaybackFile::close() {
00090     delete _readStream;
00091     _readStream = NULL;
00092     if (_writeStream != NULL) {
00093         dumpRecordsToFile();
00094         _writeStream->finalize();
00095         delete _writeStream;
00096         _writeStream = NULL;
00097         updateHeader();
00098     }
00099     if (_screenshotsFile != NULL) {
00100         _screenshotsFile->finalize();
00101         delete _screenshotsFile;
00102         _screenshotsFile = NULL;
00103     }
00104     for (HashMap<String, SaveFileBuffer>::iterator  i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) {
00105         free(i->_value.buffer);
00106     }
00107     _header.saveFiles.clear();
00108     _mode = kClosed;
00109 }
00110 
00111 bool PlaybackFile::parseHeader() {
00112     PlaybackFileHeader result;
00113     ChunkHeader nextChunk;
00114     _playbackParseState = kFileStateCheckFormat;
00115     if (!readChunkHeader(nextChunk)) {
00116         _playbackParseState = kFileStateError;
00117         return false;
00118     }
00119     while ((_playbackParseState != kFileStateDone) && (_playbackParseState != kFileStateError)) {
00120         if (processChunk(nextChunk)) {
00121             if (!readChunkHeader(nextChunk)) {
00122                 warning("Error in header parsing");
00123                 _playbackParseState = kFileStateError;
00124             }
00125         }
00126     }
00127     return _playbackParseState == kFileStateDone;
00128 }
00129 
00130 bool PlaybackFile::checkPlaybackFileVersion() {
00131     uint32 version;
00132     version = _readStream->readUint32LE();
00133     if (version != RECORD_VERSION) {
00134         warning("Incorrect playback file version. Expected version %d, but got %d.", RECORD_VERSION, version);
00135         return false;
00136     }
00137     return true;
00138 }
00139 
00140 
00141 String PlaybackFile::readString(int len) {
00142     String result;
00143     char buf[50];
00144     int readSize = 49;
00145     while (len > 0) {
00146         if (len <= 49) {
00147             readSize = len;
00148         }
00149         _readStream->read(buf, readSize);
00150         buf[readSize] = 0;
00151         result += buf;
00152         len -= readSize;
00153     }
00154     return result;
00155 }
00156 
00157 bool PlaybackFile::readChunkHeader(PlaybackFile::ChunkHeader &nextChunk) {
00158     nextChunk.id = (FileTag)_readStream->readUint32LE();
00159     nextChunk.len = _readStream->readUint32LE();
00160     return !_readStream->err() && !_readStream->eos();
00161 }
00162 
00163 bool PlaybackFile::processChunk(ChunkHeader &nextChunk) {
00164     switch (_playbackParseState) {
00165     case kFileStateCheckFormat:
00166         if (nextChunk.id == kFormatIdTag) {
00167             _playbackParseState = kFileStateCheckVersion;
00168         } else {
00169             warning("Unknown playback file signature");
00170             _playbackParseState = kFileStateError;
00171         }
00172         break;
00173     case kFileStateCheckVersion:
00174         if ((nextChunk.id == kVersionTag) && checkPlaybackFileVersion()) {
00175             _playbackParseState = kFileStateSelectSection;
00176         } else {
00177             _playbackParseState = kFileStateError;
00178         }
00179         break;
00180     case kFileStateSelectSection:
00181         switch (nextChunk.id) {
00182         case kHeaderSectionTag:
00183             _playbackParseState = kFileStateProcessHeader;
00184             break;
00185         case kHashSectionTag:
00186             _playbackParseState = kFileStateProcessHash;
00187             break;
00188         case kRandomSectionTag:
00189             _playbackParseState = kFileStateProcessRandom;
00190             break;
00191         case kEventTag:
00192         case kScreenShotTag:
00193             _readStream->seek(-8, SEEK_CUR);
00194             _playbackParseState = kFileStateDone;
00195             return false;
00196         case kSaveTag:
00197             _playbackParseState = kFileStateProcessSave;
00198             break;
00199         case kSettingsSectionTag:
00200             _playbackParseState = kFileStateProcessSettings;
00201             warning("Loading record header");
00202             break;
00203         default:
00204             _readStream->skip(nextChunk.len);
00205             break;
00206         }
00207         break;
00208     case kFileStateProcessSave:
00209         if (nextChunk.id == kSaveRecordTag) {
00210             readSaveRecord();
00211         } else {
00212             _playbackParseState = kFileStateSelectSection;
00213             return false;
00214         }
00215         break;
00216     case kFileStateProcessHeader:
00217         switch (nextChunk.id) {
00218         case kAuthorTag:
00219             _header.author = readString(nextChunk.len);
00220             break;
00221         case kCommentsTag:
00222             _header.notes = readString(nextChunk.len);
00223             break;
00224         case kNameTag:
00225             _header.name = readString(nextChunk.len);
00226             break;
00227         default:
00228             _playbackParseState = kFileStateSelectSection;
00229             return false;
00230         }
00231         break;
00232     case kFileStateProcessHash:
00233         if (nextChunk.id == kHashRecordTag) {
00234             readHashMap(nextChunk);
00235         } else {
00236             _playbackParseState = kFileStateSelectSection;
00237             return false;
00238         }
00239         break;
00240     case kFileStateProcessRandom:
00241         if (nextChunk.id == kRandomRecordTag) {
00242             processRndSeedRecord(nextChunk);
00243         } else {
00244             _playbackParseState = kFileStateSelectSection;
00245             return false;
00246         }
00247         break;
00248     case kFileStateProcessSettings:
00249         if (nextChunk.id == kSettingsRecordTag) {
00250             if (!processSettingsRecord()) {
00251                 _playbackParseState = kFileStateError;
00252                 return false;
00253             }
00254         } else {
00255             _playbackParseState = kFileStateSelectSection;
00256             return false;
00257         }
00258         break;
00259     default:
00260             return false;
00261     }
00262     return true;
00263 }
00264 
00265 void PlaybackFile::returnToChunkHeader() {
00266     _readStream->seek(-8, SEEK_CUR);
00267 }
00268 
00269 void PlaybackFile::readHashMap(ChunkHeader chunk) {
00270     String hashName = readString(chunk.len - 32);
00271     String hashMd5 = readString(32);
00272     _header.hashRecords[hashName] = hashMd5;
00273 }
00274 
00275 void PlaybackFile::processRndSeedRecord(ChunkHeader chunk) {
00276     String randomSourceName = readString(chunk.len - 4);
00277     uint32 randomSourceSeed = _readStream->readUint32LE();
00278     _header.randomSourceRecords[randomSourceName] = randomSourceSeed;
00279 }
00280 
00281 bool PlaybackFile::processSettingsRecord() {
00282     ChunkHeader keyChunk;
00283     if (!readChunkHeader(keyChunk) || (keyChunk.id != kSettingsRecordKeyTag)) {
00284         warning("Invalid format of settings section");
00285         return false;
00286     }
00287     String key = readString(keyChunk.len);
00288     ChunkHeader valueChunk;
00289     if (!readChunkHeader(valueChunk) || (valueChunk.id != kSettingsRecordValueTag)) {
00290         warning("Invalid format of settings section");
00291         return false;
00292     }
00293     String value = readString(valueChunk.len);
00294     _header.settingsRecords[key] = value;
00295     return true;
00296 }
00297 
00298 
00299 bool PlaybackFile::readSaveRecord() {
00300     ChunkHeader fileNameChunk;
00301     if (!readChunkHeader(fileNameChunk) || (fileNameChunk.id != kSaveRecordNameTag)) {
00302         warning("Invalid format of save section");
00303         return false;
00304     }
00305     String fileName = readString(fileNameChunk.len);
00306     ChunkHeader saveBufferChunk;
00307     if (!readChunkHeader(saveBufferChunk) || (saveBufferChunk.id != kSaveRecordBufferTag)) {
00308         warning("Invalid format of save section");
00309         return false;
00310     }
00311     SaveFileBuffer buf;
00312     buf.size = saveBufferChunk.len;
00313     buf.buffer = (byte *)malloc(saveBufferChunk.len);
00314     _readStream->read(buf.buffer, buf.size);
00315     _header.saveFiles[fileName] = buf;
00316     debugC(1, kDebugLevelEventRec, "playback:action=\"Load save file\" filename=%s len=%d", fileName.c_str(), buf.size);
00317     return true;
00318 }
00319 
00320 
00321 
00322 RecorderEvent PlaybackFile::getNextEvent() {
00323     assert(_mode == kRead);
00324     if (isEventsBufferEmpty()) {
00325         PlaybackFile::ChunkHeader header;
00326         header.id = kFormatIdTag;
00327         while (header.id != kEventTag) {
00328             if (!readChunkHeader(header) || _readStream->eos()) {
00329                 break;
00330             }
00331             switch (header.id) {
00332             case kEventTag:
00333                 readEventsToBuffer(header.len);
00334                 break;
00335             case kScreenShotTag:
00336                 _readStream->seek(-4, SEEK_CUR);
00337                 header.len = _readStream->readUint32BE();
00338                 _readStream->skip(header.len - 8);
00339                 break;
00340             case kMD5Tag:
00341                 checkRecordedMD5();
00342                 break;
00343             default:
00344                 _readStream->skip(header.len);
00345                 break;
00346             }
00347         }
00348     }
00349     RecorderEvent result;
00350     readEvent(result);
00351     return result;
00352 }
00353 
00354 bool PlaybackFile::isEventsBufferEmpty() {
00355     return (uint32)_tmpPlaybackFile.pos() == _eventsSize;
00356 }
00357 
00358 void PlaybackFile::readEvent(RecorderEvent& event) {
00359     event.recordedtype = (RecorderEventType)_tmpPlaybackFile.readByte();
00360     switch (event.recordedtype) {
00361     case kRecorderEventTypeTimer:
00362         event.time = _tmpPlaybackFile.readUint32LE();
00363         break;
00364     default:
00365         // fallthrough intended
00366     case kRecorderEventTypeNormal:
00367         event.type = (EventType)_tmpPlaybackFile.readUint32LE();
00368         switch (event.type) {
00369         case EVENT_KEYDOWN:
00370         case EVENT_KEYUP:
00371             event.time = _tmpPlaybackFile.readUint32LE();
00372             event.kbd.keycode = (KeyCode)_tmpPlaybackFile.readSint32LE();
00373             event.kbd.ascii = _tmpPlaybackFile.readUint16LE();
00374             event.kbd.flags = _tmpPlaybackFile.readByte();
00375             break;
00376         case EVENT_MOUSEMOVE:
00377         case EVENT_LBUTTONDOWN:
00378         case EVENT_LBUTTONUP:
00379         case EVENT_RBUTTONDOWN:
00380         case EVENT_RBUTTONUP:
00381         case EVENT_WHEELUP:
00382         case EVENT_WHEELDOWN:
00383         case EVENT_MBUTTONDOWN:
00384         case EVENT_MBUTTONUP:
00385         case EVENT_X1BUTTONDOWN:
00386         case EVENT_X1BUTTONUP:
00387         case EVENT_X2BUTTONDOWN:
00388         case EVENT_X2BUTTONUP:
00389             event.time = _tmpPlaybackFile.readUint32LE();
00390             event.mouse.x = _tmpPlaybackFile.readSint16LE();
00391             event.mouse.y = _tmpPlaybackFile.readSint16LE();
00392             break;
00393         default:
00394             event.time = _tmpPlaybackFile.readUint32LE();
00395             break;
00396         }
00397         break;
00398     }
00399     event.kbdRepeat = true;
00400 }
00401 
00402 void PlaybackFile::readEventsToBuffer(uint32 size) {
00403     _readStream->read(_tmpBuffer, size);
00404     _tmpPlaybackFile.seek(0);
00405     _eventsSize = size;
00406 }
00407 
00408 void PlaybackFile::saveScreenShot(Graphics::Surface &screen, byte md5[16]) {
00409     dumpRecordsToFile();
00410     _writeStream->writeUint32LE(kMD5Tag);
00411     _writeStream->writeUint32LE(16);
00412     _writeStream->write(md5, 16);
00413     Graphics::saveThumbnail(*_writeStream, screen);
00414 }
00415 
00416 void PlaybackFile::dumpRecordsToFile() {
00417     if (!_headerDumped) {
00418         dumpHeaderToFile();
00419         _headerDumped = true;
00420     }
00421     if (_recordCount == 0) {
00422         return;
00423     }
00424     _writeStream->writeUint32LE(kEventTag);
00425     _writeStream->writeUint32LE(_tmpRecordFile.pos());
00426     _writeStream->write(_tmpBuffer, _tmpRecordFile.pos());
00427     _tmpRecordFile.seek(0);
00428     _recordCount = 0;
00429 }
00430 
00431 void PlaybackFile::dumpHeaderToFile() {
00432     _writeStream->writeUint32LE(kFormatIdTag);
00433     // Specify size for first tag as NULL since we cannot calculate
00434     // size of the file at time of the header dumping
00435     _writeStream->writeUint32LE(0);
00436     _writeStream->writeUint32LE(kVersionTag);
00437     _writeStream->writeUint32LE(4);
00438     _writeStream->writeUint32LE(RECORD_VERSION);
00439     writeHeaderSection();
00440     writeGameHash();
00441     writeRandomRecords();
00442     writeGameSettings();
00443     writeSaveFilesSection();
00444 }
00445 
00446 void PlaybackFile::writeHeaderSection() {
00447     uint32 headerSize = 0;
00448     if (!_header.author.empty()) {
00449         headerSize = _header.author.size() + 8;
00450     }
00451     if (!_header.notes.empty()) {
00452         headerSize += _header.notes.size() + 8;
00453     }
00454     if (!_header.name.empty()) {
00455         headerSize += _header.name.size() + 8;
00456     }
00457     if (headerSize == 0) {
00458         return;
00459     }
00460     _writeStream->writeUint32LE(kHeaderSectionTag);
00461     _writeStream->writeUint32LE(headerSize);
00462     if (!_header.author.empty()) {
00463         _writeStream->writeUint32LE(kAuthorTag);
00464         _writeStream->writeUint32LE(_header.author.size());
00465         _writeStream->writeString(_header.author);
00466     }
00467     if (!_header.notes.empty()) {
00468         _writeStream->writeUint32LE(kCommentsTag);
00469         _writeStream->writeUint32LE(_header.notes.size());
00470         _writeStream->writeString(_header.notes);
00471     }
00472     if (!_header.name.empty()) {
00473         _writeStream->writeUint32LE(kNameTag);
00474         _writeStream->writeUint32LE(_header.name.size());
00475         _writeStream->writeString(_header.name);
00476     }
00477 }
00478 
00479 void PlaybackFile::writeGameHash() {
00480     uint32 hashSectionSize = 0;
00481     for (StringMap::iterator i = _header.hashRecords.begin(); i != _header.hashRecords.end(); ++i) {
00482         hashSectionSize = hashSectionSize + i->_key.size() + i->_value.size() + 8;
00483     }
00484     if (_header.hashRecords.size() == 0) {
00485         return;
00486     }
00487     _writeStream->writeUint32LE(kHashSectionTag);
00488     _writeStream->writeUint32LE(hashSectionSize);
00489     for (StringMap::iterator i = _header.hashRecords.begin(); i != _header.hashRecords.end(); ++i) {
00490         _writeStream->writeUint32LE(kHashRecordTag);
00491         _writeStream->writeUint32LE(i->_key.size() + i->_value.size());
00492         _writeStream->writeString(i->_key);
00493         _writeStream->writeString(i->_value);
00494     }
00495 }
00496 
00497 void PlaybackFile::writeRandomRecords() {
00498     uint32 randomSectionSize = 0;
00499     for (RandomSeedsDictionary::iterator i = _header.randomSourceRecords.begin(); i != _header.randomSourceRecords.end(); ++i) {
00500         randomSectionSize = randomSectionSize + i->_key.size() + 12;
00501     }
00502     if (_header.randomSourceRecords.size() == 0) {
00503         return;
00504     }
00505     _writeStream->writeUint32LE(kRandomSectionTag);
00506     _writeStream->writeUint32LE(randomSectionSize);
00507     for (RandomSeedsDictionary::iterator i = _header.randomSourceRecords.begin(); i != _header.randomSourceRecords.end(); ++i) {
00508         _writeStream->writeUint32LE(kRandomRecordTag);
00509         _writeStream->writeUint32LE(i->_key.size() + 4);
00510         _writeStream->writeString(i->_key);
00511         _writeStream->writeUint32LE(i->_value);
00512     }
00513 }
00514 
00515 void PlaybackFile::writeEvent(const RecorderEvent &event) {
00516     assert(_mode == kWrite);
00517     _recordCount++;
00518     _tmpRecordFile.writeByte(event.recordedtype);
00519     switch (event.recordedtype) {
00520     case kRecorderEventTypeTimer:
00521         _tmpRecordFile.writeUint32LE(event.time);
00522         break;
00523     default:
00524         // fallthrough intended
00525     case kRecorderEventTypeNormal:
00526         _tmpRecordFile.writeUint32LE((uint32)event.type);
00527         switch(event.type) {
00528         case EVENT_KEYDOWN:
00529         case EVENT_KEYUP:
00530             _tmpRecordFile.writeUint32LE(event.time);
00531             _tmpRecordFile.writeSint32LE(event.kbd.keycode);
00532             _tmpRecordFile.writeUint16LE(event.kbd.ascii);
00533             _tmpRecordFile.writeByte(event.kbd.flags);
00534             break;
00535         case EVENT_MOUSEMOVE:
00536         case EVENT_LBUTTONDOWN:
00537         case EVENT_LBUTTONUP:
00538         case EVENT_RBUTTONDOWN:
00539         case EVENT_RBUTTONUP:
00540         case EVENT_WHEELUP:
00541         case EVENT_WHEELDOWN:
00542         case EVENT_MBUTTONDOWN:
00543         case EVENT_MBUTTONUP:
00544         case EVENT_X1BUTTONDOWN:
00545         case EVENT_X1BUTTONUP:
00546         case EVENT_X2BUTTONDOWN:
00547         case EVENT_X2BUTTONUP:
00548             _tmpRecordFile.writeUint32LE(event.time);
00549             _tmpRecordFile.writeSint16LE(event.mouse.x);
00550             _tmpRecordFile.writeSint16LE(event.mouse.y);
00551             break;
00552         default:
00553             _tmpRecordFile.writeUint32LE(event.time);
00554             break;
00555         }
00556         break;
00557     }
00558     if (_recordCount == kMaxBufferedRecords) {
00559         dumpRecordsToFile();
00560     }
00561 }
00562 
00563 void PlaybackFile::writeGameSettings() {
00564     _writeStream->writeUint32LE(kSettingsSectionTag);
00565     uint32 settingsSectionSize = 0;
00566     for (StringMap::iterator i = _header.settingsRecords.begin(); i != _header.settingsRecords.end(); ++i) {
00567         settingsSectionSize += i->_key.size() + i->_value.size() + 24;
00568     }
00569     _writeStream->writeUint32LE(settingsSectionSize);
00570     for (StringMap::iterator i = _header.settingsRecords.begin(); i != _header.settingsRecords.end(); ++i) {
00571         _writeStream->writeUint32LE(kSettingsRecordTag);
00572         _writeStream->writeUint32LE(i->_key.size() + i->_value.size() + 16);
00573         _writeStream->writeUint32LE(kSettingsRecordKeyTag);
00574         _writeStream->writeUint32LE(i->_key.size());
00575         _writeStream->writeString(i->_key);
00576         _writeStream->writeUint32LE(kSettingsRecordValueTag);
00577         _writeStream->writeUint32LE(i->_value.size());
00578         _writeStream->writeString(i->_value);
00579     }
00580 }
00581 
00582 int PlaybackFile::getScreensCount() {
00583     if (_mode != kRead) {
00584         return 0;
00585     }
00586     _readStream->seek(0);
00587     int result = 0;
00588     while (skipToNextScreenshot()) {
00589         uint32 size = _readStream->readUint32BE();
00590         _readStream->skip(size - 8);
00591         ++result;
00592     }
00593     return result;
00594 }
00595 
00596 bool PlaybackFile::skipToNextScreenshot() {
00597     while (true) {
00598         FileTag id = (FileTag)_readStream->readUint32LE();
00599         if (_readStream->eos()) {
00600             break;
00601         }
00602         if (id == kScreenShotTag) {
00603             return true;
00604         }
00605         else {
00606             uint32 size = _readStream->readUint32LE();
00607             _readStream->skip(size);
00608         }
00609     }
00610     return false;
00611 }
00612 
00613 Graphics::Surface *PlaybackFile::getScreenShot(int number) {
00614     if (_mode != kRead) {
00615         return NULL;
00616     }
00617     _readStream->seek(0);
00618     int screenCount = 1;
00619     while (skipToNextScreenshot()) {
00620         if (screenCount == number) {
00621             screenCount++;
00622             _readStream->seek(-4, SEEK_CUR);
00623             Graphics::Surface *thumbnail;
00624             return Graphics::loadThumbnail(*_readStream, thumbnail) ? thumbnail : NULL;
00625         } else {
00626             uint32 size = _readStream->readUint32BE();
00627             _readStream->skip(size - 8);
00628             screenCount++;
00629         }
00630     }
00631     return NULL;
00632 }
00633 
00634 void PlaybackFile::updateHeader() {
00635     if (_mode == kWrite) {
00636         _readStream = g_system->getSavefileManager()->openForLoading(_header.fileName);
00637     }
00638     _readStream->seek(0);
00639     skipHeader();
00640     String tmpFilename = "_" + _header.fileName;
00641     _writeStream = g_system->getSavefileManager()->openForSaving(tmpFilename);
00642     dumpHeaderToFile();
00643     uint32 readedSize = 0;
00644     do {
00645         readedSize = _readStream->read(_tmpBuffer, kRecordBuffSize);
00646         _writeStream->write(_tmpBuffer, readedSize);
00647     } while (readedSize != 0);
00648     delete _writeStream;
00649     _writeStream = NULL;
00650     delete _readStream;
00651     _readStream = NULL;
00652     g_system->getSavefileManager()->removeSavefile(_header.fileName);
00653     g_system->getSavefileManager()->renameSavefile(tmpFilename, _header.fileName);
00654     if (_mode == kRead) {
00655         openRead(_header.fileName);
00656     }
00657 }
00658 
00659 void PlaybackFile::skipHeader() {
00660     while (true) {
00661         uint32 id = _readStream->readUint32LE();
00662         if (_readStream->eos()) {
00663             break;
00664         }
00665         if ((id == kScreenShotTag) || (id == kEventTag) || (id == kMD5Tag)) {
00666             _readStream->seek(-4, SEEK_CUR);
00667             return;
00668         }
00669         else {
00670             uint32 size = _readStream->readUint32LE();
00671             _readStream->skip(size);
00672         }
00673     }
00674 }
00675 
00676 void PlaybackFile::addSaveFile(const String &fileName, InSaveFile *saveStream) {
00677     uint oldPos = saveStream->pos();
00678     saveStream->seek(0);
00679     _header.saveFiles[fileName].buffer = (byte *)malloc(saveStream->size());
00680     _header.saveFiles[fileName].size = saveStream->size();
00681     saveStream->read(_header.saveFiles[fileName].buffer, saveStream->size());
00682     saveStream->seek(oldPos);
00683 }
00684 
00685 void PlaybackFile::writeSaveFilesSection() {
00686     uint size = 0;
00687     for (HashMap<String, SaveFileBuffer>::iterator  i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) {
00688         size += i->_value.size + i->_key.size() + 24;
00689     }
00690     if (size == 0) {
00691         return;
00692     }
00693     _writeStream->writeSint32LE(kSaveTag);
00694     _writeStream->writeSint32LE(size);
00695     for (HashMap<String, SaveFileBuffer>::iterator  i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) {
00696         _writeStream->writeSint32LE(kSaveRecordTag);
00697         _writeStream->writeSint32LE(i->_key.size() + i->_value.size + 16);
00698         _writeStream->writeSint32LE(kSaveRecordNameTag);
00699         _writeStream->writeSint32LE(i->_key.size());
00700         _writeStream->writeString(i->_key);
00701         _writeStream->writeSint32LE(kSaveRecordBufferTag);
00702         _writeStream->writeSint32LE(i->_value.size);
00703         _writeStream->write(i->_value.buffer, i->_value.size);
00704     }
00705 }
00706 
00707 
00708 void PlaybackFile::checkRecordedMD5() {
00709     uint8 currentMD5[16];
00710     uint8 savedMD5[16];
00711     Graphics::Surface screen;
00712     _readStream->read(savedMD5, 16);
00713     if (!g_eventRec.grabScreenAndComputeMD5(screen, currentMD5)) {
00714         return;
00715     }
00716     uint32 seconds = g_system->getMillis(true) / 1000;
00717     String screenTime = String::format("%.2d:%.2d:%.2d", seconds / 3600 % 24, seconds / 60 % 60, seconds % 60);
00718     if (memcmp(savedMD5, currentMD5, 16) != 0) {
00719         debugC(1, kDebugLevelEventRec, "playback:action=\"Check screenshot\" time=%s result = fail", screenTime.c_str());
00720         warning("Recorded and current screenshots are different");
00721     } else {
00722         debugC(1, kDebugLevelEventRec, "playback:action=\"Check screenshot\" time=%s result = success", screenTime.c_str());
00723     }
00724     Graphics::saveThumbnail(*_screenshotsFile, screen);
00725     screen.free();
00726 }
00727 
00728 
00729 }


Generated on Sat May 23 2020 05:00:30 for ResidualVM by doxygen 1.7.1
curved edge   curved edge