https://github.com/scummvm/scummvm
Raw File
Tip revision: a06f50421c1e672bda191d2b4775c4c31feb1194 authored by Lothar Serra Mari on 20 January 2023, 19:26:11 UTC
RELEASE: This is 2.7.0pre
Tip revision: a06f504
recorderfile.h
/* 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 <http://www.gnu.org/licenses/>.
 *
 */

#ifndef COMMON_RECORDERFILE_H
#define COMMON_RECORDERFILE_H

#include "common/scummsys.h"
#include "common/events.h"
#include "common/mutex.h"
#include "common/memstream.h"
#include "common/config-manager.h"
#include "common/savefile.h"

//capacity of records buffer
#define kMaxBufferedRecords 10000
#define kRecordBuffSize sizeof(RecorderEvent) * kMaxBufferedRecords

namespace Common {

enum RecorderEventType {
	kRecorderEventTypeNormal = 0,
	kRecorderEventTypeTimer = 1,
	kRecorderEventTypeTimeDate = 2,
	kRecorderEventTypeScreenUpdate = 3,
};

struct RecorderEvent : Event {
	RecorderEventType recordedtype;
	union {
		uint32 time;
		TimeDate timeDate;
	};

	RecorderEvent() {
		recordedtype = kRecorderEventTypeNormal;
		time = 0;
		timeDate.tm_sec = 0;
		timeDate.tm_min = 0;
		timeDate.tm_hour = 0;
		timeDate.tm_mday = 0;
		timeDate.tm_mon = 0;
		timeDate.tm_year = 0;
		timeDate.tm_wday = 0;
	}

	RecorderEvent(const Event &e) : Event(e) {
		recordedtype = kRecorderEventTypeNormal;
		time = 0;
		timeDate.tm_sec = 0;
		timeDate.tm_min = 0;
		timeDate.tm_hour = 0;
		timeDate.tm_mday = 0;
		timeDate.tm_mon = 0;
		timeDate.tm_year = 0;
		timeDate.tm_wday = 0;
	}
};



class PlaybackFile {
	typedef HashMap<String, uint32, IgnoreCase_Hash, IgnoreCase_EqualTo> RandomSeedsDictionary;
	enum fileMode {
		kRead = 0,
		kWrite = 1,
		kClosed = 2
	};
	enum PlaybackFileState {
		kFileStateCheckFormat,
		kFileStateCheckVersion,
		kFileStateProcessHash,
		kFileStateProcessHeader,
		kFileStateProcessRandom,
		kFileStateSelectSection,
		kFileStateProcessSettings,
		kFileStateProcessSave,
		kFileStateDone,
		kFileStateError
	};
	enum FileTag {
		kFormatIdTag = MKTAG('P','B','C','K'),
		kVersionTag = MKTAG('V','E','R','S'),
		kHeaderSectionTag = MKTAG('H','E','A','D'),
		kHashSectionTag = MKTAG('H','A','S','H'),
		kRandomSectionTag = MKTAG('R','A','N','D'),
		kEventTag = MKTAG('E','V','N','T'),
		kScreenShotTag = MKTAG('T','H','M','B'),
		kSettingsSectionTag = MKTAG('S','E','T','T'),
		kAuthorTag = MKTAG('H','A','U','T'),
		kCommentsTag = MKTAG('H','C','M','T'),
		kNameTag = MKTAG('H','N','A','M'),
		kHashRecordTag = MKTAG('H','R','C','D'),
		kRandomRecordTag = MKTAG('R','R','C','D'),
		kSettingsRecordTag = MKTAG('S','R','E','C'),
		kSettingsRecordKeyTag = MKTAG('S','K','E','Y'),
		kSettingsRecordValueTag = MKTAG('S','V','A','L'),
		kSaveTag = MKTAG('S','A','V','E'),
		kSaveRecordTag = MKTAG('R','S','A','V'),
		kSaveRecordNameTag = MKTAG('S','N','A','M'),
		kSaveRecordBufferTag = MKTAG('S','B','U','F'),
		kMD5Tag = MKTAG('M','D','5',' ')
	};
	struct ChunkHeader {
		FileTag id;
		uint32 len;
	};
public:
	struct SaveFileBuffer {
		byte *buffer;
		uint32 size;
	};
	struct PlaybackFileHeader {
		String fileName;
		String author;
		String name;
		String notes;
		String description;
		StringMap hashRecords;
		StringMap settingsRecords;
		HashMap<String, SaveFileBuffer> saveFiles;
		RandomSeedsDictionary randomSourceRecords;
	};
	PlaybackFile();
	~PlaybackFile();

	bool openWrite(const String &fileName);
	bool openRead(const String &fileName);
	void close();

	bool hasNextEvent() const;
	RecorderEvent getNextEvent();
	void writeEvent(const RecorderEvent &event);

	void saveScreenShot(Graphics::Surface &screen, byte md5[16]);
	Graphics::Surface *getScreenShot(int number);
	int getScreensCount();

	bool isEventsBufferEmpty();
	PlaybackFileHeader &getHeader() {return _header;}
	void updateHeader();
	void addSaveFile(const String &fileName, InSaveFile *saveStream);

	uint32 getVersion() const {return _version;}
private:
	Array<byte> _tmpBuffer;
	WriteStream *_recordFile;
	WriteStream *_writeStream;
	WriteStream *_screenshotsFile;
	MemoryReadStream _tmpPlaybackFile;
	SeekableReadStream *_readStream;
	SeekableMemoryWriteStream _tmpRecordFile;

	fileMode _mode;
	bool _headerDumped;
	int _recordCount;
	uint32 _eventsSize;
	PlaybackFileHeader _header;
	PlaybackFileState _playbackParseState;
	uint32 _version;

	void skipHeader();
	bool parseHeader();
	bool processChunk(ChunkHeader &nextChunk);
	void returnToChunkHeader();

	bool readSaveRecord();
	void checkRecordedMD5();
	bool readChunkHeader(ChunkHeader &nextChunk);
	void processRndSeedRecord(ChunkHeader chunk);
	bool processSettingsRecord();

	bool checkPlaybackFileVersion();

	void dumpHeaderToFile();
	void writeSaveFilesSection();
	void writeGameSettings();
	void writeHeaderSection();
	void writeGameHash();
	void writeRandomRecords();

	void dumpRecordsToFile();

	String readString(int len);
	void readHashMap(ChunkHeader chunk);

	bool skipToNextScreenshot();
	void readEvent(RecorderEvent& event);
	void readEventsToBuffer(uint32 size);
	bool grabScreenAndComputeMD5(Graphics::Surface &screen, uint8 md5[16]);
};

} // End of namespace Common

#endif
back to top