Added movies recording/playback support

This commit is contained in:
Sour 2019-08-09 16:25:59 -04:00
parent cae71b7907
commit ed4a0e60e8
45 changed files with 1679 additions and 167 deletions

View file

@ -8,6 +8,7 @@
#include "MessageManager.h"
#include "Console.h"
#include "EmuSettings.h"
#include "BatteryManager.h"
#include "NecDsp.h"
#include "Sa1.h"
#include "Gsu.h"
@ -265,23 +266,19 @@ CartFlags::CartFlags BaseCartridge::GetCartFlags()
void BaseCartridge::LoadBattery()
{
string saveFilePath = FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), FolderUtilities::GetFilename(((VirtualFile)_romPath).GetFileName(), false) + ".srm");
if(_saveRamSize > 0) {
VirtualFile saveFile(saveFilePath);
saveFile.ReadFile(_saveRam, _saveRamSize);
_console->GetBatteryManager()->LoadBattery(".srm", _saveRam, _saveRamSize);
} else if(_coprocessor && _hasBattery) {
_coprocessor->LoadBattery(saveFilePath);
_coprocessor->LoadBattery();
}
}
void BaseCartridge::SaveBattery()
{
string saveFilePath = FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), FolderUtilities::GetFilename(((VirtualFile)_romPath).GetFileName(), false) + ".srm");
if(_saveRamSize > 0) {
ofstream saveFile(saveFilePath, ios::binary);
saveFile.write((char*)_saveRam, _saveRamSize);
_console->GetBatteryManager()->SaveBattery(".srm", _saveRam, _saveRamSize);
} else if(_coprocessor && _hasBattery) {
_coprocessor->SaveBattery(saveFilePath);
_coprocessor->SaveBattery();
}
}

View file

@ -103,10 +103,13 @@ void BaseControlDevice::SetTextState(string textState)
int i = 0;
for(char c : textState) {
if(c != '.') {
SetBit(i);
if(c != ':') {
//Ignore colons (used by multitap to separate inputs)
if(c != '.') {
SetBit(i);
}
i++;
}
i++;
}
}
}
@ -125,8 +128,15 @@ string BaseControlDevice::GetTextState()
output += std::to_string(pos.X) + " " + std::to_string(pos.Y) + " ";
}
int keyNumber = 0;
for(size_t i = 0; i < keyNames.size(); i++) {
output += IsPressed((uint8_t)i) ? keyNames[i] : '.';
if(keyNames[i] != ':') {
//Ignore colons in string (used by multitap to split controllers)
output += IsPressed((uint8_t)keyNumber) ? keyNames[i] : '.';
keyNumber++;
} else {
output += ':';
}
}
return output;

View file

@ -9,6 +9,6 @@ public:
virtual void Reset() = 0;
virtual void ProcessEndOfFrame() { }
virtual void LoadBattery(string filePath) { }
virtual void SaveBattery(string filePath) { }
virtual void LoadBattery() { }
virtual void SaveBattery() { }
};

65
Core/BatteryManager.cpp Normal file
View file

@ -0,0 +1,65 @@
#include "stdafx.h"
#include "BatteryManager.h"
#include "../Utilities/VirtualFile.h"
#include "../Utilities/FolderUtilities.h"
void BatteryManager::Initialize(string romName)
{
_romName = romName;
}
string BatteryManager::GetBasePath()
{
return FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), _romName);
}
void BatteryManager::SetBatteryProvider(shared_ptr<IBatteryProvider> provider)
{
_provider = provider;
}
void BatteryManager::SetBatteryRecorder(shared_ptr<IBatteryRecorder> recorder)
{
_recorder = recorder;
}
void BatteryManager::SaveBattery(string extension, uint8_t* data, uint32_t length)
{
ofstream out(GetBasePath() + extension, ios::binary);
if(out) {
out.write((char*)data, length);
}
}
vector<uint8_t> BatteryManager::LoadBattery(string extension)
{
shared_ptr<IBatteryProvider> provider = _provider.lock();
vector<uint8_t> batteryData;
if(provider) {
//Used by movie player to provider initial state of ram at startup
batteryData = provider->LoadBattery(extension);
} else {
VirtualFile file = GetBasePath() + extension;
if(file.IsValid()) {
file.ReadFile(batteryData);
}
}
if(!batteryData.empty()) {
shared_ptr<IBatteryRecorder> recorder = _recorder.lock();
if(recorder) {
//Used by movies to record initial state of battery-backed ram at power on
recorder->OnLoadBattery(extension, batteryData);
}
}
return batteryData;
}
void BatteryManager::LoadBattery(string extension, uint8_t* data, uint32_t length)
{
vector<uint8_t> batteryData = LoadBattery(extension);
memset(data, 0, length);
memcpy(data, batteryData.data(), std::min((uint32_t)batteryData.size(), length));
}

35
Core/BatteryManager.h Normal file
View file

@ -0,0 +1,35 @@
#pragma once
#include "stdafx.h"
class IBatteryProvider
{
public:
virtual vector<uint8_t> LoadBattery(string extension) = 0;
};
class IBatteryRecorder
{
public:
virtual void OnLoadBattery(string extension, vector<uint8_t> batteryData) = 0;
};
class BatteryManager
{
private:
string _romName;
std::weak_ptr<IBatteryProvider> _provider;
std::weak_ptr<IBatteryRecorder> _recorder;
string GetBasePath();
public:
void Initialize(string romName);
void SetBatteryProvider(shared_ptr<IBatteryProvider> provider);
void SetBatteryRecorder(shared_ptr<IBatteryRecorder> recorder);
void SaveBattery(string extension, uint8_t* data, uint32_t length);
vector<uint8_t> LoadBattery(string extension);
void LoadBattery(string extension, uint8_t* data, uint32_t length);
};

View file

