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     case kRecorderEventTypeNormal:
00365         event.type = (EventType)_tmpPlaybackFile.readUint32LE();
00366         switch (event.type) {
00367         case EVENT_KEYDOWN:
00368         case EVENT_KEYUP:
00369             event.time = _tmpPlaybackFile.readUint32LE();
00370             event.kbd.keycode = (KeyCode)_tmpPlaybackFile.readSint32LE();
00371             event.kbd.ascii = _tmpPlaybackFile.readUint16LE();
00372             event.kbd.flags = _tmpPlaybackFile.readByte();
00373             break;
00374         case EVENT_MOUSEMOVE:
00375         case EVENT_LBUTTONDOWN:
00376         case EVENT_LBUTTONUP:
00377         case EVENT_RBUTTONDOWN:
00378         case EVENT_RBUTTONUP:
00379         case EVENT_WHEELUP:
00380         case EVENT_WHEELDOWN:
00381         case EVENT_MBUTTONDOWN:
00382         case EVENT_MBUTTONUP:
00383             event.time = _tmpPlaybackFile.readUint32LE();
00384             event.mouse.x = _tmpPlaybackFile.readSint16LE();
00385             event.mouse.y = _tmpPlaybackFile.readSint16LE();
00386             break;
00387         default:
00388             event.time = _tmpPlaybackFile.readUint32LE();
00389             break;
00390         }
00391         break;
00392     }
00393     event.kbdRepeat = true;
00394 }
00395 
00396 void PlaybackFile::readEventsToBuffer(uint32 size) {
00397     _readStream->read(_tmpBuffer, size);
00398     _tmpPlaybackFile.seek(0);
00399     _eventsSize = size;
00400 }
00401 
00402 void PlaybackFile::saveScreenShot(Graphics::Surface &screen, byte md5[16]) {
00403     dumpRecordsToFile();
00404     _writeStream->writeUint32LE(kMD5Tag);
00405     _writeStream->writeUint32LE(16);
00406     _writeStream->write(md5, 16);
00407     Graphics::saveThumbnail(*_writeStream, screen);
00408 }
00409 
00410 void PlaybackFile::dumpRecordsToFile() {
00411     if (!_headerDumped) {
00412         dumpHeaderToFile();
00413         _headerDumped = true;
00414     }
00415     if (_recordCount == 0) {
00416         return;
00417     }
00418     _writeStream->writeUint32LE(kEventTag);
00419     _writeStream->writeUint32LE(_tmpRecordFile.pos());
00420     _writeStream->write(_tmpBuffer, _tmpRecordFile.pos());
00421     _tmpRecordFile.seek(0);
00422     _recordCount = 0;
00423 }
00424 
00425 void PlaybackFile::dumpHeaderToFile() {
00426     _writeStream->writeUint32LE(kFormatIdTag);
00427     // Specify size for first tag as NULL since we cannot calculate
00428     // size of the file at time of the header dumping
00429     _writeStream->writeUint32LE(0);
00430     _writeStream->writeUint32LE(kVersionTag);
00431     _writeStream->writeUint32LE(4);
00432     _writeStream->writeUint32LE(RECORD_VERSION);
00433     writeHeaderSection();
00434     writeGameHash();
00435     writeRandomRecords();
00436     writeGameSettings();
00437     writeSaveFilesSection();
00438 }
00439 
00440 void PlaybackFile::writeHeaderSection() {
00441     uint32 headerSize = 0;
00442     if (!_header.author.empty()) {
00443         headerSize = _header.author.size() + 8;
00444     }
00445     if (!_header.notes.empty()) {
00446         headerSize += _header.notes.size() + 8;
00447     }
00448     if (!_header.name.empty()) {
00449         headerSize += _header.name.size() + 8;
00450     }
00451     if (headerSize == 0) {
00452         return;
00453     }
00454     _writeStream->writeUint32LE(kHeaderSectionTag);
00455     _writeStream->writeUint32LE(headerSize);
00456     if (!_header.author.empty()) {
00457         _writeStream->writeUint32LE(kAuthorTag);
00458         _writeStream->writeUint32LE(_header.author.size());
00459         _writeStream->writeString(_header.author);
00460     }
00461     if (!_header.notes.empty()) {
00462         _writeStream->writeUint32LE(kCommentsTag);
00463         _writeStream->writeUint32LE(_header.notes.size());
00464         _writeStream->writeString(_header.notes);
00465     }
00466     if (!_header.name.empty()) {
00467         _writeStream->writeUint32LE(kNameTag);
00468         _writeStream->writeUint32LE(_header.name.size());
00469         _writeStream->writeString(_header.name);
00470     }
00471 }
00472 
00473 void PlaybackFile::writeGameHash() {
00474     uint32 hashSectionSize = 0;
00475     for (StringMap::iterator i = _header.hashRecords.begin(); i != _header.hashRecords.end(); ++i) {
00476         hashSectionSize = hashSectionSize + i->_key.size() + i->_value.size() + 8;
00477     }
00478     if (_header.hashRecords.size() == 0) {
00479         return;
00480     }
00481     _writeStream->writeUint32LE(kHashSectionTag);
00482     _writeStream->writeUint32LE(hashSectionSize);
00483     for (StringMap::iterator i = _header.hashRecords.begin(); i != _header.hashRecords.end(); ++i) {
00484         _writeStream->writeUint32LE(kHashRecordTag);
00485         _writeStream->writeUint32LE(i->_key.size() + i->_value.size());
00486         _writeStream->writeString(i->_key);
00487         _writeStream->writeString(i->_value);
00488     }
00489 }
00490 
00491 void PlaybackFile::writeRandomRecords() {
00492     uint32 randomSectionSize = 0;
00493     for (RandomSeedsDictionary::iterator i = _header.randomSourceRecords.begin(); i != _header.randomSourceRecords.end(); ++i) {
00494         randomSectionSize = randomSectionSize + i->_key.size() + 12;
00495     }
00496     if (_header.randomSourceRecords.size() == 0) {
00497         return;
00498     }
00499     _writeStream->writeUint32LE(kRandomSectionTag);
00500     _writeStream->writeUint32LE(randomSectionSize);
00501     for (RandomSeedsDictionary::iterator i = _header.randomSourceRecords.begin(); i != _header.randomSourceRecords.end(); ++i) {
00502         _writeStream->writeUint32LE(kRandomRecordTag);
00503         _writeStream->writeUint32LE(i->_key.size() + 4);
00504         _writeStream->writeString(i->_key);
00505         _writeStream->writeUint32LE(i->_value);
00506     }
00507 }
00508 
00509 void PlaybackFile::writeEvent(const RecorderEvent &event) {
00510     assert(_mode == kWrite);
00511     _recordCount++;
00512     _tmpRecordFile.writeByte(event.recordedtype);
00513     switch (event.recordedtype) {
00514     case kRecorderEventTypeTimer:
00515         _tmpRecordFile.writeUint32LE(event.time);
00516         break;
00517     case kRecorderEventTypeNormal:
00518         _tmpRecordFile.writeUint32LE((uint32)event.type);
00519         switch(event.type) {
00520         case EVENT_KEYDOWN:
00521         case EVENT_KEYUP:
00522             _tmpRecordFile.writeUint32LE(event.time);
00523             _tmpRecordFile.writeSint32LE(event.kbd.keycode);
00524             _tmpRecordFile.writeUint16LE(event.kbd.ascii);
00525             _tmpRecordFile.writeByte(event.kbd.flags);
00526             break;
00527         case EVENT_MOUSEMOVE:
00528         case EVENT_LBUTTONDOWN:
00529         case EVENT_LBUTTONUP:
00530         case EVENT_RBUTTONDOWN:
00531         case EVENT_RBUTTONUP:
00532         case EVENT_WHEELUP:
00533         case EVENT_WHEELDOWN:
00534         case EVENT_MBUTTONDOWN:
00535         case EVENT_MBUTTONUP:
00536             _tmpRecordFile.writeUint32LE(event.time);
00537             _tmpRecordFile.writeSint16LE(event.mouse.x);
00538             _tmpRecordFile.writeSint16LE(event.mouse.y);
00539             break;
00540         default:
00541             _tmpRecordFile.writeUint32LE(event.time);
00542             break;
00543         }
00544         break;
00545     }
00546     if (_recordCount == kMaxBufferedRecords) {
00547         dumpRecordsToFile();
00548     }
00549 }
00550 
00551 void PlaybackFile::writeGameSettings() {
00552     _writeStream->writeUint32LE(kSettingsSectionTag);
00553     uint32 settingsSectionSize = 0;
00554     for (StringMap::iterator i = _header.settingsRecords.begin(); i != _header.settingsRecords.end(); ++i) {
00555         settingsSectionSize += i->_key.size() + i->_value.size() + 24;
00556     }
00557     _writeStream->writeUint32LE(settingsSectionSize);
00558     for (StringMap::iterator i = _header.settingsRecords.begin(); i != _header.settingsRecords.end(); ++i) {
00559         _writeStream->writeUint32LE(kSettingsRecordTag);
00560         _writeStream->writeUint32LE(i->_key.size() + i->_value.size() + 16);
00561         _writeStream->writeUint32LE(kSettingsRecordKeyTag);
00562         _writeStream->writeUint32LE(i->_key.size());
00563         _writeStream->writeString(i->_key);
00564         _writeStream->writeUint32LE(kSettingsRecordValueTag);
00565         _writeStream->writeUint32LE(i->_value.size());
00566         _writeStream->writeString(i->_value);
00567     }
00568 }
00569 
00570 int PlaybackFile::getScreensCount() {
00571     if (_mode != kRead) {
00572         return 0;
00573     }
00574     _readStream->seek(0);
00575     int result = 0;
00576     while (skipToNextScreenshot()) {
00577         uint32 size = _readStream->readUint32BE();
00578         _readStream->skip(size - 8);
00579         ++result;
00580     }
00581     return result;
00582 }
00583 
00584 bool PlaybackFile::skipToNextScreenshot() {
00585     while (true) {
00586         FileTag id = (FileTag)_readStream->readUint32LE();
00587         if (_readStream->eos()) {
00588             break;
00589         }
00590         if (id == kScreenShotTag) {
00591             return true;
00592         }
00593         else {
00594             uint32 size = _readStream->readUint32LE();
00595             _readStream->skip(size);
00596         }
00597     }
00598     return false;
00599 }
00600 
00601 Graphics::Surface *PlaybackFile::getScreenShot(int number) {
00602     if (_mode != kRead) {
00603         return NULL;
00604     }
00605     _readStream->seek(0);
00606     int screenCount = 1;
00607     while (skipToNextScreenshot()) {
00608         if (screenCount == number) {
00609             screenCount++;
00610             _readStream->seek(-4, SEEK_CUR);
00611             Graphics::Surface *thumbnail;
00612             return Graphics::loadThumbnail(*_readStream, thumbnail) ? thumbnail : NULL;
00613         } else {
00614             uint32 size = _readStream->readUint32BE();
00615             _readStream->skip(size - 8);
00616             screenCount++;
00617         }
00618     }
00619     return NULL;
00620 }
00621 
00622 void PlaybackFile::updateHeader() {
00623     if (_mode == kWrite) {
00624         _readStream = g_system->getSavefileManager()->openForLoading(_header.fileName);
00625     }
00626     _readStream->seek(0);
00627     skipHeader();
00628     String tmpFilename = "_" + _header.fileName;
00629     _writeStream = g_system->getSavefileManager()->openForSaving(tmpFilename);
00630     dumpHeaderToFile();
00631     uint32 readedSize = 0;
00632     do {
00633         readedSize = _readStream->read(_tmpBuffer, kRecordBuffSize);
00634         _writeStream->write(_tmpBuffer, readedSize);
00635     } while (readedSize != 0);
00636     delete _writeStream;
00637     _writeStream = NULL;
00638     delete _readStream;
00639     _readStream = NULL;
00640     g_system->getSavefileManager()->removeSavefile(_header.fileName);
00641     g_system->getSavefileManager()->renameSavefile(tmpFilename, _header.fileName);
00642     if (_mode == kRead) {
00643         openRead(_header.fileName);
00644     }
00645 }
00646 
00647 void PlaybackFile::skipHeader() {
00648     while (true) {
00649         uint32 id = _readStream->readUint32LE();
00650         if (_readStream->eos()) {
00651             break;
00652         }
00653         if ((id == kScreenShotTag) || (id == kEventTag) || (id == kMD5Tag)) {
00654             _readStream->seek(-4, SEEK_CUR);
00655             return;
00656         }
00657         else {
00658             uint32 size = _readStream->readUint32LE();
00659             _readStream->skip(size);
00660         }
00661     }
00662 }
00663 
00664 void PlaybackFile::addSaveFile(const String &fileName, InSaveFile *saveStream) {
00665     uint oldPos = saveStream->pos();
00666     saveStream->seek(0);
00667     _header.saveFiles[fileName].buffer = (byte *)malloc(saveStream->size());
00668     _header.saveFiles[fileName].size = saveStream->size();
00669     saveStream->read(_header.saveFiles[fileName].buffer, saveStream->size());
00670     saveStream->seek(oldPos);
00671 }
00672 
00673 void PlaybackFile::writeSaveFilesSection() {
00674     uint size = 0;
00675     for (HashMap<String, SaveFileBuffer>::iterator  i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) {
00676         size += i->_value.size + i->_key.size() + 24;
00677     }
00678     if (size == 0) {
00679         return;
00680     }
00681     _writeStream->writeSint32LE(kSaveTag);
00682     _writeStream->writeSint32LE(size);
00683     for (HashMap<String, SaveFileBuffer>::iterator  i = _header.saveFiles.begin(); i != _header.saveFiles.end(); ++i) {
00684         _writeStream->writeSint32LE(kSaveRecordTag);
00685         _writeStream->writeSint32LE(i->_key.size() + i->_value.size + 16);
00686         _writeStream->writeSint32LE(kSaveRecordNameTag);
00687         _writeStream->writeSint32LE(i->_key.size());
00688         _writeStream->writeString(i->_key);
00689         _writeStream->writeSint32LE(kSaveRecordBufferTag);
00690         _writeStream->writeSint32LE(i->_value.size);
00691         _writeStream->write(i->_value.buffer, i->_value.size);
00692     }
00693 }
00694 
00695 
00696 void PlaybackFile::checkRecordedMD5() {
00697     uint8 currentMD5[16];
00698     uint8 savedMD5[16];
00699     Graphics::Surface screen;
00700     _readStream->read(savedMD5, 16);
00701     if (!g_eventRec.grabScreenAndComputeMD5(screen, currentMD5)) {
00702         return;
00703     }
00704     uint32 seconds = g_system->getMillis(true) / 1000;
00705     String screenTime = String::format("%.2d:%.2d:%.2d", seconds / 3600 % 24, seconds / 60 % 60, seconds % 60);
00706     if (memcmp(savedMD5, currentMD5, 16) != 0) {
00707         debugC(1, kDebugLevelEventRec, "playback:action=\"Check screenshot\" time=%s result = fail", screenTime.c_str());
00708         warning("Recorded and current screenshots are different");
00709     } else {
00710         debugC(1, kDebugLevelEventRec, "playback:action=\"Check screenshot\" time=%s result = success", screenTime.c_str());
00711     }
00712     Graphics::saveThumbnail(*_screenshotsFile, screen);
00713     screen.free();
00714 }
00715 
00716 
00717 }


Generated on Sat Feb 16 2019 05:01:02 for ResidualVM by doxygen 1.7.1
curved edge   curved edge