2019-08-09 16:25:59 -04:00
|
|
|
|
#include "stdafx.h"
|
|
|
|
|
#include "../Utilities/ZipReader.h"
|
|
|
|
|
#include "../Utilities/StringUtilities.h"
|
|
|
|
|
#include "../Utilities/HexUtilities.h"
|
|
|
|
|
#include "../Utilities/VirtualFile.h"
|
|
|
|
|
#include "MesenMovie.h"
|
|
|
|
|
#include "MessageManager.h"
|
|
|
|
|
#include "ControlManager.h"
|
|
|
|
|
#include "BaseControlDevice.h"
|
|
|
|
|
#include "Console.h"
|
|
|
|
|
#include "EmuSettings.h"
|
|
|
|
|
#include "SaveStateManager.h"
|
|
|
|
|
#include "MovieTypes.h"
|
|
|
|
|
#include "MovieManager.h"
|
|
|
|
|
#include "NotificationManager.h"
|
|
|
|
|
#include "BatteryManager.h"
|
2019-10-13 11:57:15 -04:00
|
|
|
|
#include "CheatManager.h"
|
2019-08-09 16:25:59 -04:00
|
|
|
|
|
2019-10-16 20:22:45 -04:00
|
|
|
|
MesenMovie::MesenMovie(shared_ptr<Console> console, bool forTest)
|
2019-08-09 16:25:59 -04:00
|
|
|
|
{
|
|
|
|
|
_console = console;
|
2019-10-16 20:22:45 -04:00
|
|
|
|
_forTest = forTest;
|
2019-08-09 16:25:59 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MesenMovie::~MesenMovie()
|
|
|
|
|
{
|
|
|
|
|
Stop();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MesenMovie::Stop()
|
|
|
|
|
{
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(_playing) {
|
|
|
|
|
if(!_forTest) {
|
2019-10-16 20:22:45 -04:00
|
|
|
|
MessageManager::DisplayMessage("Movies", "MovieEnded");
|
|
|
|
|
}
|
2019-08-09 16:25:59 -04:00
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(_console->GetSettings()->GetPreferences().PauseOnMovieEnd) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
_console->Pause();
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-13 11:57:15 -04:00
|
|
|
|
_console->GetCheatManager()->SetCheats(_originalCheats);
|
|
|
|
|
|
2019-08-09 16:25:59 -04:00
|
|
|
|
_playing = false;
|
|
|
|
|
}
|
|
|
|
|
_console->GetControlManager()->UnregisterInputProvider(this);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
bool MesenMovie::SetInput(BaseControlDevice *device)
|
2019-08-09 16:25:59 -04:00
|
|
|
|
{
|
|
|
|
|
uint32_t inputRowIndex = _console->GetControlManager()->GetPollCounter();
|
|
|
|
|
_lastPollCounter = inputRowIndex;
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(_inputData.size() > inputRowIndex && _inputData[inputRowIndex].size() > _deviceIndex) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
device->SetTextState(_inputData[inputRowIndex][_deviceIndex]);
|
|
|
|
|
|
|
|
|
|
_deviceIndex++;
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(_deviceIndex >= _inputData[inputRowIndex].size()) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
//Move to the next frame's data
|
|
|
|
|
_deviceIndex = 0;
|
|
|
|
|
}
|
2021-03-10 11:13:28 -05:00
|
|
|
|
} else {
|
2019-10-16 20:22:45 -04:00
|
|
|
|
_console->GetMovieManager()->Stop();
|
2019-08-09 16:25:59 -04:00
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MesenMovie::IsPlaying()
|
|
|
|
|
{
|
|
|
|
|
return _playing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vector<uint8_t> MesenMovie::LoadBattery(string extension)
|
|
|
|
|
{
|
|
|
|
|
vector<uint8_t> batteryData;
|
|
|
|
|
_reader->ExtractFile("Battery" + extension, batteryData);
|
|
|
|
|
return batteryData;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-16 20:22:45 -04:00
|
|
|
|
void MesenMovie::ProcessNotification(ConsoleNotificationType type, void* parameter)
|
2019-08-09 16:25:59 -04:00
|
|
|
|
{
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(type == ConsoleNotificationType::GameLoaded) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
_console->GetControlManager()->RegisterInputProvider(this);
|
2019-10-16 20:22:45 -04:00
|
|
|
|
_console->GetControlManager()->SetPollCounter(_lastPollCounter);
|
2019-08-09 16:25:59 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
bool MesenMovie::Play(VirtualFile &file)
|
2019-08-09 16:25:59 -04:00
|
|
|
|
{
|
|
|
|
|
_movieFile = file;
|
|
|
|
|
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
file.ReadFile(ss);
|
|
|
|
|
|
|
|
|
|
_reader.reset(new ZipReader());
|
|
|
|
|
_reader->LoadArchive(ss);
|
|
|
|
|
|
|
|
|
|
stringstream settingsData, inputData;
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(!_reader->GetStream("GameSettings.txt", settingsData)) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
MessageManager::Log("[Movie] File not found: GameSettings.txt");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(!_reader->GetStream("Input.txt", inputData)) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
MessageManager::Log("[Movie] File not found: Input.txt");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
while(inputData) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
string line;
|
|
|
|
|
std::getline(inputData, line);
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(line.substr(0, 1) == "|") {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
_inputData.push_back(StringUtilities::Split(line.substr(1), '|'));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_deviceIndex = 0;
|
|
|
|
|
|
|
|
|
|
ParseSettings(settingsData);
|
2021-03-10 11:13:28 -05:00
|
|
|
|
|
2019-08-09 16:25:59 -04:00
|
|
|
|
_console->Lock();
|
2021-03-10 11:13:28 -05:00
|
|
|
|
|
2019-08-09 16:25:59 -04:00
|
|
|
|
_console->GetBatteryManager()->SetBatteryProvider(shared_from_this());
|
|
|
|
|
_console->GetNotificationManager()->RegisterNotificationListener(shared_from_this());
|
|
|
|
|
ApplySettings();
|
|
|
|
|
|
|
|
|
|
//TODO
|
|
|
|
|
//Disable auto-configure input option (otherwise the movie file's input types are ignored)
|
|
|
|
|
//bool autoConfigureInput = _console->GetSettings()->CheckFlag(EmulationFlags::AutoConfigureInput);
|
|
|
|
|
//_console->GetSettings()->ClearFlags(EmulationFlags::AutoConfigureInput);
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
ControlManager *controlManager = _console->GetControlManager().get();
|
|
|
|
|
if(controlManager) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
//ControlManager can be empty if no game is loaded
|
|
|
|
|
controlManager->SetPollCounter(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//bool gameLoaded = LoadGame();
|
|
|
|
|
//TODO
|
|
|
|
|
//_console->GetSettings()->SetFlagState(EmulationFlags::AutoConfigureInput, autoConfigureInput);
|
|
|
|
|
|
|
|
|
|
/*if(!gameLoaded) {
|
|
|
|
|
_console->Unlock();
|
|
|
|
|
return false;
|
|
|
|
|
}*/
|
|
|
|
|
|
2019-10-13 11:57:15 -04:00
|
|
|
|
_originalCheats = _console->GetCheatManager()->GetCheats();
|
2019-10-16 20:22:45 -04:00
|
|
|
|
|
|
|
|
|
controlManager->UpdateControlDevices();
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(!_forTest) {
|
2019-10-16 20:22:45 -04:00
|
|
|
|
_console->PowerCycle();
|
2021-03-10 11:13:28 -05:00
|
|
|
|
} else {
|
2019-10-16 20:22:45 -04:00
|
|
|
|
controlManager->RegisterInputProvider(this);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
LoadCheats();
|
2019-08-09 16:25:59 -04:00
|
|
|
|
|
|
|
|
|
stringstream saveStateData;
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(_reader->GetStream("SaveState.mss", saveStateData)) {
|
|
|
|
|
if(!_console->GetSaveStateManager()->LoadState(saveStateData, true)) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
_console->Resume();
|
|
|
|
|
return false;
|
2021-03-10 11:13:28 -05:00
|
|
|
|
} else {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
_console->GetControlManager()->SetPollCounter(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_playing = true;
|
|
|
|
|
|
|
|
|
|
_console->Unlock();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
template<typename T>
|
|
|
|
|
T FromString(string name, const vector<string> &enumNames, T defaultValue)
|
2019-08-09 16:25:59 -04:00
|
|
|
|
{
|
2021-03-10 11:13:28 -05:00
|
|
|
|
for(size_t i = 0; i < enumNames.size(); i++) {
|
|
|
|
|
if(name == enumNames[i]) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
return (T)i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return defaultValue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
void MesenMovie::ParseSettings(stringstream &data)
|
2019-08-09 16:25:59 -04:00
|
|
|
|
{
|
2021-03-10 11:13:28 -05:00
|
|
|
|
while(!data.eof()) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
string line;
|
|
|
|
|
std::getline(data, line);
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(!line.empty()) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
size_t index = line.find_first_of(' ');
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(index != string::npos) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
string name = line.substr(0, index);
|
|
|
|
|
string value = line.substr(index + 1);
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(name == "Cheat") {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
_cheats.push_back(value);
|
2021-03-10 11:13:28 -05:00
|
|
|
|
} else {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
_settings[name] = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool MesenMovie::LoadGame()
|
|
|
|
|
{
|
|
|
|
|
/*string mesenVersion = LoadString(_settings, MovieKeys::MesenVersion);
|
|
|
|
|
string gameFile = LoadString(_settings, MovieKeys::GameFile);
|
|
|
|
|
string sha1Hash = LoadString(_settings, MovieKeys::Sha1);
|
|
|
|
|
//string patchFile = LoadString(_settings, MovieKeys::PatchFile);
|
|
|
|
|
//string patchFileSha1 = LoadString(_settings, MovieKeys::PatchFileSha1);
|
|
|
|
|
//string patchedRomSha1 = LoadString(_settings, MovieKeys::PatchedRomSha1);
|
|
|
|
|
|
|
|
|
|
if(_console->GetSettings()->CheckFlag(EmulationFlags::AllowMismatchingSaveState) && _console->GetRomInfo().RomName == gameFile) {
|
|
|
|
|
//Loaded game has the right name, and we don't want to validate the hash values
|
|
|
|
|
_console->PowerCycle();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HashInfo hashInfo;
|
|
|
|
|
hashInfo.Sha1 = sha1Hash;
|
|
|
|
|
|
|
|
|
|
VirtualFile romFile = _console->FindMatchingRom(gameFile, hashInfo);
|
|
|
|
|
bool gameLoaded = false;
|
|
|
|
|
if(romFile.IsValid()) {
|
|
|
|
|
VirtualFile patchFile(_movieFile.GetFilePath(), "PatchData.dat");
|
|
|
|
|
if(patchFile.IsValid()) {
|
|
|
|
|
gameLoaded = _console->Initialize(romFile, patchFile);
|
|
|
|
|
} else {
|
|
|
|
|
gameLoaded = _console->Initialize(romFile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return gameLoaded;*/
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MesenMovie::ApplySettings()
|
|
|
|
|
{
|
|
|
|
|
EmuSettings* settings = _console->GetSettings().get();
|
|
|
|
|
EmulationConfig emuConfig = settings->GetEmulationConfig();
|
|
|
|
|
InputConfig inputConfig = settings->GetInputConfig();
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
inputConfig.Controllers[0].Type = FromString(LoadString(_settings, MovieKeys::Controller1), ControllerTypeNames, ControllerType::None);
|
|
|
|
|
inputConfig.Controllers[1].Type = FromString(LoadString(_settings, MovieKeys::Controller2), ControllerTypeNames, ControllerType::None);
|
|
|
|
|
inputConfig.Controllers[2].Type = FromString(LoadString(_settings, MovieKeys::Controller3), ControllerTypeNames, ControllerType::None);
|
|
|
|
|
inputConfig.Controllers[3].Type = FromString(LoadString(_settings, MovieKeys::Controller4), ControllerTypeNames, ControllerType::None);
|
|
|
|
|
inputConfig.Controllers[4].Type = FromString(LoadString(_settings, MovieKeys::Controller5), ControllerTypeNames, ControllerType::None);
|
2019-08-09 16:25:59 -04:00
|
|
|
|
|
|
|
|
|
emuConfig.Region = FromString(LoadString(_settings, MovieKeys::Region), ConsoleRegionNames, ConsoleRegion::Ntsc);
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(!_forTest) {
|
|
|
|
|
emuConfig.RamPowerOnState = FromString(LoadString(_settings, MovieKeys::RamPowerOnState), RamStateNames, RamState::AllOnes);
|
2019-10-16 20:22:45 -04:00
|
|
|
|
}
|
2019-08-09 16:25:59 -04:00
|
|
|
|
emuConfig.PpuExtraScanlinesAfterNmi = LoadInt(_settings, MovieKeys::ExtraScanlinesAfterNmi);
|
|
|
|
|
emuConfig.PpuExtraScanlinesBeforeNmi = LoadInt(_settings, MovieKeys::ExtraScanlinesBeforeNmi);
|
2019-10-20 20:05:39 -04:00
|
|
|
|
emuConfig.GsuClockSpeed = LoadInt(_settings, MovieKeys::GsuClockSpeed, 100);
|
2019-08-09 16:25:59 -04:00
|
|
|
|
|
|
|
|
|
settings->SetEmulationConfig(emuConfig);
|
|
|
|
|
settings->SetInputConfig(inputConfig);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
uint32_t MesenMovie::LoadInt(std::unordered_map<string, string> &settings, string name, uint32_t defaultValue)
|
2019-08-09 16:25:59 -04:00
|
|
|
|
{
|
|
|
|
|
auto result = settings.find(name);
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(result != settings.end()) {
|
|
|
|
|
try {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
return (uint32_t)std::stoul(result->second);
|
2021-03-10 11:13:28 -05:00
|
|
|
|
} catch(std::exception&) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
MessageManager::Log("[Movies] Invalid value for tag: " + name);
|
|
|
|
|
return defaultValue;
|
|
|
|
|
}
|
2021-03-10 11:13:28 -05:00
|
|
|
|
} else {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
return defaultValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
bool MesenMovie::LoadBool(std::unordered_map<string, string> &settings, string name)
|
2019-08-09 16:25:59 -04:00
|
|
|
|
{
|
|
|
|
|
auto result = settings.find(name);
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(result != settings.end()) {
|
|
|
|
|
if(result->second == "true") {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
return true;
|
2021-03-10 11:13:28 -05:00
|
|
|
|
} else if(result->second == "false") {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
return false;
|
2021-03-10 11:13:28 -05:00
|
|
|
|
} else {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
MessageManager::Log("[Movies] Invalid value for tag: " + name);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-03-10 11:13:28 -05:00
|
|
|
|
} else {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
string MesenMovie::LoadString(std::unordered_map<string, string> &settings, string name)
|
2019-08-09 16:25:59 -04:00
|
|
|
|
{
|
|
|
|
|
auto result = settings.find(name);
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(result != settings.end()) {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
return result->second;
|
2021-03-10 11:13:28 -05:00
|
|
|
|
} else {
|
2019-08-09 16:25:59 -04:00
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-13 11:57:15 -04:00
|
|
|
|
|
|
|
|
|
void MesenMovie::LoadCheats()
|
|
|
|
|
{
|
|
|
|
|
vector<CheatCode> cheats;
|
2021-03-10 11:13:28 -05:00
|
|
|
|
for(string cheatData : _cheats) {
|
2019-10-13 11:57:15 -04:00
|
|
|
|
CheatCode code;
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(LoadCheat(cheatData, code)) {
|
2019-10-13 11:57:15 -04:00
|
|
|
|
cheats.push_back(code);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_console->GetCheatManager()->SetCheats(cheats);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
bool MesenMovie::LoadCheat(string cheatData, CheatCode &code)
|
2019-10-13 11:57:15 -04:00
|
|
|
|
{
|
|
|
|
|
vector<string> data = StringUtilities::Split(cheatData, ' ');
|
|
|
|
|
|
2021-03-10 11:13:28 -05:00
|
|
|
|
if(data.size() == 2) {
|
2019-10-13 11:57:15 -04:00
|
|
|
|
code.Address = HexUtilities::FromHex(data[0]);
|
|
|
|
|
code.Value = HexUtilities::FromHex(data[1]);
|
|
|
|
|
return true;
|
2021-03-10 11:13:28 -05:00
|
|
|
|
} else {
|
2019-10-13 11:57:15 -04:00
|
|
|
|
MessageManager::Log("[Movie] Invalid cheat definition: " + cheatData);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|