@ -27,6 +27,8 @@
#include "CartTypes.h"
#include "RewindManager.h"
#include "ConsoleLock.h"
#include "MovieManager.h"
#include "BatteryManager.h"
#include "../Utilities/Serializer.h"
#include "../Utilities/Timer.h"
#include "../Utilities/VirtualFile.h"
@ -53,6 +55,7 @@ void Console::Initialize()
_lockCounter = 0;
_notificationManager.reset(new NotificationManager());
_batteryManager.reset(new BatteryManager());
_videoDecoder.reset(new VideoDecoder(shared_from_this()));
_videoRenderer.reset(new VideoRenderer(shared_from_this()));
_saveStateManager.reset(new SaveStateManager(shared_from_this()));
@ -153,6 +156,8 @@ void Console::Run()
}
}
MovieManager::Stop();
_emulationThreadId = thread::id();
PlatformUtilities::RestoreTimerResolution();
@ -285,6 +290,8 @@ bool Console::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom)
}
_cart = cart;
_batteryManager->Initialize(FolderUtilities::GetFilename(romFile.GetFileName(), false));
UpdateRegion();
_internalRegisters.reset(new InternalRegisters());
@ -550,6 +557,11 @@ shared_ptr<DebugHud> Console::GetDebugHud()
return _debugHud;
}
shared_ptr<BatteryManager> Console::GetBatteryManager()
{
return _batteryManager;
}
shared_ptr<Cpu> Console::GetCpu()
{
return _cpu;

View file

@ -23,6 +23,7 @@ class NotificationManager;
class EmuSettings;
class SaveStateManager;
class RewindManager;
class BatteryManager;
enum class MemoryOperationType;
enum class SnesMemoryType;
enum class EventType;
@ -43,6 +44,7 @@ private:
shared_ptr<Debugger> _debugger;
shared_ptr<NotificationManager> _notificationManager;
shared_ptr<BatteryManager> _batteryManager;
shared_ptr<SoundMixer> _soundMixer;
shared_ptr<VideoRenderer> _videoRenderer;
shared_ptr<VideoDecoder> _videoDecoder;
@ -110,6 +112,7 @@ public:
shared_ptr<SaveStateManager> GetSaveStateManager();
shared_ptr<RewindManager> GetRewindManager();
shared_ptr<DebugHud> GetDebugHud();
shared_ptr<BatteryManager> GetBatteryManager();
shared_ptr<Cpu> GetCpu();
shared_ptr<Ppu> GetPpu();

View file

@ -126,7 +126,7 @@ void ControlManager::UpdateControlDevices()
auto lock = _deviceLock.AcquireSafe();
_controlDevices.clear();
RegisterControlDevice(_systemActionManager);
for(int i = 0; i < 4; i++) {
for(int i = 0; i < 2; i++) {
shared_ptr<BaseControlDevice> device = CreateControllerDevice(GetControllerType(i), i, _console);
if(device) {
RegisterControlDevice(device);
@ -207,7 +207,7 @@ void ControlManager::Write(uint16_t addr, uint8_t value)
void ControlManager::Serialize(Serializer &s)
{
InputConfig cfg = _console->GetSettings()->GetInputConfig();
s.Stream(cfg.Controllers[0].Type, cfg.Controllers[1].Type, cfg.Controllers[2].Type, cfg.Controllers[3].Type);
s.Stream(cfg.Controllers[0].Type, cfg.Controllers[1].Type, cfg.Controllers[2].Type, cfg.Controllers[3].Type, cfg.Controllers[4].Type);
if(!s.IsSaving()) {
_console->GetSettings()->SetInputConfig(cfg);
UpdateControlDevices();

View file

@ -20,7 +20,6 @@ private:
vector<IInputRecorder*> _inputRecorders;
vector<IInputProvider*> _inputProviders;
//TODO: Static so that power cycle does not reset its value
uint32_t _pollCounter;
uint32_t _inputConfigVersion;

View file

@ -48,6 +48,7 @@
<ClInclude Include="BaseCartridge.h" />
<ClInclude Include="BaseControlDevice.h" />
<ClInclude Include="BaseCoprocessor.h" />
<ClInclude Include="BatteryManager.h" />
<ClInclude Include="Cpu.Shared.h" />
<ClInclude Include="CpuBwRamHandler.h" />
<ClInclude Include="CpuDebugger.h" />
@ -95,7 +96,11 @@
<ClInclude Include="LuaCallHelper.h" />
<ClInclude Include="LuaScriptingContext.h" />
<ClInclude Include="MemoryAccessCounter.h" />
<ClInclude Include="MovieTypes.h" />
<ClInclude Include="Multitap.h" />
<ClInclude Include="MesenMovie.h" />
<ClInclude Include="MovieManager.h" />
<ClInclude Include="MovieRecorder.h" />
<ClInclude Include="NecDsp.h" />
<ClInclude Include="NecDspDisUtils.h" />
<ClInclude Include="NecDspTypes.h" />
@ -180,6 +185,7 @@
<ClCompile Include="BaseRenderer.cpp" />
<ClCompile Include="BaseSoundManager.cpp" />
<ClCompile Include="BaseVideoFilter.cpp" />
<ClCompile Include="BatteryManager.cpp" />
<ClCompile Include="Breakpoint.cpp" />
<ClCompile Include="BreakpointManager.cpp" />
<ClCompile Include="CallstackManager.cpp" />
@ -217,7 +223,10 @@
<ClCompile Include="MemoryDumper.cpp" />
<ClCompile Include="MemoryManager.cpp" />
<ClCompile Include="MemoryMappings.cpp" />
<ClCompile Include="MesenMovie.cpp" />
<ClCompile Include="MessageManager.cpp" />
<ClCompile Include="MovieManager.cpp" />
<ClCompile Include="MovieRecorder.cpp" />
<ClCompile Include="NecDsp.cpp" />
<ClCompile Include="NecDspDisUtils.cpp" />
<ClCompile Include="NotificationManager.cpp" />

View file

@ -152,9 +152,6 @@
<ClInclude Include="ControlManager.h">
<Filter>SNES\Input</Filter>
</ClInclude>
<ClInclude Include="FrameLimiter.h">
<Filter>SNES</Filter>
</ClInclude>
<ClInclude Include="CodeDataLogger.h">
<Filter>Debugger</Filter>
</ClInclude>
@ -383,6 +380,24 @@
<ClInclude Include="Multitap.h">
<Filter>SNES\Input</Filter>
</ClInclude>
<ClInclude Include="MesenMovie.h">
<Filter>Movies</Filter>
</ClInclude>
<ClInclude Include="MovieManager.h">
<Filter>Movies</Filter>
</ClInclude>
<ClInclude Include="MovieRecorder.h">
<Filter>Movies</Filter>
</ClInclude>
<ClInclude Include="MovieTypes.h">
<Filter>Movies</Filter>
</ClInclude>
<ClInclude Include="BatteryManager.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="FrameLimiter.h">
<Filter>Misc</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -620,6 +635,18 @@
<ClCompile Include="Obc1.cpp">
<Filter>SNES\Coprocessors\OBC1</Filter>
</ClCompile>
<ClCompile Include="MesenMovie.cpp">
<Filter>Movies</Filter>
</ClCompile>
<ClCompile Include="MovieRecorder.cpp">
<Filter>Movies</Filter>
</ClCompile>
<ClCompile Include="MovieManager.cpp">
<Filter>Movies</Filter>
</ClCompile>
<ClCompile Include="BatteryManager.cpp">
<Filter>Misc</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">
@ -679,5 +706,8 @@
<Filter Include="SNES\Coprocessors\OBC1">
<UniqueIdentifier>{244dec9a-54e9-478d-91a2-ab3ab5f6db1c}</UniqueIdentifier>
</Filter>
<Filter Include="Movies">
<UniqueIdentifier>{e6a0a09a-5e38-4a63-83bf-095f297b7633}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View file

@ -9,6 +9,7 @@
#include "GsuRamHandler.h"
#include "EmuSettings.h"
#include "MessageManager.h"
#include "BatteryManager.h"
#include "../Utilities/HexUtilities.h"
Gsu::Gsu(Console *console, uint32_t gsuRamSize)
@ -616,16 +617,14 @@ void Gsu::Serialize(Serializer &s)
s.StreamArray(_gsuRam, _gsuRamSize);
}
void Gsu::LoadBattery(string filePath)
void Gsu::LoadBattery()
{
VirtualFile saveFile(filePath);
saveFile.ReadFile((uint8_t*)_gsuRam, _gsuRamSize);
_console->GetBatteryManager()->LoadBattery(".srm", (uint8_t*)_gsuRam, _gsuRamSize);
}
void Gsu::SaveBattery(string filePath)
void Gsu::SaveBattery()
{
ofstream saveFile(filePath, ios::binary);
saveFile.write((char*)_gsuRam, _gsuRamSize);
_console->GetBatteryManager()->SaveBattery(".srm", (uint8_t*)_gsuRam, _gsuRamSize);
}
GsuState Gsu::GetState()

View file

@ -151,8 +151,8 @@ public:
uint8_t ReadGsu(uint32_t addr, MemoryOperationType opType);
void WriteGsu(uint32_t addr, uint8_t value, MemoryOperationType opType);
void LoadBattery(string filePath) override;
void SaveBattery(string filePath) override;
void LoadBattery() override;
void SaveBattery() override;
void Run();
void Reset() override;

285
Core/MesenMovie.cpp Normal file
View file

@ -0,0 +1,285 @@
#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"
MesenMovie::MesenMovie(shared_ptr<Console> console)
{
_console = console;
}
MesenMovie::~MesenMovie()
{
Stop();
}
void MesenMovie::Stop()
{
if(_playing) {
MessageManager::DisplayMessage("Movies", "MovieEnded");
if(_console->GetSettings()->GetPreferences().PauseOnMovieEnd) {
_console->Pause();
}
_playing = false;
}
_console->GetControlManager()->UnregisterInputProvider(this);
}
bool MesenMovie::SetInput(BaseControlDevice *device)
{
uint32_t inputRowIndex = _console->GetControlManager()->GetPollCounter();
_lastPollCounter = inputRowIndex;
if(_inputData.size() > inputRowIndex && _inputData[inputRowIndex].size() > _deviceIndex) {
device->SetTextState(_inputData[inputRowIndex][_deviceIndex]);
_deviceIndex++;
if(_deviceIndex >= _inputData[inputRowIndex].size()) {
//Move to the next frame's data
_deviceIndex = 0;
}
} else {
MovieManager::Stop();
}
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;
}
void MesenMovie::ProcessNotification(ConsoleNotificationType type, void * parameter)
{
if(type == ConsoleNotificationType::GameLoaded) {
_console->GetControlManager()->RegisterInputProvider(this);
_console->GetControlManager()->SetPollCounter(_lastPollCounter + 1);
}
}
bool MesenMovie::Play(VirtualFile &file)
{
_movieFile = file;
std::stringstream ss;
file.ReadFile(ss);
_reader.reset(new ZipReader());
_reader->LoadArchive(ss);
stringstream settingsData, inputData;
if(!_reader->GetStream("GameSettings.txt", settingsData)) {
MessageManager::Log("[Movie] File not found: GameSettings.txt");
return false;
}
if(!_reader->GetStream("Input.txt", inputData)) {
MessageManager::Log("[Movie] File not found: Input.txt");
return false;
}
while(inputData) {
string line;
std::getline(inputData, line);
if(line.substr(0, 1) == "|") {
_inputData.push_back(StringUtilities::Split(line.substr(1), '|'));
}
}
_deviceIndex = 0;
ParseSettings(settingsData);
_console->Lock();
_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);
ControlManager *controlManager = _console->GetControlManager().get();
if(controlManager) {
//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;
}*/
_console->PowerCycle();
stringstream saveStateData;
if(_reader->GetStream("SaveState.mss", saveStateData)) {
if(!_console->GetSaveStateManager()->LoadState(saveStateData, true)) {
_console->Resume();
return false;
} else {
_console->GetControlManager()->SetPollCounter(0);
}
}
_playing = true;
_console->Unlock();
return true;
}
template<typename T>
T FromString(string name, const vector<string> &enumNames, T defaultValue)
{
for(size_t i = 0; i < enumNames.size(); i++) {
if(name == enumNames[i]) {
return (T)i;
}
}
return defaultValue;
}
void MesenMovie::ParseSettings(stringstream &data)
{
while(!data.eof()) {
string line;
std::getline(data, line);
if(!line.empty()) {
size_t index = line.find_first_of(' ');
if(index != string::npos) {
string name = line.substr(0, index);
string value = line.substr(index + 1);
if(name == "Cheat") {
_cheats.push_back(value);
} else {
_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();
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);
emuConfig.Region = FromString(LoadString(_settings, MovieKeys::Region), ConsoleRegionNames, ConsoleRegion::Ntsc);
emuConfig.RamPowerOnState = FromString(LoadString(_settings, MovieKeys::RamPowerOnState), RamStateNames, RamState::AllOnes);
emuConfig.PpuExtraScanlinesAfterNmi = LoadInt(_settings, MovieKeys::ExtraScanlinesAfterNmi);
emuConfig.PpuExtraScanlinesBeforeNmi = LoadInt(_settings, MovieKeys::ExtraScanlinesBeforeNmi);
settings->SetEmulationConfig(emuConfig);
settings->SetInputConfig(inputConfig);
}
uint32_t MesenMovie::LoadInt(std::unordered_map<string, string> &settings, string name, uint32_t defaultValue)
{
auto result = settings.find(name);
if(result != settings.end()) {
try {
return (uint32_t)std::stoul(result->second);
} catch(std::exception ex) {
MessageManager::Log("[Movies] Invalid value for tag: " + name);
return defaultValue;
}
} else {
return defaultValue;
}
}
bool MesenMovie::LoadBool(std::unordered_map<string, string> &settings, string name)
{
auto result = settings.find(name);
if(result != settings.end()) {
if(result->second == "true") {
return true;
} else if(result->second == "false") {
return false;
} else {
MessageManager::Log("[Movies] Invalid value for tag: " + name);
return false;
}
} else {
return false;
}
}
string MesenMovie::LoadString(std::unordered_map<string, string> &settings, string name)
{
auto result = settings.find(name);
if(result != settings.end()) {
return result->second;
} else {
return "";
}
}

50
Core/MesenMovie.h Normal file
View file

@ -0,0 +1,50 @@
#pragma once
#include "stdafx.h"
#include "MovieManager.h"
#include "../Utilities/VirtualFile.h"
#include "BatteryManager.h"
#include "INotificationListener.h"
class ZipReader;
class Console;
class MesenMovie : public IMovie, public INotificationListener, public IBatteryProvider, public std::enable_shared_from_this<MesenMovie>
{
private:
shared_ptr<Console> _console;
VirtualFile _movieFile;
shared_ptr<ZipReader> _reader;
bool _playing = false;
size_t _deviceIndex = 0;
uint32_t _lastPollCounter = 0;
vector<vector<string>> _inputData;
vector<string> _cheats;
std::unordered_map<string, string> _settings;
string _filename;
private:
void ParseSettings(stringstream &data);
void ApplySettings();
bool LoadGame();
void Stop();
uint32_t LoadInt(std::unordered_map<string, string> &settings, string name, uint32_t defaultValue = 0);
bool LoadBool(std::unordered_map<string, string> &settings, string name);
string LoadString(std::unordered_map<string, string> &settings, string name);
public:
MesenMovie(shared_ptr<Console> console);
virtual ~MesenMovie();
bool Play(VirtualFile &file) override;
bool SetInput(BaseControlDevice* device) override;
bool IsPlaying() override;
//Inherited via IBatteryProvider
vector<uint8_t> LoadBattery(string extension) override;
//Inherited via INotificationListener
void ProcessNotification(ConsoleNotificationType type, void * parameter) override;
};

View file

@ -33,6 +33,7 @@ std::unordered_map<string, string> MessageManager::_enResources = {
{ "CouldNotConnect", u8"Could not connect to the server." },
{ "CouldNotInitializeAudioSystem", u8"Could not initialize audio system" },
{ "CouldNotFindRom", u8"Could not find matching game ROM." },
{ "CouldNotWriteToFile", u8"Could not write to file: %1" },
{ "CouldNotLoadFile", u8"Could not load file: %1" },
{ "EmulationMaximumSpeed", u8"Maximum speed" },
{ "EmulationSpeedPercent", u8"%1%" },

62
Core/MovieManager.cpp Normal file
View file

@ -0,0 +1,62 @@
#include "stdafx.h"
#include "../Utilities/FolderUtilities.h"
#include "../Utilities/VirtualFile.h"
#include "../Utilities/ZipReader.h"
#include "MovieManager.h"
#include "MesenMovie.h"
#include "MovieRecorder.h"
shared_ptr<IMovie> MovieManager::_player;
shared_ptr<MovieRecorder> MovieManager::_recorder;
void MovieManager::Record(RecordMovieOptions options, shared_ptr<Console> console)
{
shared_ptr<MovieRecorder> recorder(new MovieRecorder(console));
if(recorder->Record(options)) {
_recorder = recorder;
}
}
void MovieManager::Play(VirtualFile file, shared_ptr<Console> console)
{
vector<uint8_t> fileData;
if(file.IsValid() && file.ReadFile(fileData)) {
shared_ptr<IMovie> player;
if(memcmp(fileData.data(), "PK", 2) == 0) {
//Mesen movie
ZipReader reader;
reader.LoadArchive(fileData);
vector<string> files = reader.GetFileList();
if(std::find(files.begin(), files.end(), "GameSettings.txt") != files.end()) {
player.reset(new MesenMovie(console));
}
}
if(player && player->Play(file)) {
_player = player;
MessageManager::DisplayMessage("Movies", "MoviePlaying", file.GetFileName());
}
}
}
void MovieManager::Stop()
{
_player.reset();
if(_recorder) {
_recorder->Stop();
_recorder.reset();
}
}
bool MovieManager::Playing()
{
shared_ptr<IMovie> player = _player;
return player && player->IsPlaying();
}
bool MovieManager::Recording()
{
return _recorder != nullptr;
}

30
Core/MovieManager.h Normal file
View file

@ -0,0 +1,30 @@
#pragma once
#include "stdafx.h"
#include "MessageManager.h"
#include "IInputProvider.h"
#include "MovieTypes.h"
class MovieRecorder;
class VirtualFile;
class Console;
class IMovie : public IInputProvider
{
public:
virtual bool Play(VirtualFile &file) = 0;
virtual bool IsPlaying() = 0;
};
class MovieManager
{
private:
static shared_ptr<IMovie> _player;
static shared_ptr<MovieRecorder> _recorder;
public:
static void Record(RecordMovieOptions options, shared_ptr<Console> console);
static void Play(VirtualFile file, shared_ptr<Console> console);
static void Stop();
static bool Playing();
static bool Recording();
};

234
Core/MovieRecorder.cpp Normal file
View file

@ -0,0 +1,234 @@
#include "stdafx.h"
#include <deque>
#include "../Utilities/HexUtilities.h"
#include "../Utilities/FolderUtilities.h"
#include "../Utilities/ZipWriter.h"
#include "../Utilities/VirtualFile.h"
#include "MovieRecorder.h"
#include "MessageManager.h"
#include "ControlManager.h"
#include "BaseControlDevice.h"
#include "Console.h"
#include "EmuSettings.h"
#include "SaveStateManager.h"
#include "NotificationManager.h"
#include "RewindData.h"
#include "MovieTypes.h"
#include "BatteryManager.h"
MovieRecorder::MovieRecorder(shared_ptr<Console> console)
{
_console = console;
}
MovieRecorder::~MovieRecorder()
{
}
bool MovieRecorder::Record(RecordMovieOptions options)
{
_filename = options.Filename;
_author = options.Author;
_description = options.Description;
_writer.reset(new ZipWriter());
_inputData = stringstream();
_saveStateData = stringstream();
_hasSaveState = false;
if(!_writer->Initialize(_filename)) {
MessageManager::DisplayMessage("Movies", "CouldNotWriteToFile", FolderUtilities::GetFilename(_filename, true));
_writer.reset();
return false;
} else {
_console->Lock();
_console->GetNotificationManager()->RegisterNotificationListener(shared_from_this());
if(options.RecordFrom == RecordMovieFrom::StartWithoutSaveData) {
//Power cycle and ignore save data that exists on the disk
_console->GetBatteryManager()->SetBatteryProvider(shared_from_this());
_console->PowerCycle();
} else if(options.RecordFrom == RecordMovieFrom::StartWithSaveData) {
//Power cycle and save existing battery files into the movie file
_console->GetBatteryManager()->SetBatteryRecorder(shared_from_this());
_console->PowerCycle();
} else if(options.RecordFrom == RecordMovieFrom::CurrentState) {
//Record from current state, store a save state in the movie file
_console->GetControlManager()->RegisterInputRecorder(this);
_console->GetSaveStateManager()->SaveState(_saveStateData);
_hasSaveState = true;
}
_console->GetBatteryManager()->SetBatteryRecorder(nullptr);
_console->Unlock();
MessageManager::DisplayMessage("Movies", "MovieRecordingTo", FolderUtilities::GetFilename(_filename, true));
return true;
}
}
void MovieRecorder::GetGameSettings(stringstream &out)
{
EmuSettings* settings = _console->GetSettings().get();
EmulationConfig emuConfig = settings->GetEmulationConfig();
InputConfig inputConfig = settings->GetInputConfig();
WriteString(out, MovieKeys::MesenVersion, settings->GetVersionString());
WriteInt(out, MovieKeys::MovieFormatVersion, MovieRecorder::MovieFormatVersion);
VirtualFile romFile = _console->GetRomInfo().RomFile;
WriteString(out, MovieKeys::GameFile, romFile.GetFileName());
WriteString(out, MovieKeys::Sha1, romFile.GetSha1Hash());
VirtualFile patchFile = _console->GetRomInfo().PatchFile;
if(patchFile.IsValid()) {
WriteString(out, MovieKeys::PatchFile, patchFile.GetFileName());
WriteString(out, MovieKeys::PatchFileSha1, patchFile.GetSha1Hash());
romFile.ApplyPatch(patchFile);
WriteString(out, MovieKeys::PatchedRomSha1, romFile.GetSha1Hash());
}
ConsoleRegion region = _console->GetRegion();
switch(region) {
case ConsoleRegion::Auto:
case ConsoleRegion::Ntsc: WriteString(out, MovieKeys::Region, "NTSC"); break;
case ConsoleRegion::Pal: WriteString(out, MovieKeys::Region, "PAL"); break;
}
WriteString(out, MovieKeys::Controller1, ControllerTypeNames[(int)inputConfig.Controllers[0].Type]);
WriteString(out, MovieKeys::Controller2, ControllerTypeNames[(int)inputConfig.Controllers[1].Type]);
WriteString(out, MovieKeys::Controller3, ControllerTypeNames[(int)inputConfig.Controllers[2].Type]);
WriteString(out, MovieKeys::Controller4, ControllerTypeNames[(int)inputConfig.Controllers[3].Type]);
WriteString(out, MovieKeys::Controller5, ControllerTypeNames[(int)inputConfig.Controllers[4].Type]);
WriteInt(out, MovieKeys::ExtraScanlinesBeforeNmi, emuConfig.PpuExtraScanlinesBeforeNmi);
WriteInt(out, MovieKeys::ExtraScanlinesAfterNmi, emuConfig.PpuExtraScanlinesAfterNmi);
switch(emuConfig.RamPowerOnState) {
case RamState::AllZeros: WriteString(out, MovieKeys::RamPowerOnState, "AllZeros"); break;
case RamState::AllOnes: WriteString(out, MovieKeys::RamPowerOnState, "AllOnes"); break;
case RamState::Random: WriteString(out, MovieKeys::RamPowerOnState, "AllOnes"); break; //TODO: Random memory isn't supported for movies yet
}
}
void MovieRecorder::WriteString(stringstream &out, string name, string value)
{
out << name << " " << value << "\n";
}
void MovieRecorder::WriteInt(stringstream &out, string name, uint32_t value)
{
out << name << " " << std::to_string(value) << "\n";
}
void MovieRecorder::WriteBool(stringstream &out, string name, bool enabled)
{
out << name << " " << (enabled ? "true" : "false") << "\n";
}
bool MovieRecorder::Stop()
{
if(_writer) {
_console->GetControlManager()->UnregisterInputRecorder(this);
_writer->AddFile(_inputData, "Input.txt");
stringstream out;
GetGameSettings(out);
_writer->AddFile(out, "GameSettings.txt");
if(!_author.empty() || !_description.empty()) {
stringstream movieInfo;
WriteString(movieInfo, "Author", _author);
movieInfo << "Description\n" << _description;
_writer->AddFile(movieInfo, "MovieInfo.txt");
}
VirtualFile patchFile = _console->GetRomInfo().PatchFile;
vector<uint8_t> patchData;
if(patchFile.IsValid() && patchFile.ReadFile(patchData)) {
_writer->AddFile(patchData, "PatchData.dat");
}
if(_hasSaveState) {
_writer->AddFile(_saveStateData, "SaveState.mss");
}
for(auto kvp : _batteryData) {
_writer->AddFile(kvp.second, "Battery" + kvp.first);
}
bool result = _writer->Save();
if(result) {
MessageManager::DisplayMessage("Movies", "MovieSaved", FolderUtilities::GetFilename(_filename, true));
}
return result;
}
return false;
}
void MovieRecorder::RecordInput(vector<shared_ptr<BaseControlDevice>> devices)
{
for(shared_ptr<BaseControlDevice> &device : devices) {
_inputData << ("|" + device->GetTextState());
}
_inputData << "\n";
}
void MovieRecorder::OnLoadBattery(string extension, vector<uint8_t> batteryData)
{
_batteryData[extension] = batteryData;
}
vector<uint8_t> MovieRecorder::LoadBattery(string extension)
{
return vector<uint8_t>();
}
void MovieRecorder::ProcessNotification(ConsoleNotificationType type, void *parameter)
{
if(type == ConsoleNotificationType::GameLoaded) {
_console->GetControlManager()->RegisterInputRecorder(this);
}
}
/*
bool MovieRecorder::CreateMovie(string movieFile, std::deque<RewindData> &data, uint32_t startPosition, uint32_t endPosition)
{
_filename = movieFile;
_writer.reset(new ZipWriter());
if(startPosition < data.size() && endPosition <= data.size() && _writer->Initialize(_filename)) {
vector<shared_ptr<BaseControlDevice>> devices = _console->GetControlManager()->GetControlDevices();
if(startPosition > 0 || _console->GetRomInfo().HasBattery || _console->GetSettings()->GetRamPowerOnState() == RamPowerOnState::Random) {
//Create a movie from a savestate if we don't start from the beginning (or if the game has save ram, or if the power on ram state is random)
_hasSaveState = true;
_saveStateData = stringstream();
_console->GetSaveStateManager()->GetSaveStateHeader(_saveStateData);
data[startPosition].GetStateData(_saveStateData);
}
_inputData = stringstream();
for(uint32_t i = startPosition; i < endPosition; i++) {
RewindData rewindData = data[i];
for(uint32_t i = 0; i < 30; i++) {
for(shared_ptr<BaseControlDevice> &device : devices) {
uint8_t port = device->GetPort();
if(i < rewindData.InputLogs[port].size()) {
device->SetRawState(rewindData.InputLogs[port][i]);
_inputData << ("|" + device->GetTextState());
}
}
_inputData << "\n";
}
}
//Write the movie file
return Stop();
}
return false;
}
*/

52
Core/MovieRecorder.h Normal file
View file

@ -0,0 +1,52 @@
#pragma once
#include "stdafx.h"
#include <deque>
#include <unordered_map>
#include "IInputRecorder.h"
#include "BatteryManager.h"
#include "INotificationListener.h"
#include "MovieTypes.h"
class ZipWriter;
class Console;
//class RewindData;
//struct CodeInfo;
class MovieRecorder : public INotificationListener, public IInputRecorder, public IBatteryRecorder, public IBatteryProvider, public std::enable_shared_from_this<MovieRecorder>
{
private:
static const uint32_t MovieFormatVersion = 1;
shared_ptr<Console> _console;
string _filename;
string _author;
string _description;
unique_ptr<ZipWriter> _writer;
std::unordered_map<string, vector<uint8_t>> _batteryData;
stringstream _inputData;
bool _hasSaveState = false;
stringstream _saveStateData;
void GetGameSettings(stringstream &out);
//void WriteCheat(stringstream &out, CodeInfo &code);
void WriteString(stringstream &out, string name, string value);
void WriteInt(stringstream &out, string name, uint32_t value);
void WriteBool(stringstream &out, string name, bool enabled);
public:
MovieRecorder(shared_ptr<Console> console);
virtual ~MovieRecorder();
bool Record(RecordMovieOptions options);
bool Stop();
// Inherited via IInputRecorder
void RecordInput(vector<shared_ptr<BaseControlDevice>> devices) override;
// Inherited via IBatteryRecorder
void OnLoadBattery(string extension, vector<uint8_t> batteryData) override;
vector<uint8_t> LoadBattery(string extension) override;
// Inherited via INotificationListener
void ProcessNotification(ConsoleNotificationType type, void *parameter) override;
};

60
Core/MovieTypes.h Normal file
View file

@ -0,0 +1,60 @@
#pragma once
#include "stdafx.h"
enum class RecordMovieFrom
{
StartWithoutSaveData = 0,
StartWithSaveData,
CurrentState
};
struct RecordMovieOptions
{
char Filename[2000] = {};
char Author[250] = {};
char Description[10000] = {};
RecordMovieFrom RecordFrom = RecordMovieFrom::StartWithoutSaveData;
};
const vector<string> ConsoleRegionNames = {
"Auto",
"NTSC",
"PAL"
};
const vector<string> ControllerTypeNames = {
"None",
"SnesController",
"SnesMouse",
"SuperScope",
"Multitap"
};
const vector<string> RamStateNames = {
"AllZeros",
"AllOnes",
"Random"
};
namespace MovieKeys
{
constexpr const char* MesenVersion = "MesenVersion";
constexpr const char* MovieFormatVersion = "MovieFormatVersion";
constexpr const char* GameFile = "GameFile";
constexpr const char* Sha1 = "SHA1";
constexpr const char* PatchFile = "PatchFile";
constexpr const char* PatchFileSha1 = "PatchFileSHA1";
constexpr const char* PatchedRomSha1 = "PatchedRomSHA1";
constexpr const char* Region = "Region";
constexpr const char* ConsoleType = "ConsoleType";
constexpr const char* Controller1 = "Controller1";
constexpr const char* Controller2 = "Controller2";
constexpr const char* Controller3 = "Controller3";
constexpr const char* Controller4 = "Controller4";
constexpr const char* Controller5 = "Controller5";
constexpr const char* ExtraScanlinesBeforeNmi = "ExtraScanlinesBeforeNmi";
constexpr const char* ExtraScanlinesAfterNmi = "ExtraScanlinesAfterNmi";
constexpr const char* RamPowerOnState = "RamPowerOnState";
constexpr const char* InputPollScanline = "InputPollScanline";
};

View file

@ -17,8 +17,8 @@ private:
protected:
string GetKeyNames() override
{
//Repeat key names 4x to
return string("ABXYLRSTUDLR") + "ABXYLRSTUDLR" + "ABXYLRSTUDLR" + "ABXYLRSTUDLR";
//Repeat key names 4x, once for each controller
return "ABXYLRSTUDLR:ABXYLRSTUDLR:ABXYLRSTUDLR:ABXYLRSTUDLR";
}
void InternalSetStateFromInput() override
@ -88,7 +88,8 @@ public:
uint8_t ReadRam(uint16_t addr) override
{
uint8_t portSelect = (_internalRegs->GetIoPortOutput() & 0x80) ? 0 : 2;
uint8_t selectBit = 0x80 >> ((_port == 0) ? 1 : 0);
uint8_t portSelect = (_internalRegs->GetIoPortOutput() & selectBit) ? 0 : 2;
uint8_t output = 0;
if(IsCurrentPort(addr)) {

View file

@ -9,6 +9,7 @@
#include "MessageManager.h"
#include "EmuSettings.h"
#include "RamHandler.h"
#include "BatteryManager.h"
#include "FirmwareHelper.h"
#include "../Utilities/FolderUtilities.h"
@ -105,19 +106,17 @@ void NecDsp::Reset()
_state = {};
}
void NecDsp::LoadBattery(string filePath)
void NecDsp::LoadBattery()
{
if(_type == CoprocessorType::ST010 || _type == CoprocessorType::ST011) {
VirtualFile saveFile(filePath);
saveFile.ReadFile((uint8_t*)_ram, _ramSize * sizeof(uint16_t));
_console->GetBatteryManager()->LoadBattery(".srm", (uint8_t*)_ram, _ramSize * sizeof(uint16_t));
}
}
void NecDsp::SaveBattery(string filePath)
void NecDsp::SaveBattery()
{
if(_type == CoprocessorType::ST010 || _type == CoprocessorType::ST011) {
ofstream saveFile(filePath, ios::binary);
saveFile.write((char*)_ram, _ramSize * sizeof(uint16_t));
_console->GetBatteryManager()->SaveBattery(".srm", (uint8_t*)_ram, _ramSize * sizeof(uint16_t));
}
}

View file

@ -56,8 +56,8 @@ public:
void Reset() override;
void Run();
void LoadBattery(string filePath) override;
void SaveBattery(string filePath) override;
void LoadBattery() override;
void SaveBattery() override;
uint8_t Read(uint32_t addr) override;
void Write(uint32_t addr, uint8_t value) override;

View file

@ -8,6 +8,7 @@
#include "EmuSettings.h"
#include "VideoDecoder.h"
#include "BaseCartridge.h"
#include "MovieManager.h"
SaveStateManager::SaveStateManager(shared_ptr<Console> console)
{
@ -148,7 +149,7 @@ bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired)
//Stop any movie that might have been playing/recording if a state is loaded
//(Note: Loading a state is disabled in the UI while a movie is playing/recording)
//TODO MovieManager::Stop();
MovieManager::Stop();
_console->Deserialize(stream, fileFormatVersion);

View file

@ -144,6 +144,7 @@ struct AudioConfig
double Band20Gain = 0;
};
//Update ControllerTypeNames when changing this
enum class ControllerType
{
None = 0,
@ -277,6 +278,7 @@ struct PreferencesConfig
bool ShowDebugInfo = false;
bool DisableOsd = false;
bool AllowBackgroundInput = false;
bool PauseOnMovieEnd = false;
uint32_t RewindBufferSize = 30;

View file

@ -8,6 +8,7 @@
#include "RewindManager.h"
#include "NotificationManager.h"
#include "SaveStateManager.h"
#include "MovieManager.h"
ShortcutKeyHandler::ShortcutKeyHandler(shared_ptr<Console> console)
{
@ -102,8 +103,8 @@ void ShortcutKeyHandler::CheckMappedKeys()
{
shared_ptr<EmuSettings> settings = _console->GetSettings();
bool isNetplayClient = false; //TODO GameClient::Connected();
//bool isMovieActive = false; //TODO MovieManager::Playing() || MovieManager::Recording();
bool isMovieRecording = false; //TODO MovieManager::Recording();
//bool isMovieActive = MovieManager::Playing() || MovieManager::Recording();
bool isMovieRecording = MovieManager::Recording();
//Let the UI handle these shortcuts
for(uint64_t i = (uint64_t)EmulatorShortcut::TakeScreenshot; i < (uint64_t)EmulatorShortcut::ShortcutCount; i++) {

View file

@ -2,6 +2,7 @@
#include "../Core/Console.h"
#include "../Core/VideoRenderer.h"
#include "../Core/SoundMixer.h"
#include "../Core/MovieManager.h"
extern shared_ptr<Console> _console;
enum class VideoCodec;
@ -15,4 +16,14 @@ extern "C"
DllExport void __stdcall WaveRecord(char* filename) { _console->GetSoundMixer()->StartRecording(filename); }
DllExport void __stdcall WaveStop() { _console->GetSoundMixer()->StopRecording(); }
DllExport bool __stdcall WaveIsRecording() { return _console->GetSoundMixer()->IsRecording(); }
DllExport void __stdcall MoviePlay(char* filename) { MovieManager::Play(string(filename), _console); }
DllExport void __stdcall MovieStop() { MovieManager::Stop(); }
DllExport bool __stdcall MoviePlaying() { return MovieManager::Playing(); }
DllExport bool __stdcall MovieRecording() { return MovieManager::Recording(); }
DllExport void __stdcall MovieRecord(RecordMovieOptions *options)
{
RecordMovieOptions opt = *options;
MovieManager::Record(opt, _console);
}
}

View file

@ -27,6 +27,7 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
$(CORE_DIR)/BaseRenderer.cpp \
$(CORE_DIR)/BaseSoundManager.cpp \
$(CORE_DIR)/BaseVideoFilter.cpp \
$(CORE_DIR)/BatteryManager.cpp \
$(CORE_DIR)/Breakpoint.cpp \
$(CORE_DIR)/BreakpointManager.cpp \
$(CORE_DIR)/CallstackManager.cpp \
@ -61,7 +62,10 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
$(CORE_DIR)/MemoryDumper.cpp \
$(CORE_DIR)/MemoryManager.cpp \
$(CORE_DIR)/MemoryMappings.cpp \
$(CORE_DIR)/MesenMovie.cpp \
$(CORE_DIR)/MessageManager.cpp \
$(CORE_DIR)/MovieManager.cpp \
$(CORE_DIR)/MovieRecorder.cpp \
$(CORE_DIR)/NecDsp.cpp \
$(CORE_DIR)/NecDspDisUtils.cpp \
$(CORE_DIR)/NotificationManager.cpp \

View file

@ -23,6 +23,7 @@ namespace Mesen.GUI.Config
public DebugInfo Debug;
public RecentItems RecentFiles;
public AviRecordConfig AviRecord;
public MovieRecordConfig MovieRecord;
public Point WindowLocation;
public Size WindowSize;
public bool NeedInputReinit = true;
@ -38,6 +39,7 @@ namespace Mesen.GUI.Config
Emulation = new EmulationConfig();
Preferences = new PreferencesConfig();
AviRecord = new AviRecordConfig();
MovieRecord = new MovieRecordConfig();
}
~Configuration()

View file

@ -77,8 +77,8 @@ namespace Mesen.GUI.Config
CreateMimeType("x-mesen-nes", "smc", "SNES Rom", mimeTypes, cfg.AssociateRomFiles);
CreateMimeType("x-mesen-nes", "swc", "SNES Rom", mimeTypes, cfg.AssociateRomFiles);
CreateMimeType("x-mesen-nes", "fig", "SNES Rom", mimeTypes, cfg.AssociateRomFiles);
CreateMimeType("x-mesen-mst", "mst", "Mesen-S Save State", mimeTypes, cfg.AssociateMssFiles);
CreateMimeType("x-mesen-mmo", "mmo", "Mesen-S Movie File", mimeTypes, cfg.AssociateMsmFiles);
CreateMimeType("x-mesen-mss", "mss", "Mesen-S Save State", mimeTypes, cfg.AssociateMssFiles);
CreateMimeType("x-mesen-msm", "msm", "Mesen-S Movie File", mimeTypes, cfg.AssociateMsmFiles);
//Icon used for shortcuts
//TOOD

View file

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mesen.GUI.Config
{
public class MovieRecordConfig
{
public RecordMovieFrom RecordFrom;
public string Author;
public string Description;
}
}

View file

@ -21,6 +21,7 @@ namespace Mesen.GUI.Config
public bool PauseWhenInMenusAndConfig = false;
public bool PauseWhenInDebuggingTools = false;
public bool AllowBackgroundInput = false;
public bool PauseOnMovieEnd = true;
public bool AssociateRomFiles = false;
public bool AssociateMsmFiles = false;
@ -86,12 +87,6 @@ namespace Mesen.GUI.Config
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.ToggleFullscreen, new KeyCombination() { Key1 = InputApi.GetKeyCode("F11") }));
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.TakeScreenshot, new KeyCombination() { Key1 = InputApi.GetKeyCode("F12") }));
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.ToggleBgLayer0, new KeyCombination() { Key1 = InputApi.GetKeyCode("1") }));
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.ToggleBgLayer1, new KeyCombination() { Key1 = InputApi.GetKeyCode("2") }));
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.ToggleBgLayer2, new KeyCombination() { Key1 = InputApi.GetKeyCode("3") }));
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.ToggleBgLayer3, new KeyCombination() { Key1 = InputApi.GetKeyCode("4") }));
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.ToggleSprites, new KeyCombination() { Key1 = InputApi.GetKeyCode("5") }));
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.Reset, new KeyCombination() { Key1 = InputApi.GetKeyCode("Ctrl"), Key2 = InputApi.GetKeyCode("R") }));
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.PowerCycle, new KeyCombination() { Key1 = InputApi.GetKeyCode("Ctrl"), Key2 = InputApi.GetKeyCode("T") }));
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.Pause, new KeyCombination() { Key1 = InputApi.GetKeyCode("Esc") }));
@ -161,6 +156,7 @@ namespace Mesen.GUI.Config
ShowDebugInfo = ShowDebugInfo,
DisableOsd = DisableOsd,
AllowBackgroundInput = AllowBackgroundInput,
PauseOnMovieEnd = PauseOnMovieEnd,
SaveFolderOverride = OverrideSaveDataFolder ? SaveDataFolder : "",
SaveStateFolderOverride = OverrideSaveStateFolder ? SaveStateFolder : "",
ScreenshotFolderOverride = OverrideScreenshotFolder ? ScreenshotFolder : "",
@ -177,6 +173,7 @@ namespace Mesen.GUI.Config
[MarshalAs(UnmanagedType.I1)] public bool ShowDebugInfo;
[MarshalAs(UnmanagedType.I1)] public bool DisableOsd;
[MarshalAs(UnmanagedType.I1)] public bool AllowBackgroundInput;
[MarshalAs(UnmanagedType.I1)] public bool PauseOnMovieEnd;
public UInt32 RewindBufferSize;

View file

@ -672,7 +672,7 @@
<Messages>
<Message ID="FilterAll">All Files (*.*)|*.*</Message>
<Message ID="FilterZipFiles">Zip files (*.zip)|*.zip</Message>
<Message ID="FilterMovie">Movie files (*.mmo)|*.mmo|All Files (*.*)|*.*</Message>
<Message ID="FilterMovie">Movie files (*.msm)|*.msm|All Files (*.*)|*.*</Message>
<Message ID="FilterWave">Wave files (*.wav)|*.wav|All Files (*.*)|*.*</Message>
<Message ID="FilterAvi">Avi files (*.avi)|*.avi|All Files (*.*)|*.*</Message>
<Message ID="FilterPalette">Palette Files (*.pal)|*.pal|All Files (*.*)|*.*</Message>
@ -704,7 +704,7 @@
<Message ID="HelpFullscreen">/fullscreen - Start Mesen-S in fullscreen mode</Message>
<Message ID="HelpDoNotSaveSettings">/DoNotSaveSettings - Prevent settings from being saved to the disk (useful to prevent command line options from becoming the default settings)</Message>
<Message ID="HelpRecordMovie">/RecordMovie="filename.mmo" - Start recording a movie after the specified game is loaded.</Message>
<Message ID="HelpRecordMovie">/RecordMovie="filename.msm" - Start recording a movie after the specified game is loaded.</Message>
<Message ID="HelpLoadLastSession">/LoadLastSession - Resumes the game in the state it was left in when it was last played.</Message>
<Message ID="Resume">Resume</Message>

View file

@ -103,7 +103,7 @@ namespace Mesen.GUI.Emulation
if(EmuRunner.IsRunning()) {
using(SaveFileDialog sfd = new SaveFileDialog()) {
sfd.InitialDirectory = ConfigManager.SaveStateFolder;
sfd.FileName = EmuApi.GetRomInfo().GetRomName() + ".mst";
sfd.FileName = EmuApi.GetRomInfo().GetRomName() + ".mss";
sfd.SetFilter(ResourceHelper.GetMessage("FilterSavestate"));
if(sfd.ShowDialog(Application.OpenForms[0]) == DialogResult.OK) {
EmuApi.SaveStateFile(sfd.FileName);

View file

@ -30,6 +30,13 @@
this.tabMain = new System.Windows.Forms.TabControl();
this.tpgGeneral = new System.Windows.Forms.TabPage();
this.tlpMain = new System.Windows.Forms.TableLayoutPanel();
this.chkAllowBackgroundInput = new System.Windows.Forms.CheckBox();
this.flowLayoutPanel8 = new System.Windows.Forms.FlowLayoutPanel();
this.lblPauseIn = new System.Windows.Forms.Label();
this.chkPauseWhenInBackground = new System.Windows.Forms.CheckBox();
this.chkPauseInMenuAndConfig = new System.Windows.Forms.CheckBox();
this.chkPauseInDebugger = new System.Windows.Forms.CheckBox();
this.lblPauseBackgroundSettings = new System.Windows.Forms.Label();
this.chkAutoHideMenu = new System.Windows.Forms.CheckBox();
this.chkSingleInstance = new System.Windows.Forms.CheckBox();
this.chkAutomaticallyCheckForUpdates = new System.Windows.Forms.CheckBox();
@ -90,16 +97,11 @@
this.chkAlwaysOnTop = new System.Windows.Forms.CheckBox();
this.chkShowFps = new System.Windows.Forms.CheckBox();
this.chkShowDebugInfo = new System.Windows.Forms.CheckBox();
this.lblPauseBackgroundSettings = new System.Windows.Forms.Label();
this.flowLayoutPanel8 = new System.Windows.Forms.FlowLayoutPanel();
this.lblPauseIn = new System.Windows.Forms.Label();
this.chkPauseWhenInBackground = new System.Windows.Forms.CheckBox();
this.chkPauseInMenuAndConfig = new System.Windows.Forms.CheckBox();
this.chkPauseInDebugger = new System.Windows.Forms.CheckBox();
this.chkAllowBackgroundInput = new System.Windows.Forms.CheckBox();
this.chkPauseOnMovieEnd = new System.Windows.Forms.CheckBox();
this.tabMain.SuspendLayout();
this.tpgGeneral.SuspendLayout();
this.tlpMain.SuspendLayout();
this.flowLayoutPanel8.SuspendLayout();
this.flowLayoutPanel2.SuspendLayout();
this.tableLayoutPanel5.SuspendLayout();
this.tpgShortcuts.SuspendLayout();
@ -116,7 +118,6 @@
this.tpgAdvanced.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.flowLayoutPanel6.SuspendLayout();
this.flowLayoutPanel8.SuspendLayout();
this.SuspendLayout();
//
// baseConfigPanel
@ -153,26 +154,28 @@
//
this.tlpMain.ColumnCount = 1;
this.tlpMain.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tlpMain.Controls.Add(this.chkPauseOnMovieEnd, 0, 6);
this.tlpMain.Controls.Add(this.chkAllowBackgroundInput, 0, 5);
this.tlpMain.Controls.Add(this.flowLayoutPanel8, 0, 4);
this.tlpMain.Controls.Add(this.lblPauseBackgroundSettings, 0, 3);
this.tlpMain.Controls.Add(this.chkAutoHideMenu, 0, 8);
this.tlpMain.Controls.Add(this.chkAutoHideMenu, 0, 9);
this.tlpMain.Controls.Add(this.chkSingleInstance, 0, 2);
this.tlpMain.Controls.Add(this.chkAutomaticallyCheckForUpdates, 0, 1);
this.tlpMain.Controls.Add(this.flowLayoutPanel2, 0, 0);
this.tlpMain.Controls.Add(this.lblMiscSettings, 0, 6);
this.tlpMain.Controls.Add(this.chkAutoLoadPatches, 0, 7);
this.tlpMain.Controls.Add(this.tableLayoutPanel5, 0, 10);
this.tlpMain.Controls.Add(this.lblMiscSettings, 0, 7);
this.tlpMain.Controls.Add(this.chkAutoLoadPatches, 0, 8);
this.tlpMain.Controls.Add(this.tableLayoutPanel5, 0, 11);
this.tlpMain.Dock = System.Windows.Forms.DockStyle.Fill;
this.tlpMain.Location = new System.Drawing.Point(3, 3);
this.tlpMain.Name = "tlpMain";
this.tlpMain.RowCount = 11;
this.tlpMain.RowCount = 12;
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tlpMain.RowStyles.Add(new System.Windows.Forms.RowStyle());
@ -181,10 +184,87 @@
this.tlpMain.Size = new System.Drawing.Size(534, 383);
this.tlpMain.TabIndex = 1;
//
// chkAllowBackgroundInput
//
this.chkAllowBackgroundInput.AutoSize = true;
this.chkAllowBackgroundInput.Location = new System.Drawing.Point(13, 120);
this.chkAllowBackgroundInput.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkAllowBackgroundInput.Name = "chkAllowBackgroundInput";
this.chkAllowBackgroundInput.Size = new System.Drawing.Size(177, 17);
this.chkAllowBackgroundInput.TabIndex = 30;
this.chkAllowBackgroundInput.Text = "Allow input when in background";
this.chkAllowBackgroundInput.UseVisualStyleBackColor = true;
//
// flowLayoutPanel8
//
this.flowLayoutPanel8.Controls.Add(this.lblPauseIn);
this.flowLayoutPanel8.Controls.Add(this.chkPauseWhenInBackground);
this.flowLayoutPanel8.Controls.Add(this.chkPauseInMenuAndConfig);
this.flowLayoutPanel8.Controls.Add(this.chkPauseInDebugger);
this.flowLayoutPanel8.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel8.Location = new System.Drawing.Point(7, 95);
this.flowLayoutPanel8.Margin = new System.Windows.Forms.Padding(7, 3, 0, 0);
this.flowLayoutPanel8.Name = "flowLayoutPanel8";
this.flowLayoutPanel8.Size = new System.Drawing.Size(527, 22);
this.flowLayoutPanel8.TabIndex = 29;
//
// lblPauseIn
//
this.lblPauseIn.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblPauseIn.AutoSize = true;
this.lblPauseIn.Location = new System.Drawing.Point(3, 4);
this.lblPauseIn.Margin = new System.Windows.Forms.Padding(3, 0, 3, 1);
this.lblPauseIn.Name = "lblPauseIn";
this.lblPauseIn.Size = new System.Drawing.Size(80, 13);
this.lblPauseIn.TabIndex = 0;
this.lblPauseIn.Text = "Pause when in:";
//
// chkPauseWhenInBackground
//
this.chkPauseWhenInBackground.AutoSize = true;
this.chkPauseWhenInBackground.Location = new System.Drawing.Point(89, 3);
this.chkPauseWhenInBackground.Name = "chkPauseWhenInBackground";
this.chkPauseWhenInBackground.Size = new System.Drawing.Size(84, 17);
this.chkPauseWhenInBackground.TabIndex = 13;
this.chkPauseWhenInBackground.Text = "Background";
this.chkPauseWhenInBackground.UseVisualStyleBackColor = true;
//
// chkPauseInMenuAndConfig
//
this.chkPauseInMenuAndConfig.AutoSize = true;
this.chkPauseInMenuAndConfig.Location = new System.Drawing.Point(179, 3);
this.chkPauseInMenuAndConfig.Name = "chkPauseInMenuAndConfig";
this.chkPauseInMenuAndConfig.Size = new System.Drawing.Size(142, 17);
this.chkPauseInMenuAndConfig.TabIndex = 16;
this.chkPauseInMenuAndConfig.Text = "Menu and config dialogs";
this.chkPauseInMenuAndConfig.UseVisualStyleBackColor = true;
//
// chkPauseInDebugger
//
this.chkPauseInDebugger.AutoSize = true;
this.chkPauseInDebugger.Location = new System.Drawing.Point(327, 3);
this.chkPauseInDebugger.Name = "chkPauseInDebugger";
this.chkPauseInDebugger.Size = new System.Drawing.Size(103, 17);
this.chkPauseInDebugger.TabIndex = 18;
this.chkPauseInDebugger.Text = "Debugging tools";
this.chkPauseInDebugger.UseVisualStyleBackColor = true;
//
// lblPauseBackgroundSettings
//
this.lblPauseBackgroundSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblPauseBackgroundSettings.AutoSize = true;
this.lblPauseBackgroundSettings.ForeColor = System.Drawing.SystemColors.GrayText;
this.lblPauseBackgroundSettings.Location = new System.Drawing.Point(0, 79);
this.lblPauseBackgroundSettings.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.lblPauseBackgroundSettings.Name = "lblPauseBackgroundSettings";
this.lblPauseBackgroundSettings.Size = new System.Drawing.Size(141, 13);
this.lblPauseBackgroundSettings.TabIndex = 26;
this.lblPauseBackgroundSettings.Text = "Pause/Background Settings";
//
// chkAutoHideMenu
//
this.chkAutoHideMenu.AutoSize = true;
this.chkAutoHideMenu.Location = new System.Drawing.Point(13, 186);
this.chkAutoHideMenu.Location = new System.Drawing.Point(13, 209);
this.chkAutoHideMenu.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkAutoHideMenu.Name = "chkAutoHideMenu";
this.chkAutoHideMenu.Size = new System.Drawing.Size(158, 17);
@ -247,7 +327,7 @@
this.lblMiscSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblMiscSettings.AutoSize = true;
this.lblMiscSettings.ForeColor = System.Drawing.SystemColors.GrayText;
this.lblMiscSettings.Location = new System.Drawing.Point(0, 147);
this.lblMiscSettings.Location = new System.Drawing.Point(0, 170);
this.lblMiscSettings.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.lblMiscSettings.Name = "lblMiscSettings";
this.lblMiscSettings.Size = new System.Drawing.Size(73, 13);
@ -257,7 +337,7 @@
// chkAutoLoadPatches
//
this.chkAutoLoadPatches.AutoSize = true;
this.chkAutoLoadPatches.Location = new System.Drawing.Point(13, 163);
this.chkAutoLoadPatches.Location = new System.Drawing.Point(13, 186);
this.chkAutoLoadPatches.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkAutoLoadPatches.Name = "chkAutoLoadPatches";
this.chkAutoLoadPatches.Size = new System.Drawing.Size(198, 17);
@ -310,7 +390,7 @@
this.tpgShortcuts.Location = new System.Drawing.Point(4, 22);
this.tpgShortcuts.Name = "tpgShortcuts";
this.tpgShortcuts.Padding = new System.Windows.Forms.Padding(3);
this.tpgShortcuts.Size = new System.Drawing.Size(540, 418);
this.tpgShortcuts.Size = new System.Drawing.Size(540, 389);
this.tpgShortcuts.TabIndex = 7;
this.tpgShortcuts.Text = "Shortcut Keys";
this.tpgShortcuts.UseVisualStyleBackColor = true;
@ -320,7 +400,7 @@
this.ctrlEmulatorShortcuts.Dock = System.Windows.Forms.DockStyle.Fill;
this.ctrlEmulatorShortcuts.Location = new System.Drawing.Point(3, 3);
this.ctrlEmulatorShortcuts.Name = "ctrlEmulatorShortcuts";
this.ctrlEmulatorShortcuts.Size = new System.Drawing.Size(534, 412);
this.ctrlEmulatorShortcuts.Size = new System.Drawing.Size(534, 383);
this.ctrlEmulatorShortcuts.TabIndex = 0;
//
// tpgFiles
@ -329,7 +409,7 @@
this.tpgFiles.Location = new System.Drawing.Point(4, 22);
this.tpgFiles.Name = "tpgFiles";
this.tpgFiles.Padding = new System.Windows.Forms.Padding(3);
this.tpgFiles.Size = new System.Drawing.Size(540, 418);
this.tpgFiles.Size = new System.Drawing.Size(540, 389);
this.tpgFiles.TabIndex = 2;
this.tpgFiles.Text = "Folders/Files";
this.tpgFiles.UseVisualStyleBackColor = true;
@ -350,7 +430,7 @@
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel6.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel6.Size = new System.Drawing.Size(534, 412);
this.tableLayoutPanel6.Size = new System.Drawing.Size(534, 383);
this.tableLayoutPanel6.TabIndex = 13;
//
// grpPathOverrides
@ -757,7 +837,7 @@
this.tpgAdvanced.Location = new System.Drawing.Point(4, 22);
this.tpgAdvanced.Name = "tpgAdvanced";
this.tpgAdvanced.Padding = new System.Windows.Forms.Padding(3);
this.tpgAdvanced.Size = new System.Drawing.Size(540, 418);
this.tpgAdvanced.Size = new System.Drawing.Size(540, 389);
this.tpgAdvanced.TabIndex = 1;
this.tpgAdvanced.Text = "Advanced";
this.tpgAdvanced.UseVisualStyleBackColor = true;
@ -793,7 +873,7 @@
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(534, 412);
this.tableLayoutPanel1.Size = new System.Drawing.Size(534, 383);
this.tableLayoutPanel1.TabIndex = 0;
//
// lblAdvancedMisc
@ -973,82 +1053,16 @@
this.chkShowDebugInfo.Text = "Show debug information";
this.chkShowDebugInfo.UseVisualStyleBackColor = true;
//
// lblPauseBackgroundSettings
// chkPauseOnMovieEnd
//
this.lblPauseBackgroundSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblPauseBackgroundSettings.AutoSize = true;
this.lblPauseBackgroundSettings.ForeColor = System.Drawing.SystemColors.GrayText;
this.lblPauseBackgroundSettings.Location = new System.Drawing.Point(0, 79);
this.lblPauseBackgroundSettings.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.lblPauseBackgroundSettings.Name = "lblPauseBackgroundSettings";
this.lblPauseBackgroundSettings.Size = new System.Drawing.Size(141, 13);
this.lblPauseBackgroundSettings.TabIndex = 26;
this.lblPauseBackgroundSettings.Text = "Pause/Background Settings";
//
// flowLayoutPanel8
//
this.flowLayoutPanel8.Controls.Add(this.lblPauseIn);
this.flowLayoutPanel8.Controls.Add(this.chkPauseWhenInBackground);
this.flowLayoutPanel8.Controls.Add(this.chkPauseInMenuAndConfig);
this.flowLayoutPanel8.Controls.Add(this.chkPauseInDebugger);
this.flowLayoutPanel8.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel8.Location = new System.Drawing.Point(7, 95);
this.flowLayoutPanel8.Margin = new System.Windows.Forms.Padding(7, 3, 0, 0);
this.flowLayoutPanel8.Name = "flowLayoutPanel8";
this.flowLayoutPanel8.Size = new System.Drawing.Size(527, 22);
this.flowLayoutPanel8.TabIndex = 29;
//
// lblPauseIn
//
this.lblPauseIn.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblPauseIn.AutoSize = true;
this.lblPauseIn.Location = new System.Drawing.Point(3, 4);
this.lblPauseIn.Margin = new System.Windows.Forms.Padding(3, 0, 3, 1);
this.lblPauseIn.Name = "lblPauseIn";
this.lblPauseIn.Size = new System.Drawing.Size(80, 13);
this.lblPauseIn.TabIndex = 0;
this.lblPauseIn.Text = "Pause when in:";
//
// chkPauseWhenInBackground
//
this.chkPauseWhenInBackground.AutoSize = true;
this.chkPauseWhenInBackground.Location = new System.Drawing.Point(89, 3);
this.chkPauseWhenInBackground.Name = "chkPauseWhenInBackground";
this.chkPauseWhenInBackground.Size = new System.Drawing.Size(84, 17);
this.chkPauseWhenInBackground.TabIndex = 13;
this.chkPauseWhenInBackground.Text = "Background";
this.chkPauseWhenInBackground.UseVisualStyleBackColor = true;
//
// chkPauseInMenuAndConfig
//
this.chkPauseInMenuAndConfig.AutoSize = true;
this.chkPauseInMenuAndConfig.Location = new System.Drawing.Point(179, 3);
this.chkPauseInMenuAndConfig.Name = "chkPauseInMenuAndConfig";
this.chkPauseInMenuAndConfig.Size = new System.Drawing.Size(142, 17);
this.chkPauseInMenuAndConfig.TabIndex = 16;
this.chkPauseInMenuAndConfig.Text = "Menu and config dialogs";
this.chkPauseInMenuAndConfig.UseVisualStyleBackColor = true;
//
// chkPauseInDebugger
//
this.chkPauseInDebugger.AutoSize = true;
this.chkPauseInDebugger.Location = new System.Drawing.Point(327, 3);
this.chkPauseInDebugger.Name = "chkPauseInDebugger";
this.chkPauseInDebugger.Size = new System.Drawing.Size(103, 17);
this.chkPauseInDebugger.TabIndex = 18;
this.chkPauseInDebugger.Text = "Debugging tools";
this.chkPauseInDebugger.UseVisualStyleBackColor = true;
//
// chkAllowBackgroundInput
//
this.chkAllowBackgroundInput.AutoSize = true;
this.chkAllowBackgroundInput.Location = new System.Drawing.Point(13, 120);
this.chkAllowBackgroundInput.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkAllowBackgroundInput.Name = "chkAllowBackgroundInput";
this.chkAllowBackgroundInput.Size = new System.Drawing.Size(177, 17);
this.chkAllowBackgroundInput.TabIndex = 30;
this.chkAllowBackgroundInput.Text = "Allow input when in background";
this.chkAllowBackgroundInput.UseVisualStyleBackColor = true;
this.chkPauseOnMovieEnd.AutoSize = true;
this.chkPauseOnMovieEnd.Location = new System.Drawing.Point(13, 143);
this.chkPauseOnMovieEnd.Margin = new System.Windows.Forms.Padding(13, 3, 3, 3);
this.chkPauseOnMovieEnd.Name = "chkPauseOnMovieEnd";
this.chkPauseOnMovieEnd.Size = new System.Drawing.Size(199, 17);
this.chkPauseOnMovieEnd.TabIndex = 31;
this.chkPauseOnMovieEnd.Text = "Pause when a movie finishes playing";
this.chkPauseOnMovieEnd.UseVisualStyleBackColor = true;
//
// frmPreferences
//
@ -1065,6 +1079,8 @@
this.tpgGeneral.ResumeLayout(false);
this.tlpMain.ResumeLayout(false);
this.tlpMain.PerformLayout();
this.flowLayoutPanel8.ResumeLayout(false);
this.flowLayoutPanel8.PerformLayout();
this.flowLayoutPanel2.ResumeLayout(false);
this.flowLayoutPanel2.PerformLayout();
this.tableLayoutPanel5.ResumeLayout(false);
@ -1090,8 +1106,6 @@
this.tableLayoutPanel1.PerformLayout();
this.flowLayoutPanel6.ResumeLayout(false);
this.flowLayoutPanel6.PerformLayout();
this.flowLayoutPanel8.ResumeLayout(false);
this.flowLayoutPanel8.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
@ -1169,5 +1183,6 @@
private System.Windows.Forms.CheckBox chkPauseInMenuAndConfig;
private System.Windows.Forms.CheckBox chkPauseInDebugger;
private System.Windows.Forms.Label lblPauseBackgroundSettings;
private System.Windows.Forms.CheckBox chkPauseOnMovieEnd;
}
}

View file

@ -37,6 +37,7 @@ namespace Mesen.GUI.Forms.Config
AddBinding(nameof(PreferencesConfig.PauseWhenInMenusAndConfig), chkPauseInMenuAndConfig);
AddBinding(nameof(PreferencesConfig.PauseWhenInDebuggingTools), chkPauseInDebugger);
AddBinding(nameof(PreferencesConfig.AllowBackgroundInput), chkAllowBackgroundInput);
AddBinding(nameof(PreferencesConfig.PauseOnMovieEnd), chkPauseOnMovieEnd);
AddBinding(nameof(PreferencesConfig.AssociateRomFiles), chkRomFormat);
AddBinding(nameof(PreferencesConfig.AssociateMsmFiles), chkMsmFormat);

View file

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Mesen.GUI.Config;
namespace Mesen.GUI.Forms
{
public partial class frmRecordMovie : BaseConfigForm
{
public frmRecordMovie()
{
InitializeComponent();
Entity = ConfigManager.Config.MovieRecord;
AddBinding(nameof(MovieRecordConfig.Author), txtAuthor);
AddBinding(nameof(MovieRecordConfig.Description), txtDescription);
AddBinding(nameof(MovieRecordConfig.RecordFrom), cboRecordFrom);
}
protected override bool ValidateInput()
{
return !string.IsNullOrWhiteSpace(txtFilename.Text);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
if(this.DialogResult == DialogResult.OK) {
RecordMovieOptions options = new RecordMovieOptions(
this.txtFilename.Text,
this.txtAuthor.Text,
this.txtDescription.Text,
this.cboRecordFrom.GetEnumValue<RecordMovieFrom>()
);
RecordApi.MovieRecord(ref options);
}
}
private void btnBrowse_Click(object sender, EventArgs e)
{
SaveFileDialog sfd = new SaveFileDialog();
sfd.SetFilter(ResourceHelper.GetMessage("FilterMovie"));
sfd.InitialDirectory = ConfigManager.MovieFolder;
sfd.FileName = EmuApi.GetRomInfo().GetRomName() + ".msm";
if(sfd.ShowDialog() == DialogResult.OK) {
txtFilename.Text = sfd.FileName;
}
}
}
}

219
UI/Forms/Config/frmRecordMovie.designer.cs generated Normal file
View file

@ -0,0 +1,219 @@
namespace Mesen.GUI.Forms
{
partial class frmRecordMovie
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.lblSaveTo = new System.Windows.Forms.Label();
this.txtFilename = new System.Windows.Forms.TextBox();
this.btnBrowse = new System.Windows.Forms.Button();
this.txtAuthor = new System.Windows.Forms.TextBox();
this.lblRecordFrom = new System.Windows.Forms.Label();
this.lblAuthor = new System.Windows.Forms.Label();
this.lblDescription = new System.Windows.Forms.Label();
this.cboRecordFrom = new System.Windows.Forms.ComboBox();
this.txtDescription = new System.Windows.Forms.TextBox();
this.lblMovieInformation = new System.Windows.Forms.Label();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// baseConfigPanel
//
this.baseConfigPanel.Location = new System.Drawing.Point(0, 202);
this.baseConfigPanel.Size = new System.Drawing.Size(397, 29);
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 3;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel1.Controls.Add(this.lblSaveTo, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.txtFilename, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.btnBrowse, 2, 0);
this.tableLayoutPanel1.Controls.Add(this.txtAuthor, 1, 3);
this.tableLayoutPanel1.Controls.Add(this.lblRecordFrom, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.lblAuthor, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.lblDescription, 0, 4);
this.tableLayoutPanel1.Controls.Add(this.cboRecordFrom, 1, 1);
this.tableLayoutPanel1.Controls.Add(this.txtDescription, 1, 4);
this.tableLayoutPanel1.Controls.Add(this.lblMovieInformation, 0, 2);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 6;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(397, 231);
this.tableLayoutPanel1.TabIndex = 0;
//
// lblSaveTo
//
this.lblSaveTo.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblSaveTo.AutoSize = true;
this.lblSaveTo.Location = new System.Drawing.Point(3, 8);
this.lblSaveTo.Name = "lblSaveTo";
this.lblSaveTo.Size = new System.Drawing.Size(47, 13);
this.lblSaveTo.TabIndex = 0;
this.lblSaveTo.Text = "Save to:";
//
// txtFilename
//
this.txtFilename.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtFilename.Location = new System.Drawing.Point(82, 3);
this.txtFilename.MaxLength = 1999;
this.txtFilename.Name = "txtFilename";
this.txtFilename.ReadOnly = true;
this.txtFilename.Size = new System.Drawing.Size(231, 20);
this.txtFilename.TabIndex = 1;
//
// btnBrowse
//
this.btnBrowse.Location = new System.Drawing.Point(319, 3);
this.btnBrowse.Name = "btnBrowse";
this.btnBrowse.Size = new System.Drawing.Size(75, 23);
this.btnBrowse.TabIndex = 2;
this.btnBrowse.Text = "Browse...";
this.btnBrowse.UseVisualStyleBackColor = true;
this.btnBrowse.Click += new System.EventHandler(this.btnBrowse_Click);
//
// txtAuthor
//
this.tableLayoutPanel1.SetColumnSpan(this.txtAuthor, 2);
this.txtAuthor.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtAuthor.Location = new System.Drawing.Point(82, 84);
this.txtAuthor.MaxLength = 249;
this.txtAuthor.Name = "txtAuthor";
this.txtAuthor.Size = new System.Drawing.Size(312, 20);
this.txtAuthor.TabIndex = 12;
//
// lblRecordFrom
//
this.lblRecordFrom.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblRecordFrom.AutoSize = true;
this.lblRecordFrom.Location = new System.Drawing.Point(3, 36);
this.lblRecordFrom.Name = "lblRecordFrom";
this.lblRecordFrom.Size = new System.Drawing.Size(68, 13);
this.lblRecordFrom.TabIndex = 6;
this.lblRecordFrom.Text = "Record from:";
//
// lblAuthor
//
this.lblAuthor.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblAuthor.AutoSize = true;
this.lblAuthor.Location = new System.Drawing.Point(13, 87);
this.lblAuthor.Margin = new System.Windows.Forms.Padding(13, 0, 3, 0);
this.lblAuthor.Name = "lblAuthor";
this.lblAuthor.Size = new System.Drawing.Size(41, 13);
this.lblAuthor.TabIndex = 5;
this.lblAuthor.Text = "Author:";
//
// lblDescription
//
this.lblDescription.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblDescription.AutoSize = true;
this.lblDescription.Location = new System.Drawing.Point(13, 146);
this.lblDescription.Margin = new System.Windows.Forms.Padding(13, 0, 3, 0);
this.lblDescription.Name = "lblDescription";
this.lblDescription.Size = new System.Drawing.Size(63, 13);
this.lblDescription.TabIndex = 11;
this.lblDescription.Text = "Description:";
//
// cboRecordFrom
//
this.cboRecordFrom.Dock = System.Windows.Forms.DockStyle.Fill;
this.cboRecordFrom.FormattingEnabled = true;
this.cboRecordFrom.Location = new System.Drawing.Point(82, 32);
this.cboRecordFrom.Name = "cboRecordFrom";
this.cboRecordFrom.Size = new System.Drawing.Size(231, 21);
this.cboRecordFrom.TabIndex = 13;
//
// txtDescription
//
this.txtDescription.AcceptsReturn = true;
this.tableLayoutPanel1.SetColumnSpan(this.txtDescription, 2);
this.txtDescription.Dock = System.Windows.Forms.DockStyle.Fill;
this.txtDescription.Location = new System.Drawing.Point(82, 110);
this.txtDescription.MaxLength = 9999;
this.txtDescription.Multiline = true;
this.txtDescription.Name = "txtDescription";
this.txtDescription.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.txtDescription.Size = new System.Drawing.Size(312, 86);
this.txtDescription.TabIndex = 10;
//
// lblMovieInformation
//
this.lblMovieInformation.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.lblMovieInformation.AutoSize = true;
this.tableLayoutPanel1.SetColumnSpan(this.lblMovieInformation, 2);
this.lblMovieInformation.ForeColor = System.Drawing.SystemColors.GrayText;
this.lblMovieInformation.Location = new System.Drawing.Point(3, 65);
this.lblMovieInformation.Name = "lblMovieInformation";
this.lblMovieInformation.Padding = new System.Windows.Forms.Padding(0, 0, 0, 3);
this.lblMovieInformation.Size = new System.Drawing.Size(139, 16);
this.lblMovieInformation.TabIndex = 24;
this.lblMovieInformation.Text = "Movie Information (Optional)";
//
// frmRecordMovie
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(397, 231);
this.Controls.Add(this.tableLayoutPanel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "frmRecordMovie";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Movie Recording Options";
this.Controls.SetChildIndex(this.tableLayoutPanel1, 0);
this.Controls.SetChildIndex(this.baseConfigPanel, 0);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Label lblSaveTo;
private System.Windows.Forms.TextBox txtFilename;
private System.Windows.Forms.Button btnBrowse;
private System.Windows.Forms.Label lblAuthor;
private System.Windows.Forms.Label lblRecordFrom;
private System.Windows.Forms.TextBox txtDescription;
private System.Windows.Forms.Label lblDescription;
private System.Windows.Forms.TextBox txtAuthor;
private System.Windows.Forms.ComboBox cboRecordFrom;
private System.Windows.Forms.Label lblMovieInformation;
}
}

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolTip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -144,6 +144,11 @@
this.mnuAbout = new System.Windows.Forms.ToolStripMenuItem();
this.pnlRenderer = new System.Windows.Forms.Panel();
this.ctrlRecentGames = new Mesen.GUI.Controls.ctrlRecentGames();
this.mnuMovies = new System.Windows.Forms.ToolStripMenuItem();
this.mnuPlayMovie = new System.Windows.Forms.ToolStripMenuItem();
this.mnuRecordMovie = new System.Windows.Forms.ToolStripMenuItem();
this.mnuStopMovie = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem25 = new System.Windows.Forms.ToolStripSeparator();
this.mnuMain.SuspendLayout();
this.pnlRenderer.SuspendLayout();
this.SuspendLayout();
@ -764,6 +769,8 @@
// mnuTools
//
this.mnuTools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuMovies,
this.toolStripMenuItem25,
this.mnuSoundRecorder,
this.mnuVideoRecorder,
this.toolStripMenuItem11,
@ -1039,6 +1046,46 @@
this.ctrlRecentGames.TabIndex = 1;
this.ctrlRecentGames.Visible = false;
//
// mnuMovies
//
this.mnuMovies.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuPlayMovie,
this.mnuRecordMovie,
this.mnuStopMovie});
this.mnuMovies.Image = global::Mesen.GUI.Properties.Resources.Movie;
this.mnuMovies.Name = "mnuMovies";
this.mnuMovies.Size = new System.Drawing.Size(159, 22);
this.mnuMovies.Text = "Movies";
//
// mnuPlayMovie
//
this.mnuPlayMovie.Image = global::Mesen.GUI.Properties.Resources.MediaPlay;
this.mnuPlayMovie.Name = "mnuPlayMovie";
this.mnuPlayMovie.Size = new System.Drawing.Size(152, 22);
this.mnuPlayMovie.Text = "Play...";
this.mnuPlayMovie.Click += new System.EventHandler(this.mnuPlayMovie_Click);
//
// mnuRecordMovie
//
this.mnuRecordMovie.Image = global::Mesen.GUI.Properties.Resources.Record;
this.mnuRecordMovie.Name = "mnuRecordMovie";
this.mnuRecordMovie.Size = new System.Drawing.Size(152, 22);
this.mnuRecordMovie.Text = "Record...";
this.mnuRecordMovie.Click += new System.EventHandler(this.mnuRecordMovie_Click);
//
// mnuStopMovie
//
this.mnuStopMovie.Image = global::Mesen.GUI.Properties.Resources.MediaStop;
this.mnuStopMovie.Name = "mnuStopMovie";
this.mnuStopMovie.Size = new System.Drawing.Size(152, 22);
this.mnuStopMovie.Text = "Stop";
this.mnuStopMovie.Click += new System.EventHandler(this.mnuStopMovie_Click);
//
// toolStripMenuItem25
//
this.toolStripMenuItem25.Name = "toolStripMenuItem25";
this.toolStripMenuItem25.Size = new System.Drawing.Size(156, 6);
//
// frmMain
//
this.AllowDrop = true;
@ -1181,5 +1228,10 @@
private System.Windows.Forms.ToolStripMenuItem mnuScriptWindow;
private System.Windows.Forms.ToolStripMenuItem mnuSa1Debugger;
private System.Windows.Forms.ToolStripMenuItem mnuGsuDebugger;
private System.Windows.Forms.ToolStripMenuItem mnuMovies;
private System.Windows.Forms.ToolStripMenuItem mnuPlayMovie;
private System.Windows.Forms.ToolStripMenuItem mnuRecordMovie;
private System.Windows.Forms.ToolStripMenuItem mnuStopMovie;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem25;
}
}

View file

@ -517,6 +517,22 @@ namespace Mesen.GUI.Forms
mnuRegionPal.Checked = ConfigManager.Config.Emulation.Region == ConsoleRegion.Pal;
}
private void mnuTools_DropDownOpening(object sender, EventArgs e)
{
mnuMovies.Enabled = EmuRunner.IsRunning();
mnuPlayMovie.Enabled = EmuRunner.IsRunning() && !RecordApi.MoviePlaying() && !RecordApi.MovieRecording();
mnuRecordMovie.Enabled = EmuRunner.IsRunning() && !RecordApi.MoviePlaying() && !RecordApi.MovieRecording();
mnuStopMovie.Enabled = EmuRunner.IsRunning() && (RecordApi.MoviePlaying() || RecordApi.MovieRecording());
mnuSoundRecorder.Enabled = EmuRunner.IsRunning();
mnuWaveRecord.Enabled = EmuRunner.IsRunning() && !RecordApi.WaveIsRecording();
mnuWaveStop.Enabled = EmuRunner.IsRunning() && RecordApi.WaveIsRecording();
mnuVideoRecorder.Enabled = EmuRunner.IsRunning();
mnuAviRecord.Enabled = EmuRunner.IsRunning() && !RecordApi.AviIsRecording();
mnuAviStop.Enabled = EmuRunner.IsRunning() && RecordApi.AviIsRecording();
}
private void mnuAviRecord_Click(object sender, EventArgs e)
{
using(frmRecordAvi frm = new frmRecordAvi()) {
@ -531,17 +547,6 @@ namespace Mesen.GUI.Forms
RecordApi.AviStop();
}
private void mnuTools_DropDownOpening(object sender, EventArgs e)
{
mnuSoundRecorder.Enabled = EmuRunner.IsRunning();
mnuWaveRecord.Enabled = EmuRunner.IsRunning() && !RecordApi.WaveIsRecording();
mnuWaveStop.Enabled = EmuRunner.IsRunning() && RecordApi.WaveIsRecording();
mnuVideoRecorder.Enabled = EmuRunner.IsRunning();
mnuAviRecord.Enabled = EmuRunner.IsRunning() && !RecordApi.AviIsRecording();
mnuAviStop.Enabled = EmuRunner.IsRunning() && RecordApi.AviIsRecording();
}
private void mnuWaveRecord_Click(object sender, EventArgs e)
{
using(SaveFileDialog sfd = new SaveFileDialog()) {
@ -559,6 +564,29 @@ namespace Mesen.GUI.Forms
RecordApi.WaveStop();
}
private void mnuPlayMovie_Click(object sender, EventArgs e)
{
using(OpenFileDialog ofd = new OpenFileDialog()) {
ofd.SetFilter(ResourceHelper.GetMessage("FilterMovie"));
ofd.InitialDirectory = ConfigManager.MovieFolder;
if(ofd.ShowDialog(this) == DialogResult.OK) {
RecordApi.MoviePlay(ofd.FileName);
}
}
}
private void mnuStopMovie_Click(object sender, EventArgs e)
{
RecordApi.MovieStop();
}
private void mnuRecordMovie_Click(object sender, EventArgs e)
{
using(frmRecordMovie frm = new frmRecordMovie()) {
frm.ShowDialog(mnuMovies, this);
}
}
private void mnu_DropDownOpened(object sender, EventArgs e)
{
Interlocked.Increment(ref _inMenu);

View file

@ -20,10 +20,52 @@ namespace Mesen.GUI
[DllImport(DllPath)] public static extern void WaveStop();
[DllImport(DllPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool WaveIsRecording();
/*[DllImport(DllPath)] public static extern void MoviePlay([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filename);
[DllImport(DllPath)] public static extern void MoviePlay([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filename);
[DllImport(DllPath)] public static extern void MovieRecord(ref RecordMovieOptions options);
[DllImport(DllPath)] public static extern void MovieStop();
[DllImport(DllPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool MoviePlaying();
[DllImport(DllPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool MovieRecording();*/
[DllImport(DllPath)] [return: MarshalAs(UnmanagedType.I1)] public static extern bool MovieRecording();
}
public enum RecordMovieFrom
{
StartWithoutSaveData,
StartWithSaveData,
CurrentState
}
public struct RecordMovieOptions
{
private const int AuthorMaxSize = 250;
private const int DescriptionMaxSize = 10000;
private const int FilenameMaxSize = 2000;
public RecordMovieOptions(string filename, string author, string description, RecordMovieFrom recordFrom)
{
Author = Encoding.UTF8.GetBytes(author);
Array.Resize(ref Author, AuthorMaxSize);
Author[AuthorMaxSize - 1] = 0;
Description = Encoding.UTF8.GetBytes(description.Replace("\r", ""));
Array.Resize(ref Description, DescriptionMaxSize);
Description[DescriptionMaxSize - 1] = 0;
Filename = Encoding.UTF8.GetBytes(filename);
Array.Resize(ref Filename, FilenameMaxSize);
Filename[FilenameMaxSize - 1] = 0;
RecordFrom = recordFrom;
}
[MarshalAs(UnmanagedType.ByValArray, SizeConst = FilenameMaxSize)]
public byte[] Filename;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = AuthorMaxSize)]
public byte[] Author;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = DescriptionMaxSize)]
public byte[] Description;
public RecordMovieFrom RecordFrom;
}
}

View file

@ -217,6 +217,7 @@
<Compile Include="Config\ConfigManager.cs" />
<Compile Include="Config\InputConfig.cs" />
<Compile Include="Config\FileAssociationHelper.cs" />
<Compile Include="Config\MovieRecordConfig.cs" />
<Compile Include="Config\PreferencesConfig.cs" />
<Compile Include="Config\Shortcuts\EmulatorShortcut.cs" />
<Compile Include="Config\Shortcuts\KeyCombination.cs" />
@ -767,6 +768,12 @@
<Compile Include="Forms\frmFullscreenRenderer.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Forms\Config\frmRecordMovie.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Forms\Config\frmRecordMovie.Designer.cs">
<DependentUpon>frmRecordMovie.cs</DependentUpon>
</Compile>
<Compile Include="Forms\Tools\frmLogWindow.cs">
<SubType>Form</SubType>
</Compile>
@ -1048,6 +1055,9 @@
<EmbeddedResource Include="Forms\frmConfigWizard.resx">
<DependentUpon>frmConfigWizard.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\Config\frmRecordMovie.resx">
<DependentUpon>frmRecordMovie.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\Tools\frmLogWindow.resx">
<DependentUpon>frmLogWindow.cs</DependentUpon>
</EmbeddedResource>

View file

@ -52,8 +52,8 @@ namespace Mesen.GUI.Utilities
} else if(!Path.IsPathRooted(moviePath)) {
moviePath = Path.Combine(Program.OriginalFolder, moviePath);
}
if(!moviePath.ToLower().EndsWith(".mmo")) {
moviePath += ".mmo";
if(!moviePath.ToLower().EndsWith(".msm")) {
moviePath += ".msm";
}
_movieToRecord = moviePath;
break;