Save state support

This commit is contained in:
Sour 2019-03-12 09:15:57 -04:00
parent fe958a8285
commit 73913e1f0c
55 changed files with 2571 additions and 987 deletions

View file

@ -8,6 +8,8 @@
#include "../Utilities/HexUtilities.h"
#include "../Utilities/VirtualFile.h"
#include "../Utilities/FolderUtilities.h"
#include "../Utilities/Serializer.h"
#include "../Utilities/sha1.h"
BaseCartridge::~BaseCartridge()
{
@ -158,6 +160,11 @@ RomInfo BaseCartridge::GetRomInfo()
return info;
}
string BaseCartridge::GetSha1Hash()
{
return SHA1::GetHash(_prgRom, _prgRomSize);
}
void BaseCartridge::LoadBattery()
{
if(_saveRamSize > 0) {
@ -240,6 +247,11 @@ void BaseCartridge::RegisterHandlers(MemoryManager &mm)
}
}
void BaseCartridge::Serialize(Serializer &s)
{
s.StreamArray(_saveRam, _saveRamSize);
}
void BaseCartridge::DisplayCartInfo()
{
int nameLength = 21;

View file

@ -1,39 +1,12 @@
#pragma once
#include "stdafx.h"
#include "IMemoryHandler.h"
#include "CartTypes.h"
#include "../Utilities/ISerializable.h"
class MemoryManager;
class VirtualFile;
struct SnesCartInformation
{
uint8_t MakerCode[2];
uint8_t GameCode[4];
uint8_t Reserved[7];
uint8_t ExpansionRamSize;
uint8_t SpecialVersion;
uint8_t CartridgeType;
char CartName[21];
uint8_t MapMode;
uint8_t RomType;
uint8_t RomSize;
uint8_t SramSize;
uint8_t DestinationCode;
uint8_t Reserved2;
uint8_t Version;
uint8_t ChecksumComplement[2];
uint8_t Checksum[2];
};
struct RomInfo
{
SnesCartInformation Header;
string RomPath;
};
namespace CartFlags
{
enum CartFlags
@ -47,7 +20,7 @@ namespace CartFlags
};
}
class BaseCartridge
class BaseCartridge : public ISerializable
{
private:
vector<unique_ptr<IMemoryHandler>> _prgRomHandlers;
@ -79,6 +52,7 @@ public:
void Init();
RomInfo GetRomInfo();
string GetSha1Hash();
void RegisterHandlers(MemoryManager &mm);
@ -86,4 +60,6 @@ public:
uint8_t* DebugGetSaveRam() { return _saveRam; }
uint32_t DebugGetPrgRomSize() { return _prgRomSize; }
uint32_t DebugGetSaveRamSize() { return _saveRamSize; }
void Serialize(Serializer &s) override;
};

View file

@ -1,9 +1,9 @@
#include "stdafx.h"
#include "BaseControlDevice.h"
#include "Console.h"
#include "KeyManager.h"
#include "../Utilities/StringUtilities.h"
#include "Console.h"
//#include "EmulationSettings.h"
#include "../Utilities/Serializer.h"
BaseControlDevice::BaseControlDevice(shared_ptr<Console> console, uint8_t port, KeyMappingSet keyMappingSet)
{
@ -32,16 +32,6 @@ void BaseControlDevice::InternalSetStateFromInput()
{
}
//TODO
/*
void BaseControlDevice::StreamState(bool saving)
{
auto lock = _stateLock.AcquireSafe();
VectorInfo<uint8_t> state{ &_state.State };
Stream(_strobe, state);
}
*/
bool BaseControlDevice::IsCurrentPort(uint16_t addr)
{
return _port == (addr - 0x4016);
@ -277,3 +267,10 @@ void BaseControlDevice::SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_
state1->SetBit(button1);
}
}
void BaseControlDevice::Serialize(Serializer &s)
{
auto lock = _stateLock.AcquireSafe();
s.Stream(_strobe);
s.StreamVector(_state.State);
}

View file

@ -4,10 +4,11 @@
#include "SettingTypes.h"
#include "IKeyManager.h"
#include "../Utilities/SimpleLock.h"
#include "../Utilities/ISerializable.h"
class Console;
class BaseControlDevice
class BaseControlDevice : public ISerializable
{
private:
ControlDeviceState _state;
@ -80,4 +81,6 @@ public:
virtual void WriteRam(uint16_t addr, uint8_t value) = 0;
void static SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_t button1, shared_ptr<BaseControlDevice> state2, uint8_t button2);
void Serialize(Serializer &s) override;
};

31
Core/CartTypes.h Normal file
View file

@ -0,0 +1,31 @@
#pragma once
#include "stdafx.h"
struct SnesCartInformation
{
uint8_t MakerCode[2];
uint8_t GameCode[4];
uint8_t Reserved[7];
uint8_t ExpansionRamSize;
uint8_t SpecialVersion;
uint8_t CartridgeType;
char CartName[21];
uint8_t MapMode;
uint8_t RomType;
uint8_t RomSize;
uint8_t SramSize;
uint8_t DestinationCode;
uint8_t Reserved2;
uint8_t Version;
uint8_t ChecksumComplement[2];
uint8_t Checksum[2];
};
struct RomInfo
{
SnesCartInformation Header;
string RomPath;
};

View file

@ -21,7 +21,11 @@
#include "KeyManager.h"
#include "EventType.h"
#include "EmuSettings.h"
#include "SaveStateManager.h"
#include "DebugStats.h"
#include "CartTypes.h"
#include "ConsoleLock.h"
#include "../Utilities/Serializer.h"
#include "../Utilities/Timer.h"
#include "../Utilities/VirtualFile.h"
#include "../Utilities/PlatformUtilities.h"
@ -33,10 +37,13 @@ Console::~Console()
void Console::Initialize()
{
_lockCounter = 0;
_settings.reset(new EmuSettings());
_notificationManager.reset(new NotificationManager());
_videoDecoder.reset(new VideoDecoder(shared_from_this()));
_videoRenderer.reset(new VideoRenderer(shared_from_this()));
_saveStateManager.reset(new SaveStateManager(shared_from_this()));
_soundMixer.reset(new SoundMixer(this));
_debugHud.reset(new DebugHud());
@ -55,6 +62,9 @@ void Console::Release()
_videoRenderer.reset();
_debugHud.reset();
_notificationManager.reset();
_saveStateManager.reset();
_soundMixer.reset();
_settings.reset();
}
void Console::Run()
@ -81,6 +91,8 @@ void Console::Run()
_cpu->Exec();
if(previousFrameCount != _ppu->GetFrameCount()) {
WaitForLock();
frameLimiter.ProcessFrame();
frameLimiter.WaitForNextFrame();
@ -169,6 +181,18 @@ void Console::LoadRom(VirtualFile romFile, VirtualFile patchFile)
//_debugger.reset();
//GetDebugger();
//}
_notificationManager->SendNotification(ConsoleNotificationType::GameLoaded);
}
}
RomInfo Console::GetRomInfo()
{
shared_ptr<BaseCartridge> cart = _cart;
if(cart) {
return cart->GetRomInfo();
} else {
return {};
}
}
@ -184,6 +208,63 @@ double Console::GetFrameDelay()
return frameDelay;
}
ConsoleLock Console::AcquireLock()
{
return ConsoleLock(this);
}
void Console::Lock()
{
_lockCounter++;
_runLock.Acquire();
}
void Console::Unlock()
{
_runLock.Release();
_lockCounter--;
}
void Console::WaitForLock()
{
if(_lockCounter > 0) {
//Need to temporarely pause the emu (to save/load a state, etc.)
_runLock.Release();
//Spin wait until we are allowed to start again
while(_lockCounter > 0) {}
_runLock.Acquire();
}
}
void Console::Serialize(ostream &out)
{
Serializer serializer(SaveStateManager::FileFormatVersion);
serializer.Stream(_cpu.get());
serializer.Stream(_memoryManager.get());
serializer.Stream(_ppu.get());
serializer.Stream(_dmaController.get());
serializer.Stream(_internalRegisters.get());
serializer.Stream(_cart.get());
serializer.Stream(_controlManager.get());
serializer.Stream(_spc.get());
serializer.Save(out);
}
void Console::Deserialize(istream &in, uint32_t fileFormatVersion)
{
Serializer serializer(in, fileFormatVersion);
serializer.Stream(_cpu.get());
serializer.Stream(_memoryManager.get());
serializer.Stream(_ppu.get());
serializer.Stream(_dmaController.get());
serializer.Stream(_internalRegisters.get());
serializer.Stream(_cart.get());
serializer.Stream(_controlManager.get());
serializer.Stream(_spc.get());
}
shared_ptr<SoundMixer> Console::GetSoundMixer()
{
return _soundMixer;
@ -209,6 +290,11 @@ shared_ptr<EmuSettings> Console::GetSettings()
return _settings;
}
shared_ptr<SaveStateManager> Console::GetSaveStateManager()
{
return _saveStateManager;
}
shared_ptr<DebugHud> Console::GetDebugHud()
{
return _debugHud;

View file

@ -1,5 +1,7 @@
#pragma once
#include "stdafx.h"
#include "CartTypes.h"
#include "ConsoleLock.h"
#include "../Utilities/VirtualFile.h"
#include "../Utilities/SimpleLock.h"
@ -18,6 +20,7 @@ class VideoRenderer;
class VideoDecoder;
class NotificationManager;
class EmuSettings;
class SaveStateManager;
enum class MemoryOperationType;
enum class SnesMemoryType;
enum class EventType;
@ -42,14 +45,18 @@ private:
shared_ptr<VideoDecoder> _videoDecoder;
shared_ptr<DebugHud> _debugHud;
shared_ptr<EmuSettings> _settings;
shared_ptr<SaveStateManager> _saveStateManager;
thread::id _emulationThreadId;
atomic<uint32_t> _lockCounter;
SimpleLock _runLock;
SimpleLock _debuggerLock;
atomic<bool> _stopFlag;
double GetFrameDelay();
void WaitForLock();
public:
~Console();
@ -61,12 +68,21 @@ public:
void Stop();
void LoadRom(VirtualFile romFile, VirtualFile patchFile);
RomInfo GetRomInfo();
ConsoleLock AcquireLock();
void Lock();
void Unlock();
void Serialize(ostream &out);
void Deserialize(istream &in, uint32_t fileFormatVersion);
shared_ptr<SoundMixer> GetSoundMixer();
shared_ptr<VideoRenderer> GetVideoRenderer();
shared_ptr<VideoDecoder> GetVideoDecoder();
shared_ptr<NotificationManager> GetNotificationManager();
shared_ptr<EmuSettings> GetSettings();
shared_ptr<SaveStateManager> GetSaveStateManager();
shared_ptr<DebugHud> GetDebugHud();

14
Core/ConsoleLock.cpp Normal file
View file

@ -0,0 +1,14 @@
#include "stdafx.h"
#include "ConsoleLock.h"
#include "Console.h"
ConsoleLock::ConsoleLock(Console *console)
{
_console = console;
_console->Lock();
}
ConsoleLock::~ConsoleLock()
{
_console->Unlock();
}

14
Core/ConsoleLock.h Normal file
View file

@ -0,0 +1,14 @@
#pragma once
#include "stdafx.h"
class Console;
class ConsoleLock
{
private:
Console *_console = nullptr;
public:
ConsoleLock(Console *console);
~ConsoleLock();
};

View file

@ -7,6 +7,7 @@
#include "IInputProvider.h"
#include "IInputRecorder.h"
#include "SnesController.h"
#include "../Utilities/Serializer.h"
ControlManager::ControlManager(shared_ptr<Console> console)
{
@ -216,4 +217,11 @@ void ControlManager::Write(uint16_t addr, uint8_t value)
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
device->WriteRam(addr, value);
}
}
}
void ControlManager::Serialize(Serializer &s)
{
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
s.Stream(device.get());
}
}

View file

@ -2,6 +2,7 @@
#include "stdafx.h"
#include "../Utilities/SimpleLock.h"
#include "../Utilities/ISerializable.h"
#include "IMemoryHandler.h"
class BaseControlDevice;
@ -12,13 +13,13 @@ struct ControlDeviceState;
enum class ControllerType;
enum class ExpansionPortDevice;
class ControlManager
class ControlManager : public ISerializable
{
private:
vector<IInputRecorder*> _inputRecorders;
vector<IInputProvider*> _inputProviders;
//Static so that power cycle does not reset its value
//TODO: Static so that power cycle does not reset its value
uint32_t _pollCounter;
protected:
@ -57,4 +58,6 @@ public:
uint8_t Read(uint16_t addr);
void Write(uint16_t addr, uint8_t value);
void Serialize(Serializer &s) override;
};

View file

@ -54,8 +54,10 @@
<ClInclude Include="blargg_source.h" />
<ClInclude Include="Breakpoint.h" />
<ClInclude Include="BreakpointManager.h" />
<ClInclude Include="CartTypes.h" />
<ClInclude Include="CodeDataLogger.h" />
<ClInclude Include="Console.h" />
<ClInclude Include="ConsoleLock.h" />
<ClInclude Include="ControlDeviceState.h" />
<ClInclude Include="ControlManager.h" />
<ClInclude Include="Cpu.h" />
@ -104,6 +106,7 @@
<ClInclude Include="RamHandler.h" />
<ClInclude Include="RegisterHandlerA.h" />
<ClInclude Include="RomHandler.h" />
<ClInclude Include="SaveStateManager.h" />
<ClInclude Include="ScaleFilter.h" />
<ClInclude Include="SettingTypes.h" />
<ClInclude Include="ShortcutKeyHandler.h" />
@ -130,6 +133,7 @@
<ClCompile Include="BreakpointManager.cpp" />
<ClCompile Include="CodeDataLogger.cpp" />
<ClCompile Include="Console.cpp" />
<ClCompile Include="ConsoleLock.cpp" />
<ClCompile Include="ControlManager.cpp" />
<ClCompile Include="Cpu.cpp" />
<ClCompile Include="Cpu.Instructions.cpp" />
@ -152,6 +156,8 @@
<ClCompile Include="NtscFilter.cpp" />
<ClCompile Include="Ppu.cpp" />
<ClCompile Include="PpuTools.cpp" />
<ClCompile Include="RegisterHandlerB.cpp" />
<ClCompile Include="SaveStateManager.cpp" />
<ClCompile Include="ScaleFilter.cpp" />
<ClCompile Include="ShortcutKeyHandler.cpp" />
<ClCompile Include="SNES_SPC.cpp" />

View file

@ -227,6 +227,15 @@
<ClInclude Include="ShortcutKeyHandler.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="SaveStateManager.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="CartTypes.h">
<Filter>SNES</Filter>
</ClInclude>
<ClInclude Include="ConsoleLock.h">
<Filter>Misc</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -362,6 +371,13 @@
<ClCompile Include="DebugStats.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="RegisterHandlerB.cpp">
<Filter>SNES</Filter>
</ClCompile>
<ClCompile Include="SaveStateManager.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="ConsoleLock.cpp" />
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">

View file

@ -4,6 +4,7 @@
#include "Console.h"
#include "MemoryManager.h"
#include "EventType.h"
#include "../Utilities/Serializer.h"
Cpu::Cpu(Console *console)
{
@ -612,3 +613,12 @@ bool Cpu::CheckFlag(uint8_t flag)
{
return (_state.PS & flag) == flag;
}
void Cpu::Serialize(Serializer &s)
{
s.Stream(
_state.A, _state.CycleCount, _state.D, _state.DBR, _state.EmulationMode, _state.IrqSource, _state.K,
_state.NmiFlag, _state.PC, _state.PrevIrqSource, _state.PrevNmiFlag, _state.PS, _state.SP, _state.StopState,
_state.X, _state.Y
);
}

View file

@ -7,11 +7,12 @@
#include "stdafx.h"
#include "CpuTypes.h"
#include "../Utilities/ISerializable.h"
class MemoryManager;
class Console;
class Cpu
class Cpu : public ISerializable
{
private:
static constexpr uint32_t NmiVector = 0x00FFEA;
@ -300,6 +301,9 @@ public:
bool CheckIrqSource(IrqSource source);
void ClearIrqSource(IrqSource source);
// Inherited via ISerializable
void Serialize(Serializer &s) override;
#ifdef DUMMYCPU
private:
uint32_t _writeCounter = 0;

View file

@ -2,6 +2,7 @@
#include "DmaController.h"
#include "MemoryManager.h"
#include "MessageManager.h"
#include "../Utilities/Serializer.h"
DmaController::DmaController(MemoryManager *memoryManager)
{
@ -428,5 +429,19 @@ uint8_t DmaController::Read(uint16_t addr)
return channel.HdmaLineCounterAndRepeat;
}
}
return 0; //TODO : open bus
}
return _memoryManager->GetOpenBus();
}
void DmaController::Serialize(Serializer &s)
{
s.Stream(_hdmaPending, _hdmaChannels);
for(int i = 0; i < 8; i++) {
s.Stream(
_channel[i].Decrement, _channel[i].DestAddress, _channel[i].DoTransfer, _channel[i].FixedTransfer,
_channel[i].HdmaBank, _channel[i].HdmaFinished, _channel[i].HdmaIndirectAddressing,
_channel[i].HdmaLineCounterAndRepeat, _channel[i].HdmaTableAddress, _channel[i].InterruptedByHdma,
_channel[i].InvertDirection, _channel[i].SrcAddress, _channel[i].SrcBank, _channel[i].TransferMode,
_channel[i].TransferSize, _channel[i].UnusedFlag
);
}
}

View file

@ -1,6 +1,7 @@
#pragma once
#include "stdafx.h"
#include "CpuTypes.h"
#include "../Utilities/ISerializable.h"
class MemoryManager;
@ -28,7 +29,7 @@ struct DmaChannelConfig
bool UnusedFlag;
};
class DmaController
class DmaController : public ISerializable
{
private:
static constexpr uint8_t _transferByteCount[8] = { 1, 2, 2, 4, 4, 4, 2, 4 };
@ -57,4 +58,6 @@ public:
void Write(uint16_t addr, uint8_t value);
uint8_t Read(uint16_t addr);
void Serialize(Serializer &s) override;
};

View file

@ -4,6 +4,12 @@
#include "MessageManager.h"
#include "../Utilities/FolderUtilities.h"
uint32_t EmuSettings::GetVersion()
{
//0.1.0
return 0x00000100;
}
void EmuSettings::ProcessString(string & str, const char ** strPointer)
{
//Make a copy of the string and keep it (the original pointer will not be valid after the call is over)

View file

@ -24,6 +24,8 @@ private:
void SetShortcutKey(EmulatorShortcut shortcut, KeyCombination keyCombination, int keySetIndex);
public:
uint32_t GetVersion();
void SetVideoConfig(VideoConfig config);
VideoConfig GetVideoConfig();

View file

@ -5,6 +5,7 @@
#include "Ppu.h"
#include "ControlManager.h"
#include "MessageManager.h"
#include "../Utilities/Serializer.h"
#include "../Utilities/HexUtilities.h"
InternalRegisters::InternalRegisters(shared_ptr<Console> console)
@ -101,7 +102,7 @@ uint8_t InternalRegisters::Read(uint16_t addr)
return (uint8_t)(_controllerData[((addr & 0x0E) - 8) >> 1] >> 8);
default:
MessageManager::DisplayMessage("Debug", "Unimplemented register read: " + HexUtilities::ToHex(addr));
MessageManager::Log("[Debug] Unimplemented register read: " + HexUtilities::ToHex(addr));
return 0;
}
}
@ -159,7 +160,16 @@ void InternalRegisters::Write(uint16_t addr, uint8_t value)
case 0x420D: _enableFastRom = (value & 0x01) != 0; break;
default:
MessageManager::DisplayMessage("Debug", "Unimplemented register write: " + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value));
MessageManager::Log("[Debug] Unimplemented register write: " + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value));
break;
}
}
void InternalRegisters::Serialize(Serializer &s)
{
s.Stream(
_multOperand1, _multOperand2, _multOrRemainderResult, _dividend, _divisor, _divResult, _enableAutoJoypadRead,
_enableFastRom, _nmiFlag, _enableNmi, _enableHorizontalIrq, _enableVerticalIrq, _horizontalTimer,
_verticalTimer, _ioPortOutput, _controllerData[0], _controllerData[1], _controllerData[2], _controllerData[3]
);
}

View file

@ -1,9 +1,10 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/ISerializable.h"
class Console;
class InternalRegisters
class InternalRegisters : public ISerializable
{
private:
shared_ptr<Console> _console;
@ -46,4 +47,6 @@ public:
uint8_t Read(uint16_t addr);
void Write(uint16_t addr, uint8_t value);
void Serialize(Serializer &s) override;
};

View file

@ -10,6 +10,7 @@
#include "RamHandler.h"
#include "MessageManager.h"
#include "DebugTypes.h"
#include "../Utilities/Serializer.h"
#include "../Utilities/HexUtilities.h"
void MemoryManager::Initialize(shared_ptr<Console> console)
@ -165,7 +166,7 @@ uint8_t MemoryManager::Read(uint32_t addr, MemoryOperationType type)
} else {
//open bus
value = _openBus;
MessageManager::DisplayMessage("Debug", "Read - missing handler: $" + HexUtilities::ToHex(addr));
MessageManager::Log("[Debug] Read - missing handler: $" + HexUtilities::ToHex(addr));
}
_console->ProcessCpuRead(addr, value, type);
return value;
@ -181,7 +182,7 @@ uint8_t MemoryManager::ReadDma(uint32_t addr)
} else {
//open bus
value = _openBus;
MessageManager::DisplayMessage("Debug", "Read - missing handler: $" + HexUtilities::ToHex(addr));
MessageManager::Log("[Debug] Read - missing handler: $" + HexUtilities::ToHex(addr));
}
_console->ProcessCpuRead(addr, value, MemoryOperationType::DmaRead);
return value;
@ -212,7 +213,7 @@ void MemoryManager::Write(uint32_t addr, uint8_t value, MemoryOperationType type
if(_handlers[addr >> 12]) {
return _handlers[addr >> 12]->Write(addr, value);
} else {
MessageManager::DisplayMessage("Debug", "Write - missing handler: $" + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value));
MessageManager::Log("[Debug] Write - missing handler: $" + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value));
}
}
@ -224,7 +225,7 @@ void MemoryManager::WriteDma(uint32_t addr, uint8_t value)
if(_handlers[addr >> 12]) {
return _handlers[addr >> 12]->Write(addr, value);
} else {
MessageManager::DisplayMessage("Debug", "Write - missing handler: $" + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value));
MessageManager::Log("[Debug] Write - missing handler: $" + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value));
}
}
@ -291,3 +292,9 @@ int MemoryManager::GetRelativeAddress(AddressInfo &address, int32_t cpuAddress)
}
return -1;
}
void MemoryManager::Serialize(Serializer &s)
{
s.Stream(_masterClock, _openBus);
s.StreamArray(_workRam, MemoryManager::WorkRamSize);
}

View file

@ -1,7 +1,7 @@
#pragma once
#include "stdafx.h"
#include "DebugTypes.h"
#include "Ppu.h"
#include "../Utilities/ISerializable.h"
class IMemoryHandler;
class RegisterHandlerA;
@ -12,7 +12,7 @@ class Console;
class Ppu;
enum class MemoryOperationType;
class MemoryManager
class MemoryManager : public ISerializable
{
public:
constexpr static uint32_t WorkRamSize = 0x20000;
@ -65,6 +65,8 @@ public:
bool IsWorkRam(uint32_t cpuAddress);
AddressInfo GetAbsoluteAddress(uint32_t addr);
int GetRelativeAddress(AddressInfo &address, int32_t cpuAddress = -1);
void Serialize(Serializer &s) override;
};
template<uint16_t value>

View file

@ -12,6 +12,7 @@
#include "MessageManager.h"
#include "EventType.h"
#include "../Utilities/HexUtilities.h"
#include "../Utilities/Serializer.h"
Ppu::Ppu(shared_ptr<Console> console)
{
@ -1270,7 +1271,7 @@ uint8_t Ppu::Read(uint16_t addr)
}
default:
MessageManager::DisplayMessage("Debug", "Unimplemented register read: " + HexUtilities::ToHex(addr));
MessageManager::Log("[Debug] Unimplemented register read: " + HexUtilities::ToHex(addr));
break;
}
@ -1600,11 +1601,50 @@ void Ppu::Write(uint32_t addr, uint8_t value)
break;
default:
MessageManager::DisplayMessage("Debug", "Unimplemented register write: " + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value));
MessageManager::Log("[Debug] Unimplemented register write: " + HexUtilities::ToHex(addr) + " = " + HexUtilities::ToHex(value));
break;
}
}
void Ppu::Serialize(Serializer &s)
{
s.Stream(
_forcedVblank, _screenBrightness, _cycle, _scanline, _frameCount, _drawStartX, _drawEndX, _irqDelay, _bgMode,
_mode1Bg3Priority, _mainScreenLayers, _subScreenLayers, _vramAddress, _vramIncrementValue, _vramAddressRemapping,
_vramAddrIncrementOnSecondReg, _vramReadBuffer, _ppu1OpenBus, _ppu2OpenBus, _cgramAddress, _mosaicSize, _mosaicEnabled,
_mosaicStartScanline, _oamMode, _oamBaseAddress, _oamAddressOffset, _oamRamAddress, _enableOamPriority,
_internalOamAddress, _oamWriteBuffer, _timeOver, _rangeOver, _hiResMode, _screenInterlace, _objInterlace,
_overscanMode, _directColorMode, _colorMathClipMode, _colorMathPreventMode, _colorMathAddSubscreen, _colorMathEnabled,
_colorMathSubstractMode, _colorMathHalveResult, _fixedColor, _hvScrollLatchValue, _hScrollLatchValue,
_horizontalLocation, _horizontalLocToggle, _verticalLocation, _verticalLocationToggle, _locationLatched,
_maskLogic[0], _maskLogic[1], _maskLogic[2], _maskLogic[3], _maskLogic[4], _maskLogic[5],
_windowMaskMain[0], _windowMaskMain[1], _windowMaskMain[2], _windowMaskMain[3], _windowMaskMain[4],
_windowMaskSub[0], _windowMaskSub[1], _windowMaskSub[2], _windowMaskSub[3], _windowMaskSub[4],
_mode7.CenterX, _mode7.CenterY, _mode7.ExtBgEnabled, _mode7.FillWithTile0, _mode7.HorizontalMirroring,
_mode7.HScroll, _mode7.LargeMap, _mode7.Matrix[0], _mode7.Matrix[1], _mode7.Matrix[2], _mode7.Matrix[3],
_mode7.ValueLatch, _mode7.VerticalMirroring, _mode7.VScroll
);
for(int i = 0; i < 4; i++) {
s.Stream(
_layerConfig[i].ChrAddress, _layerConfig[i].DoubleHeight, _layerConfig[i].DoubleWidth, _layerConfig[i].HScroll,
_layerConfig[i].LargeTiles, _layerConfig[i].TilemapAddress, _layerConfig[i].VScroll
);
}
for(int i = 0; i < 2; i++) {
s.Stream(
_window[i].ActiveLayers[0], _window[i].ActiveLayers[1], _window[i].ActiveLayers[2], _window[i].ActiveLayers[3], _window[i].ActiveLayers[4], _window[i].ActiveLayers[5],
_window[i].InvertedLayers[0], _window[i].InvertedLayers[1], _window[i].InvertedLayers[2], _window[i].InvertedLayers[3], _window[i].InvertedLayers[4], _window[i].InvertedLayers[5],
_window[i].Left, _window[i].Right
);
}
s.StreamArray(_vram, Ppu::VideoRamSize);
s.StreamArray(_oamRam, Ppu::SpriteRamSize);
s.StreamArray(_cgram, Ppu::CgRamSize);
}
/* Everything below this point is used to select the proper arguments for templates */
template<uint8_t layerIndex, uint8_t bpp, bool processHighPriority, bool forMainScreen, uint16_t basePaletteOffset, bool hiResMode, bool largeTileWidth, bool largeTileHeight, uint8_t activeWindowCount, bool applyMosaic>
void Ppu::RenderTilemap()

View file

@ -1,6 +1,7 @@
#pragma once
#include "stdafx.h"
#include "PpuTypes.h"
#include "../Utilities/ISerializable.h"
class Console;
class InternalRegisters;
@ -20,7 +21,7 @@ struct SpriteInfo
uint8_t LargeSprite;
};
class Ppu
class Ppu : public ISerializable
{
public:
constexpr static uint32_t SpriteRamSize = 544;
@ -242,4 +243,6 @@ public:
uint8_t Read(uint16_t addr);
void Write(uint32_t addr, uint8_t value);
void Serialize(Serializer &s) override;
};

69
Core/RegisterHandlerB.cpp Normal file
View file

@ -0,0 +1,69 @@
#include "stdafx.h"
#include "RegisterHandlerB.h"
#include "Console.h"
#include "Ppu.h"
#include "Spc.h"
#include "../Utilities/Serializer.h"
RegisterHandlerB::RegisterHandlerB(Console * console, Ppu * ppu, Spc * spc, uint8_t * workRam)
{
_console = console;
_ppu = ppu;
_spc = spc;
_workRam = workRam;
_wramPosition = 0;
_memoryType = SnesMemoryType::Register;
}
uint8_t RegisterHandlerB::Read(uint32_t addr)
{
addr &= 0xFFFF;
if(addr >= 0x2140 && addr <= 0x217F) {
return _spc->Read(addr & 0x03);
} else if(addr == 0x2180) {
uint8_t value = _workRam[_wramPosition];
_console->ProcessWorkRamRead(_wramPosition, value);
_wramPosition = (_wramPosition + 1) & 0x1FFFF;
return value;
} else {
return _ppu->Read(addr);
}
}
uint8_t RegisterHandlerB::Peek(uint32_t addr)
{
//Avoid side effects for now
return 0;
}
void RegisterHandlerB::Write(uint32_t addr, uint8_t value)
{
addr &= 0xFFFF;
if(addr >= 0x2140 && addr <= 0x217F) {
return _spc->Write(addr & 0x03, value);
} if(addr >= 0x2180 && addr <= 0x2183) {
switch(addr & 0xFFFF) {
case 0x2180:
_console->ProcessWorkRamWrite(_wramPosition, value);
_workRam[_wramPosition] = value;
_wramPosition = (_wramPosition + 1) & 0x1FFFF;
break;
case 0x2181: _wramPosition = (_wramPosition & 0x1FF00) | value; break;
case 0x2182: _wramPosition = (_wramPosition & 0x100FF) | (value << 8); break;
case 0x2183: _wramPosition = (_wramPosition & 0xFFFF) | ((value & 0x01) << 16); break;
}
} else {
_ppu->Write(addr, value);
}
}
AddressInfo RegisterHandlerB::GetAbsoluteAddress(uint32_t address)
{
return { -1, SnesMemoryType::CpuMemory };
}
void RegisterHandlerB::Serialize(Serializer &s)
{
s.Stream(_wramPosition);
}

View file

@ -1,75 +1,30 @@
#pragma once
#include "stdafx.h"
#include "IMemoryHandler.h"
#include "Console.h"
#include "Ppu.h"
#include "Spc.h"
#include "../Utilities/ISerializable.h"
class RegisterHandlerB : public IMemoryHandler
class Console;
class Ppu;
class Spc;
class RegisterHandlerB : public IMemoryHandler, public ISerializable
{
private:
Console *_console;
Ppu *_ppu;
Spc *_spc;
uint8_t *_workRam;
uint32_t _wramPosition;
public:
RegisterHandlerB(Console *console, Ppu *ppu, Spc *spc, uint8_t *workRam)
{
_console = console;
_ppu = ppu;
_spc = spc;
_workRam = workRam;
_wramPosition = 0;
_memoryType = SnesMemoryType::Register;
}
RegisterHandlerB(Console *console, Ppu *ppu, Spc *spc, uint8_t *workRam);
uint8_t Read(uint32_t addr) override
{
addr &= 0xFFFF;
if(addr >= 0x2140 && addr <= 0x217F) {
return _spc->Read(addr & 0x03);
} else if(addr == 0x2180) {
uint8_t value = _workRam[_wramPosition];
_console->ProcessWorkRamRead(_wramPosition, value);
_wramPosition = (_wramPosition + 1) & 0x1FFFF;
return value;
} else {
return _ppu->Read(addr);
}
}
uint8_t Read(uint32_t addr) override;
uint8_t Peek(uint32_t addr) override;
void Write(uint32_t addr, uint8_t value) override;
uint8_t Peek(uint32_t addr) override
{
//Avoid side effects for now
return 0;
}
AddressInfo GetAbsoluteAddress(uint32_t address) override;
void Write(uint32_t addr, uint8_t value) override
{
addr &= 0xFFFF;
if(addr >= 0x2140 && addr <= 0x217F) {
return _spc->Write(addr & 0x03, value);
} if(addr >= 0x2180 && addr <= 0x2183) {
switch(addr & 0xFFFF) {
case 0x2180:
_console->ProcessWorkRamWrite(_wramPosition, value);
_workRam[_wramPosition] = value;
_wramPosition = (_wramPosition + 1) & 0x1FFFF;
break;
case 0x2181: _wramPosition = (_wramPosition & 0x1FF00) | value; break;
case 0x2182: _wramPosition = (_wramPosition & 0x100FF) | (value << 8); break;
case 0x2183: _wramPosition = (_wramPosition & 0xFFFF) | ((value & 0x01) << 16); break;
}
} else {
_ppu->Write(addr, value);
}
}
AddressInfo GetAbsoluteAddress(uint32_t address) override
{
return { -1, SnesMemoryType::CpuMemory };
}
void Serialize(Serializer &s) override;
};

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
// Fast SNES SPC-700 DSP emulator (about 3x speed of accurate one)
// Highly accurate SNES SPC-700 DSP emulator
// snes_spc 0.9.0
#ifndef SPC_DSP_H
@ -6,12 +6,14 @@
#include "blargg_common.h"
extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); }
class SPC_DSP {
public:
typedef BOOST::uint8_t uint8_t;
// Setup
// Initializes DSP and has it use the 64K RAM provided
void init( void* ram_64k );
@ -26,14 +28,14 @@ public:
int sample_count() const;
// Emulation
// Resets DSP to power-on state
void reset();
// Emulates pressing reset switch on SNES
void soft_reset();
// Reads/writes DSP registers. For accuracy, you must first call spc_run_dsp()
// Reads/writes DSP registers. For accuracy, you must first call run()
// to catch the DSP up to present.
int read ( int addr ) const;
void write( int addr, int data );
@ -41,23 +43,28 @@ public:
// Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks
// a pair of samples is be generated.
void run( int clock_count );
// Sound control
// Mutes voices corresponding to non-zero bits in mask (overrides VxVOL with 0).
// Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
// Reduces emulation accuracy.
enum { voice_count = 8 };
void mute_voices( int mask );
// If true, prevents channels and global volumes from being phase-negated
void disable_surround( bool disable = true );
// State
// Resets DSP and uses supplied values to initialize registers
enum { register_count = 128 };
void load( uint8_t const regs [register_count] );
// Saves/loads exact emulator state
enum { state_size = 640 }; // maximum space needed when saving
typedef dsp_copy_func_t copy_func_t;
void copy_state( unsigned char** io, copy_func_t );
// Returns non-zero if new key-on events occurred since last call
bool check_kon();
// DSP register addresses
// Global registers
@ -86,6 +93,7 @@ public:
enum { extra_size = 16 };
sample_t* extra() { return m.extra; }
sample_t const* out_pos() const { return m.out; }
void disable_surround( bool ) { } // not supported
public:
BLARGG_DISABLE_NOTHROW
@ -99,18 +107,21 @@ public:
struct voice_t
{
int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling)
int* buf_pos; // place in buffer where next samples will be decoded
int buf_pos; // place in buffer where next samples will be decoded
int interp_pos; // relative fractional position in sample (0x1000 = 1.0)
int brr_addr; // address of current BRR block
int brr_offset; // current decoding offset in BRR block
uint8_t* regs; // pointer to voice's DSP registers
int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc.
int kon_delay; // KON delay/current setup phase
env_mode_t env_mode;
int env; // current envelope level
int hidden_env; // used by GAIN mode 7, very obscure quirk
int volume [2]; // copy of volume from DSP registers, with surround disabled
int enabled; // -1 if enabled, 0 if muted
uint8_t t_envx_out;
};
private:
enum { brr_block_size = 9 };
struct state_t
{
uint8_t regs [register_count];
@ -122,22 +133,53 @@ private:
int every_other_sample; // toggles every sample
int kon; // KON value when last checked
int noise;
int counter;
int echo_offset; // offset from ESA in echo buffer
int echo_length; // number of bytes that echo_offset will stop at
int phase; // next clock cycle to run (0-31)
unsigned counters [4];
bool kon_check; // set when a new KON occurs
// Hidden registers also written to when main register is written to
int new_kon;
uint8_t endx_buf;
uint8_t envx_buf;
uint8_t outx_buf;
// Temporary state between clocks
// read once per sample
int t_pmon;
int t_non;
int t_eon;
int t_dir;
int t_koff;
voice_t voices [voice_count];
// read a few clocks ahead then used
int t_brr_next_addr;
int t_adsr0;
int t_brr_header;
int t_brr_byte;
int t_srcn;
int t_esa;
int t_echo_enabled;
unsigned* counter_select [32];
// internal state that is recalculated every sample
int t_dir_addr;
int t_pitch;
int t_output;
int t_looped;
int t_echo_ptr;
// left/right sums
int t_main_out [2];
int t_echo_out [2];
int t_echo_in [2];
voice_t voices [voice_count];
// non-emulation state
uint8_t* ram; // 64K shared RAM between DSP and SMP
int mute_mask;
int surround_threshold;
sample_t* out;
sample_t* out_end;
sample_t* out_begin;
@ -146,10 +188,49 @@ private:
state_t m;
void init_counter();
void run_counter( int );
void run_counters();
unsigned read_counter( int rate );
int interpolate( voice_t const* v );
void run_envelope( voice_t* const v );
void decode_brr( voice_t* v );
void misc_27();
void misc_28();
void misc_29();
void misc_30();
void voice_output( voice_t const* v, int ch );
void voice_V1( voice_t* const );
void voice_V2( voice_t* const );
void voice_V3( voice_t* const );
void voice_V3a( voice_t* const );
void voice_V3b( voice_t* const );
void voice_V3c( voice_t* const );
void voice_V4( voice_t* const );
void voice_V5( voice_t* const );
void voice_V6( voice_t* const );
void voice_V7( voice_t* const );
void voice_V8( voice_t* const );
void voice_V9( voice_t* const );
void voice_V7_V4_V1( voice_t* const );
void voice_V8_V5_V2( voice_t* const );
void voice_V9_V6_V3( voice_t* const );
void echo_read( int ch );
int echo_output( int ch );
void echo_write( int ch );
void echo_22();
void echo_23();
void echo_24();
void echo_25();
void echo_26();
void echo_27();
void echo_28();
void echo_29();
void echo_30();
void soft_reset_common();
void write_outline( int addr, int data );
void update_voice_vol( int addr );
};
#include <assert.h>
@ -162,51 +243,62 @@ inline int SPC_DSP::read( int addr ) const
return m.regs [addr];
}
inline void SPC_DSP::update_voice_vol( int addr )
{
int l = (int8_t) m.regs [addr + v_voll];
int r = (int8_t) m.regs [addr + v_volr];
if ( l * r < m.surround_threshold )
{
// signs differ, so negate those that are negative
l ^= l >> 7;
r ^= r >> 7;
}
voice_t& v = m.voices [addr >> 4];
int enabled = v.enabled;
v.volume [0] = l & enabled;
v.volume [1] = r & enabled;
}
inline void SPC_DSP::write( int addr, int data )
{
assert( (unsigned) addr < register_count );
m.regs [addr] = (uint8_t) data;
int low = addr & 0x0F;
if ( low < 0x2 ) // voice volumes
{
update_voice_vol( low ^ addr );
}
else if ( low == 0xC )
switch ( addr & 0x0F )
{
case v_envx:
m.envx_buf = (uint8_t) data;
break;
case v_outx:
m.outx_buf = (uint8_t) data;
break;
case 0x0C:
if ( addr == r_kon )
m.new_kon = (uint8_t) data;
if ( addr == r_endx ) // always cleared, regardless of data written
{
m.endx_buf = 0;
m.regs [r_endx] = 0;
}
break;
}
}
inline void SPC_DSP::disable_surround( bool disable )
inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; }
inline bool SPC_DSP::check_kon()
{
m.surround_threshold = disable ? 0 : -0x4000;
bool old = m.kon_check;
m.kon_check = 0;
return old;
}
#define SPC_NO_COPY_STATE_FUNCS 1
#if !SPC_NO_COPY_STATE_FUNCS
#define SPC_LESS_ACCURATE 1
class SPC_State_Copier {
SPC_DSP::copy_func_t func;
unsigned char** buf;
public:
SPC_State_Copier( unsigned char** p, SPC_DSP::copy_func_t f ) { func = f; buf = p; }
void copy( void* state, size_t size );
int copy_int( int state, int size );
void skip( int count );
void extra();
};
#define SPC_COPY( type, state )\
{\
state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\
assert( (BOOST::type) state == state );\
}
#endif
#endif

250
Core/SaveStateManager.cpp Normal file
View file

@ -0,0 +1,250 @@
#include "stdafx.h"
#include "../Utilities/FolderUtilities.h"
#include "../Utilities/ZipWriter.h"
#include "../Utilities/ZipReader.h"
#include "SaveStateManager.h"
#include "MessageManager.h"
#include "Console.h"
#include "EmuSettings.h"
#include "VideoDecoder.h"
#include "BaseCartridge.h"
SaveStateManager::SaveStateManager(shared_ptr<Console> console)
{
_console = console;
_lastIndex = 1;
}
string SaveStateManager::GetStateFilepath(int stateIndex)
{
string romPath = _console->GetRomInfo().RomPath;
string folder = FolderUtilities::GetSaveStateFolder();
string filename = FolderUtilities::GetFilename(romPath, false) + "_" + std::to_string(stateIndex) + ".mst";
return FolderUtilities::CombinePath(folder, filename);
}
uint64_t SaveStateManager::GetStateInfo(int stateIndex)
{
string filepath = SaveStateManager::GetStateFilepath(stateIndex);
ifstream file(filepath, ios::in | ios::binary);
if(file) {
file.close();
return FolderUtilities::GetFileModificationTime(filepath);
}
return 0;
}
void SaveStateManager::MoveToNextSlot()
{
_lastIndex = (_lastIndex % MaxIndex) + 1;
MessageManager::DisplayMessage("SaveStates", "SaveStateSlotSelected", std::to_string(_lastIndex));
}
void SaveStateManager::MoveToPreviousSlot()
{
_lastIndex = (_lastIndex == 1 ? SaveStateManager::MaxIndex : (_lastIndex - 1));
MessageManager::DisplayMessage("SaveStates", "SaveStateSlotSelected", std::to_string(_lastIndex));
}
void SaveStateManager::SaveState()
{
SaveState(_lastIndex);
}
bool SaveStateManager::LoadState()
{
return LoadState(_lastIndex);
}
void SaveStateManager::GetSaveStateHeader(ostream &stream)
{
uint32_t emuVersion = _console->GetSettings()->GetVersion();
uint32_t formatVersion = SaveStateManager::FileFormatVersion;
stream.write("MST", 3);
stream.write((char*)&emuVersion, sizeof(emuVersion));
stream.write((char*)&formatVersion, sizeof(uint32_t));
string sha1Hash = _console->GetCartridge()->GetSha1Hash();
stream.write(sha1Hash.c_str(), sha1Hash.size());
RomInfo romInfo = _console->GetCartridge()->GetRomInfo();
string romName = FolderUtilities::GetFilename(romInfo.RomPath, true);
uint32_t nameLength = (uint32_t)romName.size();
stream.write((char*)&nameLength, sizeof(uint32_t));
stream.write(romName.c_str(), romName.size());
}
void SaveStateManager::SaveState(ostream &stream)
{
GetSaveStateHeader(stream);
_console->Serialize(stream);
}
bool SaveStateManager::SaveState(string filepath)
{
ofstream file(filepath, ios::out | ios::binary);
if(file) {
auto lock = _console->AcquireLock();
SaveState(file);
file.close();
//TODO LUA
/*shared_ptr<Debugger> debugger = _console->GetDebugger(false);
if(debugger) {
debugger->ProcessEvent(EventType::StateSaved);
}*/
return true;
}
return false;
}
void SaveStateManager::SaveState(int stateIndex, bool displayMessage)
{
string filepath = SaveStateManager::GetStateFilepath(stateIndex);
if(SaveState(filepath)) {
if(displayMessage) {
MessageManager::DisplayMessage("SaveStates", "SaveStateSaved", std::to_string(stateIndex));
}
}
}
bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired)
{
char header[3];
stream.read(header, 3);
if(memcmp(header, "MST", 3) == 0) {
uint32_t emuVersion, fileFormatVersion;
stream.read((char*)&emuVersion, sizeof(emuVersion));
if(emuVersion > _console->GetSettings()->GetVersion()) {
MessageManager::DisplayMessage("SaveStates", "SaveStateNewerVersion");
return false;
}
stream.read((char*)&fileFormatVersion, sizeof(fileFormatVersion));
if(fileFormatVersion < 1) {
MessageManager::DisplayMessage("SaveStates", "SaveStateIncompatibleVersion");
return false;
} else {
char hash[41] = {};
stream.read(hash, 40);
uint32_t nameLength = 0;
stream.read((char*)&nameLength, sizeof(uint32_t));
vector<char> nameBuffer(nameLength);
stream.read(nameBuffer.data(), nameBuffer.size());
string romName(nameBuffer.data(), nameLength);
//TODO
/*shared_ptr<BaseCartridge> cartridge = _console->GetCartridge();
if(cartridge) {
string sha1Hash = cartridge->GetSha1Hash();
bool gameLoaded = !sha1Hash.empty();
if(sha1Hash != string(hash)) {
//CRC doesn't match
}
}*/
}
//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();
_console->Deserialize(stream, fileFormatVersion);
return true;
}
MessageManager::DisplayMessage("SaveStates", "SaveStateInvalidFile");
return false;
}
bool SaveStateManager::LoadState(string filepath, bool hashCheckRequired)
{
ifstream file(filepath, ios::in | ios::binary);
bool result = false;
if(file.good()) {
auto lock = _console->AcquireLock();
if(LoadState(file, hashCheckRequired)) {
result = true;
}
file.close();
//TODO LUA
/*shared_ptr<Debugger> debugger = _console->GetDebugger(false);
if(debugger) {
debugger->ProcessEvent(EventType::StateLoaded);
}*/
} else {
MessageManager::DisplayMessage("SaveStates", "SaveStateEmpty");
}
return result;
}
bool SaveStateManager::LoadState(int stateIndex)
{
string filepath = SaveStateManager::GetStateFilepath(stateIndex);
if(LoadState(filepath, false)) {
MessageManager::DisplayMessage("SaveStates", "SaveStateLoaded", std::to_string(stateIndex));
return true;
}
return false;
}
//TODO
/*
void SaveStateManager::SaveRecentGame(string romName, string romPath, string patchPath)
{
if(!_console->GetSettings()->CheckFlag(EmulationFlags::ConsoleMode) && !_console->GetSettings()->CheckFlag(EmulationFlags::DisableGameSelectionScreen) && _console->GetRomInfo().Format != RomFormat::Nsf) {
string filename = FolderUtilities::GetFilename(_console->GetRomInfo().RomName, false) + ".rgd";
ZipWriter writer;
writer.Initialize(FolderUtilities::CombinePath(FolderUtilities::GetRecentGamesFolder(), filename));
std::stringstream pngStream;
_console->GetVideoDecoder()->TakeScreenshot(pngStream);
writer.AddFile(pngStream, "Screenshot.png");
std::stringstream stateStream;
SaveStateManager::SaveState(stateStream);
writer.AddFile(stateStream, "Savestate.mst");
std::stringstream romInfoStream;
romInfoStream << romName << std::endl;
romInfoStream << romPath << std::endl;
romInfoStream << patchPath << std::endl;
writer.AddFile(romInfoStream, "RomInfo.txt");
writer.Save();
}
}
void SaveStateManager::LoadRecentGame(string filename, bool resetGame)
{
ZipReader reader;
reader.LoadArchive(filename);
stringstream romInfoStream, stateStream;
reader.GetStream("RomInfo.txt", romInfoStream);
reader.GetStream("Savestate.mst", stateStream);
string romName, romPath, patchPath;
std::getline(romInfoStream, romName);
std::getline(romInfoStream, romPath);
std::getline(romInfoStream, patchPath);
_console->Pause();
try {
if(_console->Initialize(romPath, patchPath)) {
if(!resetGame) {
SaveStateManager::LoadState(stateStream, false);
}
}
} catch(std::exception ex) {
_console->Stop();
}
_console->Resume();
}
*/

40
Core/SaveStateManager.h Normal file
View file

@ -0,0 +1,40 @@
#pragma once
#include "stdafx.h"
class Console;
class SaveStateManager
{
private:
static constexpr uint32_t MaxIndex = 10;
atomic<uint32_t> _lastIndex;
shared_ptr<Console> _console;
string GetStateFilepath(int stateIndex);
public:
static constexpr uint32_t FileFormatVersion = 1;
SaveStateManager(shared_ptr<Console> console);
uint64_t GetStateInfo(int stateIndex);
void SaveState();
bool LoadState();
void GetSaveStateHeader(ostream & stream);
void SaveState(ostream &stream);
bool SaveState(string filepath);
void SaveState(int stateIndex, bool displayMessage = true);
bool LoadState(istream &stream, bool hashCheckRequired = true);
bool LoadState(string filepath, bool hashCheckRequired = true);
bool LoadState(int stateIndex);
//void SaveRecentGame(string romName, string romPath, string patchPath);
//void LoadRecentGame(string filename, bool resetGame);
void MoveToNextSlot();
void MoveToPreviousSlot();
};

View file

@ -316,6 +316,7 @@ enum class EmulatorShortcut
LoadStateSlot8,
LoadStateSlot9,
LoadStateSlot10,
LoadStateSlotAuto,
LoadStateFromFile,
OpenFile,

View file

@ -1,6 +1,7 @@
#pragma once
#include "stdafx.h"
#include "BaseControlDevice.h"
#include "../Utilities/Serializer.h"
class SnesController : public BaseControlDevice
{
@ -52,13 +53,11 @@ protected:
((uint8_t)IsPressed(Buttons::R) << 11);
}
//TODO
/*
void StreamState(bool saving) override
void Serialize(Serializer &s) override
{
BaseControlDevice::StreamState(saving);
Stream(_stateBuffer);
}*/
BaseControlDevice::Serialize(s);
s.Stream(_stateBuffer);
}
void RefreshStateBuffer() override
{

View file

@ -4,6 +4,7 @@
#include "Console.h"
#include "MemoryManager.h"
#include "SoundMixer.h"
#include "../Utilities/Serializer.h"
Spc::Spc(shared_ptr<Console> console, vector<uint8_t> &spcRomData)
{
@ -55,3 +56,30 @@ void Spc::ProcessEndFrame()
uint64_t remainder = (_console->GetMemoryManager()->GetMasterClock() - _startFrameMasterClock) * 1024000 % 21477000 / 1024000;
_startFrameMasterClock = _console->GetMemoryManager()->GetMasterClock() - remainder;
}
void Spc::Serialize(Serializer &s)
{
s.Stream(_startFrameMasterClock);
uint8_t state[SNES_SPC::state_size];
memset(state, 0, SNES_SPC::state_size);
if(s.IsSaving()) {
uint8_t *out = state;
_spc->copy_state(&out, [](uint8_t** output, void* in, size_t size) {
memcpy(*output, in, size);
*output += size;
});
s.StreamArray(state, SNES_SPC::state_size);
} else {
s.StreamArray(state, SNES_SPC::state_size);
uint8_t *in = state;
_spc->copy_state(&in, [](uint8_t** input, void* output, size_t size) {
memcpy(output, *input, size);
*input += size;
});
_spc->set_output(_soundBuffer, Spc::SampleBufferSize >> 1);
}
}

View file

@ -1,10 +1,11 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/ISerializable.h"
class Console;
struct SNES_SPC;
class Spc
class Spc : public ISerializable
{
private:
static constexpr int SampleBufferSize = 0x100000;
@ -25,4 +26,6 @@ public:
void Write(uint32_t addr, uint8_t value);
void ProcessEndFrame();
void Serialize(Serializer &s) override;
};

View file

@ -1,7 +1,9 @@
#include "stdafx.h"
#include "../Core/Console.h"
#include "../Core/EmuSettings.h"
#include "../Core/VideoDecoder.h"
#include "../Core/MessageManager.h"
#include "../Core/SaveStateManager.h"
#include "../Core/INotificationListener.h"
#include "../Core/KeyManager.h"
#include "../Core/ShortcutKeyHandler.h"
@ -36,7 +38,7 @@ extern "C" {
return true;
}
DllExport uint32_t __stdcall GetMesenVersion() { return 0x00000100; }
DllExport uint32_t __stdcall GetMesenVersion() { return _console->GetSettings()->GetVersion(); }
DllExport void __stdcall InitDll()
{
@ -157,6 +159,12 @@ extern "C" {
DllExport void __stdcall WriteLogEntry(char* message) { MessageManager::Log(message); }
DllExport void __stdcall SaveState(uint32_t stateIndex) { _console->GetSaveStateManager()->SaveState(stateIndex); }
DllExport void __stdcall LoadState(uint32_t stateIndex) { _console->GetSaveStateManager()->LoadState(stateIndex); }
DllExport void __stdcall SaveStateFile(char* filepath) { _console->GetSaveStateManager()->SaveState(filepath); }
DllExport void __stdcall LoadStateFile(char* filepath) { _console->GetSaveStateManager()->LoadState(filepath); }
DllExport int64_t __stdcall GetStateInfo(uint32_t stateIndex) { return _console->GetSaveStateManager()->GetStateInfo(stateIndex); }
DllExport void __stdcall PgoRunTest(vector<string> testRoms, bool enableDebugger)
{
FolderUtilities::SetHomeFolder("../PGOMesenHome");

View file

@ -107,7 +107,7 @@ namespace Mesen.GUI.Config
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.LoadStateSlot5, new KeyCombination() { Key1 = InputApi.GetKeyCode("F5") }));
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.LoadStateSlot6, new KeyCombination() { Key1 = InputApi.GetKeyCode("F6") }));
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.LoadStateSlot7, new KeyCombination() { Key1 = InputApi.GetKeyCode("F7") }));
//TODO ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.LoadStateSlotAuto, new KeyCombination() { Key1 = InputApi.GetKeyCode("F8") }));
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.LoadStateSlotAuto, new KeyCombination() { Key1 = InputApi.GetKeyCode("F8") }));
ShortcutKeys1.Add(new ShortcutKeyInfo(EmulatorShortcut.LoadStateFromFile, new KeyCombination() { Key1 = InputApi.GetKeyCode("Ctrl"), Key2 = InputApi.GetKeyCode("L") }));
ShortcutKeys2 = new List<ShortcutKeyInfo>();

View file

@ -1,4 +1,5 @@
using Mesen.GUI.Forms;
using Mesen.GUI.Emulation;
using Mesen.GUI.Forms;
using System;
using System.Collections.Generic;
using System.IO;

View file

@ -73,6 +73,7 @@ namespace Mesen.GUI.Config.Shortcuts
LoadStateSlot8,
LoadStateSlot9,
LoadStateSlot10,
LoadStateSlotAuto,
LoadStateFromFile,
OpenFile

View file

@ -1,80 +0,0 @@
using Mesen.GUI.Config;
using Mesen.GUI.Forms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Mesen.GUI
{
public static class EmuRunner
{
private static Thread _emuThread = null;
public static void LoadRom(ResourcePath romPath, ResourcePath? patchPath = null)
{
if(!frmSelectRom.SelectRom(ref romPath)) {
return;
}
EmuApi.LoadRom(romPath, patchPath);
ConfigManager.Config.RecentFiles.AddRecentFile(romPath, patchPath);
_emuThread = new Thread(() => {
EmuApi.Run();
});
_emuThread.Start();
}
public static bool IsRunning()
{
return _emuThread != null;
}
public static void SaveState(uint slot)
{
if(_emuThread != null) {
//EmuApi.SaveState(slot);
}
}
public static void LoadState(uint slot)
{
if(_emuThread != null) {
//EmuApi.LoadState(slot);
}
}
public static void LoadStateFromFile()
{
if(_emuThread != null) {
using(OpenFileDialog ofd = new OpenFileDialog()) {
ofd.InitialDirectory = ConfigManager.SaveStateFolder;
ofd.SetFilter(ResourceHelper.GetMessage("FilterSavestate"));
if(ofd.ShowDialog(Application.OpenForms[0]) == DialogResult.OK) {
//EmuApi.LoadStateFile(ofd.FileName);
}
}
}
}
public static void SaveStateToFile()
{
if(_emuThread != null) {
using(SaveFileDialog sfd = new SaveFileDialog()) {
sfd.InitialDirectory = ConfigManager.SaveStateFolder;
//TODO
//sfd.FileName = EmuApi.GetRomInfo().GetRomName() + ".mst";
sfd.SetFilter(ResourceHelper.GetMessage("FilterSavestate"));
if(sfd.ShowDialog(Application.OpenForms[0]) == DialogResult.OK) {
//EmuApi.SaveStateFile(sfd.FileName);
}
}
}
}
}
}

38
UI/Emulation/EmuRunner.cs Normal file
View file

@ -0,0 +1,38 @@
using Mesen.GUI.Config;
using Mesen.GUI.Forms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Mesen.GUI.Emulation
{
public static class EmuRunner
{
private static Thread _emuThread = null;
public static void LoadRom(ResourcePath romPath, ResourcePath? patchPath = null)
{
if(!frmSelectRom.SelectRom(ref romPath)) {
return;
}
EmuApi.LoadRom(romPath, patchPath);
ConfigManager.Config.RecentFiles.AddRecentFile(romPath, patchPath);
_emuThread = new Thread(() => {
EmuApi.Run();
});
_emuThread.Start();
}
public static bool IsRunning()
{
return _emuThread != null;
}
}
}

View file

@ -0,0 +1,116 @@
using Mesen.GUI.Config;
using Mesen.GUI.Config.Shortcuts;
using Mesen.GUI.Forms;
using Mesen.GUI.Properties;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Mesen.GUI.Emulation
{
public static class SaveStateManager
{
private const int NumberOfSaveSlots = 10;
public static void UpdateStateMenu(ToolStripMenuItem menu, bool forSave)
{
for(uint i = 1; i <= NumberOfSaveSlots + (forSave ? 0 : 1); i++) {
Int64 fileTime = EmuApi.GetStateInfo(i);
string label;
bool isAutoSaveSlot = i == NumberOfSaveSlots + 1;
string slotName = isAutoSaveSlot ? "Auto" : i.ToString();
if(fileTime == 0) {
label = slotName + ". " + ResourceHelper.GetMessage("EmptyState");
} else {
DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(fileTime).ToLocalTime();
label = slotName + ". " + dateTime.ToShortDateString() + " " + dateTime.ToShortTimeString();
}
if(i == NumberOfSaveSlots + 1) {
//Autosave slot (load only)
menu.DropDownItems[NumberOfSaveSlots + 1].Text = label;
} else {
menu.DropDownItems[(int)i - 1].Text = label;
}
}
}
public static void InitializeStateMenu(ToolStripMenuItem menu, bool forSave, ShortcutHandler shortcutHandler)
{
Action<uint> addSaveStateInfo = (i) => {
ToolStripMenuItem item = new ToolStripMenuItem();
menu.DropDownItems.Add(item);
if(forSave) {
shortcutHandler.BindShortcut(item, (EmulatorShortcut)((int)EmulatorShortcut.SaveStateSlot1 + i - 1));
} else {
shortcutHandler.BindShortcut(item, (EmulatorShortcut)((int)EmulatorShortcut.LoadStateSlot1 + i - 1));
}
};
for(uint i = 1; i <= NumberOfSaveSlots; i++) {
addSaveStateInfo(i);
}
if(!forSave) {
menu.DropDownItems.Add("-");
addSaveStateInfo(NumberOfSaveSlots + 1);
menu.DropDownItems.Add("-");
ToolStripMenuItem loadFromFile = new ToolStripMenuItem(ResourceHelper.GetMessage("LoadFromFile"), Resources.Folder);
menu.DropDownItems.Add(loadFromFile);
shortcutHandler.BindShortcut(loadFromFile, EmulatorShortcut.LoadStateFromFile);
} else {
menu.DropDownItems.Add("-");
ToolStripMenuItem saveToFile = new ToolStripMenuItem(ResourceHelper.GetMessage("SaveToFile"), Resources.SaveFloppy);
menu.DropDownItems.Add(saveToFile);
shortcutHandler.BindShortcut(saveToFile, EmulatorShortcut.SaveStateToFile);
}
}
public static void SaveState(uint slot)
{
if(EmuRunner.IsRunning()) {
EmuApi.SaveState(slot);
}
}
public static void LoadState(uint slot)
{
if(EmuRunner.IsRunning()) {
EmuApi.LoadState(slot);
}
}
public static void LoadStateFromFile()
{
if(EmuRunner.IsRunning()) {
using(OpenFileDialog ofd = new OpenFileDialog()) {
ofd.InitialDirectory = ConfigManager.SaveStateFolder;
ofd.SetFilter(ResourceHelper.GetMessage("FilterSavestate"));
if(ofd.ShowDialog(Application.OpenForms[0]) == DialogResult.OK) {
EmuApi.LoadStateFile(ofd.FileName);
}
}
}
}
public static void SaveStateToFile()
{
if(EmuRunner.IsRunning()) {
using(SaveFileDialog sfd = new SaveFileDialog()) {
sfd.InitialDirectory = ConfigManager.SaveStateFolder;
//TODO
//sfd.FileName = EmuApi.GetRomInfo().GetRomName() + ".mst";
sfd.SetFilter(ResourceHelper.GetMessage("FilterSavestate"));
if(sfd.ShowDialog(Application.OpenForms[0]) == DialogResult.OK) {
EmuApi.SaveStateFile(sfd.FileName);
}
}
}
}
}
}

View file

@ -1,5 +1,6 @@
using Mesen.GUI.Config;
using Mesen.GUI.Config.Shortcuts;
using Mesen.GUI.Forms;
using System;
using System.Collections.Generic;
using System.IO;
@ -8,7 +9,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Mesen.GUI.Forms
namespace Mesen.GUI.Emulation
{
public class ShortcutHandler
{
@ -92,29 +93,30 @@ namespace Mesen.GUI.Forms
case EmulatorShortcut.TakeScreenshot: EmuApi.TakeScreenshot(); break;
case EmulatorShortcut.LoadStateFromFile: EmuRunner.LoadStateFromFile(); break;
case EmulatorShortcut.SaveStateToFile: EmuRunner.SaveStateToFile(); break;
case EmulatorShortcut.LoadStateFromFile: SaveStateManager.LoadStateFromFile(); break;
case EmulatorShortcut.SaveStateToFile: SaveStateManager.SaveStateToFile(); break;
case EmulatorShortcut.SaveStateSlot1: EmuRunner.SaveState(1); break;
case EmulatorShortcut.SaveStateSlot2: EmuRunner.SaveState(2); break;
case EmulatorShortcut.SaveStateSlot3: EmuRunner.SaveState(3); break;
case EmulatorShortcut.SaveStateSlot4: EmuRunner.SaveState(4); break;
case EmulatorShortcut.SaveStateSlot5: EmuRunner.SaveState(5); break;
case EmulatorShortcut.SaveStateSlot6: EmuRunner.SaveState(6); break;
case EmulatorShortcut.SaveStateSlot7: EmuRunner.SaveState(7); break;
case EmulatorShortcut.SaveStateSlot8: EmuRunner.SaveState(8); break;
case EmulatorShortcut.SaveStateSlot9: EmuRunner.SaveState(9); break;
case EmulatorShortcut.SaveStateSlot10: EmuRunner.SaveState(10); break;
case EmulatorShortcut.LoadStateSlot1: EmuRunner.LoadState(1); break;
case EmulatorShortcut.LoadStateSlot2: EmuRunner.LoadState(2); break;
case EmulatorShortcut.LoadStateSlot3: EmuRunner.LoadState(3); break;
case EmulatorShortcut.LoadStateSlot4: EmuRunner.LoadState(4); break;
case EmulatorShortcut.LoadStateSlot5: EmuRunner.LoadState(5); break;
case EmulatorShortcut.LoadStateSlot6: EmuRunner.LoadState(6); break;
case EmulatorShortcut.LoadStateSlot7: EmuRunner.LoadState(7); break;
case EmulatorShortcut.LoadStateSlot8: EmuRunner.LoadState(8); break;
case EmulatorShortcut.LoadStateSlot9: EmuRunner.LoadState(9); break;
case EmulatorShortcut.LoadStateSlot10: EmuRunner.LoadState(10); break;
case EmulatorShortcut.SaveStateSlot1: SaveStateManager.SaveState(1); break;
case EmulatorShortcut.SaveStateSlot2: SaveStateManager.SaveState(2); break;
case EmulatorShortcut.SaveStateSlot3: SaveStateManager.SaveState(3); break;
case EmulatorShortcut.SaveStateSlot4: SaveStateManager.SaveState(4); break;
case EmulatorShortcut.SaveStateSlot5: SaveStateManager.SaveState(5); break;
case EmulatorShortcut.SaveStateSlot6: SaveStateManager.SaveState(6); break;
case EmulatorShortcut.SaveStateSlot7: SaveStateManager.SaveState(7); break;
case EmulatorShortcut.SaveStateSlot8: SaveStateManager.SaveState(8); break;
case EmulatorShortcut.SaveStateSlot9: SaveStateManager.SaveState(9); break;
case EmulatorShortcut.SaveStateSlot10: SaveStateManager.SaveState(10); break;
case EmulatorShortcut.LoadStateSlot1: SaveStateManager.LoadState(1); break;
case EmulatorShortcut.LoadStateSlot2: SaveStateManager.LoadState(2); break;
case EmulatorShortcut.LoadStateSlot3: SaveStateManager.LoadState(3); break;
case EmulatorShortcut.LoadStateSlot4: SaveStateManager.LoadState(4); break;
case EmulatorShortcut.LoadStateSlot5: SaveStateManager.LoadState(5); break;
case EmulatorShortcut.LoadStateSlot6: SaveStateManager.LoadState(6); break;
case EmulatorShortcut.LoadStateSlot7: SaveStateManager.LoadState(7); break;
case EmulatorShortcut.LoadStateSlot8: SaveStateManager.LoadState(8); break;
case EmulatorShortcut.LoadStateSlot9: SaveStateManager.LoadState(9); break;
case EmulatorShortcut.LoadStateSlot10: SaveStateManager.LoadState(10); break;
case EmulatorShortcut.LoadStateSlotAuto: SaveStateManager.LoadState(11); break;
}
//TODO

View file

@ -32,6 +32,9 @@
this.mnuFile = new System.Windows.Forms.ToolStripMenuItem();
this.mnuOpen = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
this.mnuSaveState = new System.Windows.Forms.ToolStripMenuItem();
this.mnuLoadState = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem10 = new System.Windows.Forms.ToolStripSeparator();
this.mnuRecentFiles = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem6 = new System.Windows.Forms.ToolStripSeparator();
this.mnuExit = new System.Windows.Forms.ToolStripMenuItem();
@ -152,6 +155,9 @@
this.mnuFile.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.mnuOpen,
this.toolStripMenuItem2,
this.mnuSaveState,
this.mnuLoadState,
this.toolStripMenuItem10,
this.mnuRecentFiles,
this.toolStripMenuItem6,
this.mnuExit});
@ -165,30 +171,49 @@
this.mnuOpen.Image = global::Mesen.GUI.Properties.Resources.Folder;
this.mnuOpen.Name = "mnuOpen";
this.mnuOpen.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O)));
this.mnuOpen.Size = new System.Drawing.Size(146, 22);
this.mnuOpen.Size = new System.Drawing.Size(152, 22);
this.mnuOpen.Text = "Open";
//
// toolStripMenuItem2
//
this.toolStripMenuItem2.Name = "toolStripMenuItem2";
this.toolStripMenuItem2.Size = new System.Drawing.Size(143, 6);
this.toolStripMenuItem2.Size = new System.Drawing.Size(149, 6);
//
// mnuSaveState
//
this.mnuSaveState.Name = "mnuSaveState";
this.mnuSaveState.Size = new System.Drawing.Size(152, 22);
this.mnuSaveState.Text = "Save State";
this.mnuSaveState.DropDownOpening += new System.EventHandler(this.mnuSaveState_DropDownOpening);
//
// mnuLoadState
//
this.mnuLoadState.Name = "mnuLoadState";
this.mnuLoadState.Size = new System.Drawing.Size(152, 22);
this.mnuLoadState.Text = "Load State";
this.mnuLoadState.DropDownOpening += new System.EventHandler(this.mnuLoadState_DropDownOpening);
//
// toolStripMenuItem10
//
this.toolStripMenuItem10.Name = "toolStripMenuItem10";
this.toolStripMenuItem10.Size = new System.Drawing.Size(149, 6);
//
// mnuRecentFiles
//
this.mnuRecentFiles.Name = "mnuRecentFiles";
this.mnuRecentFiles.Size = new System.Drawing.Size(146, 22);
this.mnuRecentFiles.Size = new System.Drawing.Size(152, 22);
this.mnuRecentFiles.Text = "Recent Files";
//
// toolStripMenuItem6
//
this.toolStripMenuItem6.Name = "toolStripMenuItem6";
this.toolStripMenuItem6.Size = new System.Drawing.Size(143, 6);
this.toolStripMenuItem6.Size = new System.Drawing.Size(149, 6);
//
// mnuExit
//
this.mnuExit.Image = global::Mesen.GUI.Properties.Resources.Exit;
this.mnuExit.Name = "mnuExit";
this.mnuExit.Size = new System.Drawing.Size(146, 22);
this.mnuExit.Size = new System.Drawing.Size(152, 22);
this.mnuExit.Text = "Exit";
this.mnuExit.Click += new System.EventHandler(this.mnuExit_Click);
//
@ -209,7 +234,7 @@
this.mnuPause.Enabled = false;
this.mnuPause.Image = global::Mesen.GUI.Properties.Resources.MediaPause;
this.mnuPause.Name = "mnuPause";
this.mnuPause.Size = new System.Drawing.Size(139, 22);
this.mnuPause.Size = new System.Drawing.Size(152, 22);
this.mnuPause.Text = "Pause";
//
// mnuReset
@ -217,7 +242,7 @@
this.mnuReset.Enabled = false;
this.mnuReset.Image = global::Mesen.GUI.Properties.Resources.Refresh;
this.mnuReset.Name = "mnuReset";
this.mnuReset.Size = new System.Drawing.Size(139, 22);
this.mnuReset.Size = new System.Drawing.Size(152, 22);
this.mnuReset.Text = "Reset";
//
// mnuPowerCycle
@ -225,19 +250,19 @@
this.mnuPowerCycle.Enabled = false;
this.mnuPowerCycle.Image = global::Mesen.GUI.Properties.Resources.PowerCycle;
this.mnuPowerCycle.Name = "mnuPowerCycle";
this.mnuPowerCycle.Size = new System.Drawing.Size(139, 22);
this.mnuPowerCycle.Size = new System.Drawing.Size(152, 22);
this.mnuPowerCycle.Text = "Power Cycle";
//
// toolStripMenuItem24
//
this.toolStripMenuItem24.Name = "toolStripMenuItem24";
this.toolStripMenuItem24.Size = new System.Drawing.Size(136, 6);
this.toolStripMenuItem24.Size = new System.Drawing.Size(149, 6);
//
// mnuPowerOff
//
this.mnuPowerOff.Image = global::Mesen.GUI.Properties.Resources.MediaStop;
this.mnuPowerOff.Name = "mnuPowerOff";
this.mnuPowerOff.Size = new System.Drawing.Size(139, 22);
this.mnuPowerOff.Size = new System.Drawing.Size(152, 22);
this.mnuPowerOff.Text = "Power Off";
//
// optionsToolStripMenuItem
@ -272,7 +297,7 @@
this.mnuShowFPS});
this.mnuEmulationSpeed.Image = global::Mesen.GUI.Properties.Resources.Speed;
this.mnuEmulationSpeed.Name = "mnuEmulationSpeed";
this.mnuEmulationSpeed.Size = new System.Drawing.Size(152, 22);
this.mnuEmulationSpeed.Size = new System.Drawing.Size(135, 22);
this.mnuEmulationSpeed.Text = "Speed";
this.mnuEmulationSpeed.DropDownOpening += new System.EventHandler(this.mnuEmulationSpeed_DropDownOpening);
//
@ -360,7 +385,7 @@
this.mnuFullscreen});
this.mnuVideoScale.Image = global::Mesen.GUI.Properties.Resources.Fullscreen;
this.mnuVideoScale.Name = "mnuVideoScale";
this.mnuVideoScale.Size = new System.Drawing.Size(152, 22);
this.mnuVideoScale.Size = new System.Drawing.Size(135, 22);
this.mnuVideoScale.Text = "Video Size";
this.mnuVideoScale.DropDownOpening += new System.EventHandler(this.mnuVideoScale_DropDownOpening);
//
@ -446,7 +471,7 @@
this.mnuBilinearInterpolation});
this.mnuVideoFilter.Image = global::Mesen.GUI.Properties.Resources.VideoFilter;
this.mnuVideoFilter.Name = "mnuVideoFilter";
this.mnuVideoFilter.Size = new System.Drawing.Size(152, 22);
this.mnuVideoFilter.Size = new System.Drawing.Size(135, 22);
this.mnuVideoFilter.Text = "Video Filter";
this.mnuVideoFilter.DropDownOpening += new System.EventHandler(this.mnuVideoFilter_DropDownOpening);
//
@ -627,13 +652,13 @@
// toolStripMenuItem4
//
this.toolStripMenuItem4.Name = "toolStripMenuItem4";
this.toolStripMenuItem4.Size = new System.Drawing.Size(149, 6);
this.toolStripMenuItem4.Size = new System.Drawing.Size(132, 6);
//
// mnuAudioConfig
//
this.mnuAudioConfig.Image = global::Mesen.GUI.Properties.Resources.Audio;
this.mnuAudioConfig.Name = "mnuAudioConfig";
this.mnuAudioConfig.Size = new System.Drawing.Size(152, 22);
this.mnuAudioConfig.Size = new System.Drawing.Size(135, 22);
this.mnuAudioConfig.Text = "Audio";
this.mnuAudioConfig.Click += new System.EventHandler(this.mnuAudioConfig_Click);
//
@ -641,20 +666,20 @@
//
this.mnuVideoConfig.Image = global::Mesen.GUI.Properties.Resources.VideoOptions;
this.mnuVideoConfig.Name = "mnuVideoConfig";
this.mnuVideoConfig.Size = new System.Drawing.Size(152, 22);
this.mnuVideoConfig.Size = new System.Drawing.Size(135, 22);
this.mnuVideoConfig.Text = "Video";
this.mnuVideoConfig.Click += new System.EventHandler(this.mnuVideoConfig_Click);
//
// toolStripMenuItem3
//
this.toolStripMenuItem3.Name = "toolStripMenuItem3";
this.toolStripMenuItem3.Size = new System.Drawing.Size(149, 6);
this.toolStripMenuItem3.Size = new System.Drawing.Size(132, 6);
//
// mnuPreferences
//
this.mnuPreferences.Image = global::Mesen.GUI.Properties.Resources.Settings;
this.mnuPreferences.Name = "mnuPreferences";
this.mnuPreferences.Size = new System.Drawing.Size(152, 22);
this.mnuPreferences.Size = new System.Drawing.Size(135, 22);
this.mnuPreferences.Text = "Preferences";
this.mnuPreferences.Click += new System.EventHandler(this.mnuPreferences_Click);
//
@ -940,5 +965,8 @@
private System.Windows.Forms.ToolStripMenuItem mnuLogWindow;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem7;
private System.Windows.Forms.ToolStripMenuItem mnuTakeScreenshot;
private System.Windows.Forms.ToolStripMenuItem mnuSaveState;
private System.Windows.Forms.ToolStripMenuItem mnuLoadState;
private System.Windows.Forms.ToolStripSeparator toolStripMenuItem10;
}
}

View file

@ -1,6 +1,7 @@
using Mesen.GUI.Config;
using Mesen.GUI.Config.Shortcuts;
using Mesen.GUI.Debugger;
using Mesen.GUI.Emulation;
using Mesen.GUI.Forms.Config;
using System;
using System.Collections.Generic;
@ -42,6 +43,9 @@ namespace Mesen.GUI.Forms
_notifListener = new NotificationListener();
_notifListener.OnNotification += OnNotificationReceived;
SaveStateManager.InitializeStateMenu(mnuSaveState, true, _shortcuts);
SaveStateManager.InitializeStateMenu(mnuLoadState, false, _shortcuts);
BindShortcuts();
}
@ -64,6 +68,13 @@ namespace Mesen.GUI.Forms
private void OnNotificationReceived(NotificationEventArgs e)
{
switch(e.NotificationType) {
case ConsoleNotificationType.GameLoaded:
this.BeginInvoke((Action)(() => {
SaveStateManager.UpdateStateMenu(mnuLoadState, false);
SaveStateManager.UpdateStateMenu(mnuSaveState, true);
}));
break;
case ConsoleNotificationType.ResolutionChanged:
ScreenSize size = EmuApi.GetScreenSize(false);
this.BeginInvoke((Action)(() => {
@ -327,5 +338,15 @@ namespace Mesen.GUI.Forms
mnuEmuSpeedTriple.Checked = emulationSpeed == 300;
mnuEmuSpeedMaximumSpeed.Checked = emulationSpeed == 0;
}
private void mnuLoadState_DropDownOpening(object sender, EventArgs e)
{
SaveStateManager.UpdateStateMenu(mnuLoadState, false);
}
private void mnuSaveState_DropDownOpening(object sender, EventArgs e)
{
SaveStateManager.UpdateStateMenu(mnuSaveState, true);
}
}
}

View file

@ -54,6 +54,12 @@ namespace Mesen.GUI
[DllImport(DllPath)] public static extern void DisplayMessage([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string title, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string message, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string param1 = null);
[DllImport(DllPath)] public static extern IntPtr GetArchiveRomList([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filename);
[DllImport(DllPath)] public static extern void SaveState(UInt32 stateIndex);
[DllImport(DllPath)] public static extern void LoadState(UInt32 stateIndex);
[DllImport(DllPath)] public static extern void SaveStateFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filepath);
[DllImport(DllPath)] public static extern void LoadStateFile([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8Marshaler))]string filepath);
[DllImport(DllPath)] public static extern Int64 GetStateInfo(UInt32 stateIndex);
}
public struct ScreenSize

View file

@ -460,6 +460,7 @@
<SubType>Form</SubType>
</Compile>
<Compile Include="Debugger\WatchManager.cs" />
<Compile Include="Emulation\SaveStateManager.cs" />
<Compile Include="Forms\BaseConfigForm.Designer.cs">
<DependentUpon>BaseConfigForm.cs</DependentUpon>
</Compile>
@ -525,7 +526,7 @@
<Compile Include="Forms\OpenSaveFileDialogExtensions.cs" />
<Compile Include="Forms\ResourceHelper.cs" />
<Compile Include="Forms\ResourcePath.cs" />
<Compile Include="Forms\ShortcutHandler.cs" />
<Compile Include="Emulation\ShortcutHandler.cs" />
<Compile Include="Interop\DebugApi.cs" />
<Compile Include="Interop\ConfigApi.cs" />
<Compile Include="Interop\InputApi.cs" />
@ -535,7 +536,7 @@
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ResourceExtractor.cs" />
<Compile Include="EmuRunner.cs" />
<Compile Include="Emulation\EmuRunner.cs" />
<Compile Include="RuntimeChecker.cs" />
<Compile Include="SingleInstance.cs" />
<Compile Include="Utilities\ArchiveHelper.cs" />

10
Utilities/ISerializable.h Normal file
View file

@ -0,0 +1,10 @@
#pragma once
class Serializer;
class ISerializable
{
public:
virtual void Serialize(Serializer &s) = 0;
};

140
Utilities/Serializer.cpp Normal file
View file

@ -0,0 +1,140 @@
#include "stdafx.h"
#include <algorithm>
#include "Serializer.h"
#include "ISerializable.h"
Serializer::Serializer(uint32_t version)
{
_version = version;
_streamSize = 0x50000;
_stream = new uint8_t[_streamSize];
_position = 0;
_saving = true;
}
Serializer::Serializer(istream &file, uint32_t version)
{
_version = version;
_position = 0;
_saving = false;
file.read((char*)&_streamSize, sizeof(_streamSize));
_stream = new uint8_t[_streamSize];
file.read((char*)_stream, _streamSize);
}
Serializer::~Serializer()
{
delete[] _stream;
}
void Serializer::EnsureCapacity(uint32_t typeSize)
{
//Make sure the current block/stream is large enough to fit the next write
uint32_t oldSize;
uint32_t sizeRequired;
uint8_t *oldBuffer;
if(_inBlock) {
oldBuffer = _blockBuffer;
oldSize = _blockSize;
sizeRequired = _blockPosition + typeSize;
} else {
oldBuffer = _stream;
oldSize = _streamSize;
sizeRequired = _position + typeSize;
}
uint8_t *newBuffer = nullptr;
uint32_t newSize = oldSize * 2;
if(oldSize < sizeRequired) {
while(newSize < sizeRequired) {
newSize *= 2;
}
newBuffer = new uint8_t[newSize];
memcpy(newBuffer, oldBuffer, oldSize);
delete[] oldBuffer;
}
if(newBuffer) {
if(_inBlock) {
_blockBuffer = newBuffer;
_blockSize = newSize;
} else {
_stream = newBuffer;
_streamSize = newSize;
}
}
}
void Serializer::RecursiveStream()
{
}
void Serializer::StreamStartBlock()
{
if(_inBlock) {
throw new std::runtime_error("Cannot start a new block before ending the last block");
}
if(!_saving) {
InternalStream(_blockSize);
_blockSize = std::min(_blockSize, (uint32_t)0xFFFFF);
_blockBuffer = new uint8_t[_blockSize];
ArrayInfo<uint8_t> arrayInfo = { _blockBuffer, _blockSize };
InternalStream(arrayInfo);
} else {
_blockSize = 0x100;
_blockBuffer = new uint8_t[_blockSize];
}
_blockPosition = 0;
_inBlock = true;
}
void Serializer::StreamEndBlock()
{
_inBlock = false;
if(_saving) {
InternalStream(_blockPosition);
ArrayInfo<uint8_t> arrayInfo = { _blockBuffer, _blockPosition };
InternalStream(arrayInfo);
}
delete[] _blockBuffer;
_blockBuffer = nullptr;
}
void Serializer::Save(ostream& file)
{
file.write((char*)&_position, sizeof(_position));
file.write((char*)_stream, _position);
}
void Serializer::WriteEmptyBlock(ostream* file)
{
int blockSize = 0;
file->write((char*)&blockSize, sizeof(blockSize));
}
void Serializer::SkipBlock(istream* file)
{
int blockSize = 0;
file->read((char*)&blockSize, sizeof(blockSize));
file->seekg(blockSize, std::ios::cur);
}
void Serializer::Stream(ISerializable &obj)
{
//StreamStartBlock();
obj.Serialize(*this);
//StreamEndBlock();
}
void Serializer::Stream(ISerializable *obj)
{
//StreamStartBlock();
obj->Serialize(*this);
//StreamEndBlock();
}

219
Utilities/Serializer.h Normal file
View file

@ -0,0 +1,219 @@
#pragma once
#include "stdafx.h"
class Serializer;
class ISerializable;
template<typename T>
struct ArrayInfo
{
T* Array;
uint32_t ElementCount;
};
template<typename T>
struct VectorInfo
{
vector<T>* Vector;
};
template<typename T>
struct ValueInfo
{
T* Value;
T DefaultValue;
};
template<typename T>
struct EmptyInfo
{
T Empty;
};
class Serializer
{
private:
uint8_t* _stream = nullptr;
uint32_t _position = 0;
uint32_t _streamSize = 0;
uint32_t _version = 0;
bool _inBlock = false;
uint8_t* _blockBuffer = nullptr;
uint32_t _blockSize = 0;
uint32_t _blockPosition = 0;
bool _saving = false;
private:
void EnsureCapacity(uint32_t typeSize);
template<typename T> void StreamElement(T &value, T defaultValue = T());
template<typename T> void InternalStream(EmptyInfo<T> &info);
template<typename T> void InternalStream(ArrayInfo<T> &info);
template<typename T> void InternalStream(VectorInfo<T> &info);
template<typename T> void InternalStream(ValueInfo<T> &info);
template<typename T> void InternalStream(T &value);
void RecursiveStream();
template<typename T, typename... T2> void RecursiveStream(T &value, T2&... args);
void StreamStartBlock();
void StreamEndBlock();
public:
Serializer(uint32_t version);
Serializer(istream &file, uint32_t version);
~Serializer();
uint32_t GetVersion() { return _version; }
bool IsSaving() { return _saving; }
template<typename... T> void Stream(T&... args);
template<typename T> void StreamArray(T *array, uint32_t size);
template<typename T> void StreamVector(vector<T> &list);
void Save(ostream &file);
void Stream(ISerializable &obj);
void Stream(ISerializable *obj);
void WriteEmptyBlock(ostream* file);
void SkipBlock(istream* file);
};
template<typename T>
void Serializer::StreamElement(T &value, T defaultValue)
{
if(_saving) {
uint8_t* bytes = (uint8_t*)&value;
int typeSize = sizeof(T);
EnsureCapacity(typeSize);
for(int i = 0; i < typeSize; i++) {
if(_inBlock) {
_blockBuffer[_blockPosition++] = bytes[i];
} else {
_stream[_position++] = bytes[i];
}
}
} else {
if(_inBlock) {
if(_blockPosition + sizeof(T) <= _blockSize) {
memcpy(&value, _blockBuffer + _blockPosition, sizeof(T));
_blockPosition += sizeof(T);
} else {
value = defaultValue;
_blockPosition = _blockSize;
}
} else {
if(_position + sizeof(T) <= _streamSize) {
memcpy(&value, _stream + _position, sizeof(T));
_position += sizeof(T);
} else {
value = defaultValue;
_position = _streamSize;
}
}
}
}
template<typename T>
void Serializer::InternalStream(EmptyInfo<T> &info)
{
if(_inBlock) {
_blockPosition += sizeof(T);
} else {
_position += sizeof(T);
}
}
template<typename T>
void Serializer::InternalStream(ArrayInfo<T> &info)
{
T* pointer = info.Array;
uint32_t count = info.ElementCount;
StreamElement<uint32_t>(count);
if(!_saving) {
//Reset array to 0 before loading from file
memset(info.Array, 0, sizeof(T) * info.ElementCount);
}
//Load the number of elements requested, or the maximum possible (based on what is present in the save state)
for(uint32_t i = 0; i < info.ElementCount && i < count; i++) {
StreamElement<T>(*pointer);
pointer++;
}
}
template<typename T>
void Serializer::InternalStream(VectorInfo<T> &info)
{
vector<T> *vector = info.Vector;
uint32_t count = (uint32_t)vector->size();
StreamElement<uint32_t>(count);
if(!_saving) {
if(count > 0xFFFFFF) {
throw std::runtime_error("Invalid save state");
}
vector->resize(count);
memset(vector->data(), 0, sizeof(T)*count);
}
//Load the number of elements requested
T* pointer = vector->data();
for(uint32_t i = 0; i < count; i++) {
StreamElement<T>(*pointer);
pointer++;
}
}
template<typename T>
void Serializer::InternalStream(ValueInfo<T> &info)
{
StreamElement<T>(*info.Value, info.DefaultValue);
}
template<typename T>
void Serializer::InternalStream(T &value)
{
StreamElement<T>(value);
}
template<typename T, typename... T2>
void Serializer::RecursiveStream(T &value, T2&... args)
{
InternalStream(value);
RecursiveStream(args...);
}
template<typename... T>
void Serializer::Stream(T&... args)
{
//StreamStartBlock();
RecursiveStream(args...);
//StreamEndBlock();
}
template<typename T>
void Serializer::StreamArray(T *array, uint32_t size)
{
ArrayInfo<T> info;
info.Array = array;
info.ElementCount = size;
InternalStream(info);
}
template<typename T>
void Serializer::StreamVector(vector<T> &list)
{
VectorInfo<T> info;
info.Vector = &list;
InternalStream(info);
}

View file

@ -413,6 +413,7 @@
<ClInclude Include="HQX\common.h" />
<ClInclude Include="HQX\hqx.h" />
<ClInclude Include="IpsPatcher.h" />
<ClInclude Include="ISerializable.h" />
<ClInclude Include="KreedSaiEagle\SaiEagle.h" />
<ClInclude Include="LowPassFilter.h" />
<ClInclude Include="md5.h" />
@ -426,6 +427,7 @@
<ClInclude Include="Scale2x\scale2x.h" />
<ClInclude Include="Scale2x\scale3x.h" />
<ClInclude Include="Scale2x\scalebit.h" />
<ClInclude Include="Serializer.h" />
<ClInclude Include="sha1.h" />
<ClInclude Include="snes_ntsc.h" />
<ClInclude Include="snes_ntsc_config.h" />
@ -584,6 +586,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Libretro|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='PGO Optimize|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="Serializer.cpp" />
<ClCompile Include="sha1.cpp" />
<ClCompile Include="SimpleLock.cpp" />
<ClCompile Include="snes_ntsc.cpp" />

View file

@ -1,117 +1,98 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="xBRZ">
<UniqueIdentifier>{34df7dd9-5f1b-4aec-9212-1b70f1fada59}</UniqueIdentifier>
</Filter>
<Filter Include="HQX">
<UniqueIdentifier>{c29925fd-7698-4db8-a328-73ef7f8993a9}</UniqueIdentifier>
</Filter>
<Filter Include="Scale2x">
<UniqueIdentifier>{87329bd1-28ac-4ced-a4c2-b51777018d16}</UniqueIdentifier>
</Filter>
<Filter Include="KreedSaiEagle">
<UniqueIdentifier>{8e159744-fb91-4e16-aa82-8d8703ba2762}</UniqueIdentifier>
</Filter>
<Filter Include="Avi">
<UniqueIdentifier>{8b0e23bf-1bd9-4cc1-8046-784fd01e8688}</UniqueIdentifier>
</Filter>
<Filter Include="Patches">
<UniqueIdentifier>{6d519bc1-7c40-448a-95d2-9ad94cd20644}</UniqueIdentifier>
</Filter>
<Filter Include="Hashes">
<UniqueIdentifier>{7b254092-7a93-4010-b203-ec6d48276d11}</UniqueIdentifier>
</Filter>
<Filter Include="Video">
<UniqueIdentifier>{954e8e7b-1ee6-4ab4-a197-3154ce63061e}</UniqueIdentifier>
</Filter>
<Filter Include="Video\xBRZ">
<UniqueIdentifier>{34df7dd9-5f1b-4aec-9212-1b70f1fada59}</UniqueIdentifier>
</Filter>
<Filter Include="Video\Scale2x">
<UniqueIdentifier>{87329bd1-28ac-4ced-a4c2-b51777018d16}</UniqueIdentifier>
</Filter>
<Filter Include="Video\KreedSaiEagle">
<UniqueIdentifier>{8e159744-fb91-4e16-aa82-8d8703ba2762}</UniqueIdentifier>
</Filter>
<Filter Include="Video\HQX">
<UniqueIdentifier>{c29925fd-7698-4db8-a328-73ef7f8993a9}</UniqueIdentifier>
</Filter>
<Filter Include="Archives">
<UniqueIdentifier>{99c6afb9-49a2-4609-b3cd-3d26eb16d8a5}</UniqueIdentifier>
</Filter>
<Filter Include="Audio">
<UniqueIdentifier>{cde7b53f-be5e-4201-8194-0352d00216aa}</UniqueIdentifier>
</Filter>
<Filter Include="Misc">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="targetver.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="Timer.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="FolderUtilities.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="Socket.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="SimpleLock.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="UPnPPortMapper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CRC32.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="miniz.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="UTF8Util.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PNGHelper.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="AutoResetEvent.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="md5.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ZipWriter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ZipReader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LowPassFilter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="blip_buf.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="xBRZ\config.h">
<Filter>xBRZ</Filter>
<Filter>Video\xBRZ</Filter>
</ClInclude>
<ClInclude Include="xBRZ\xbrz.h">
<Filter>xBRZ</Filter>
<Filter>Video\xBRZ</Filter>
</ClInclude>
<ClInclude Include="HQX\hqx.h">
<Filter>HQX</Filter>
<Filter>Video\HQX</Filter>
</ClInclude>
<ClInclude Include="HQX\common.h">
<Filter>HQX</Filter>
<Filter>Video\HQX</Filter>
</ClInclude>
<ClInclude Include="Scale2x\scale2x.h">
<Filter>Scale2x</Filter>
<Filter>Video\Scale2x</Filter>
</ClInclude>
<ClInclude Include="Scale2x\scale3x.h">
<Filter>Scale2x</Filter>
<Filter>Video\Scale2x</Filter>
</ClInclude>
<ClInclude Include="Scale2x\scalebit.h">
<Filter>Scale2x</Filter>
<Filter>Video\Scale2x</Filter>
</ClInclude>
<ClInclude Include="KreedSaiEagle\SaiEagle.h">
<Filter>KreedSaiEagle</Filter>
</ClInclude>
<ClInclude Include="SZReader.h">
<Filter>Header Files</Filter>
<Filter>Video\KreedSaiEagle</Filter>
</ClInclude>
<ClInclude Include="PlatformUtilities.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="HexUtilities.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="ZmbvCodec.h">
<Filter>Avi</Filter>
@ -129,7 +110,7 @@
<Filter>Avi</Filter>
</ClInclude>
<ClInclude Include="StringUtilities.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="IpsPatcher.h">
<Filter>Patches</Filter>
@ -140,130 +121,106 @@
<ClInclude Include="BpsPatcher.h">
<Filter>Patches</Filter>
</ClInclude>
<ClInclude Include="orfanidis_eq.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="sha1.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ArchiveReader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="stb_vorbis.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Base64.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="VirtualFile.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="FastString.h">
<Filter>Header Files</Filter>
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="snes_ntsc.h">
<Filter>Header Files</Filter>
<ClInclude Include="CRC32.h">
<Filter>Hashes</Filter>
</ClInclude>
<ClInclude Include="snes_ntsc_config.h">
<Filter>Header Files</Filter>
<ClInclude Include="sha1.h">
<Filter>Hashes</Filter>
</ClInclude>
<ClInclude Include="md5.h">
<Filter>Hashes</Filter>
</ClInclude>
<ClInclude Include="snes_ntsc_impl.h">
<Filter>Header Files</Filter>
<Filter>Video</Filter>
</ClInclude>
<ClInclude Include="snes_ntsc.h">
<Filter>Video</Filter>
</ClInclude>
<ClInclude Include="snes_ntsc_config.h">
<Filter>Video</Filter>
</ClInclude>
<ClInclude Include="PNGHelper.h">
<Filter>Video</Filter>
</ClInclude>
<ClInclude Include="ArchiveReader.h">
<Filter>Archives</Filter>
</ClInclude>
<ClInclude Include="ZipReader.h">
<Filter>Archives</Filter>
</ClInclude>
<ClInclude Include="ZipWriter.h">
<Filter>Archives</Filter>
</ClInclude>
<ClInclude Include="SZReader.h">
<Filter>Archives</Filter>
</ClInclude>
<ClInclude Include="miniz.h">
<Filter>Archives</Filter>
</ClInclude>
<ClInclude Include="stb_vorbis.h">
<Filter>Audio</Filter>
</ClInclude>
<ClInclude Include="orfanidis_eq.h">
<Filter>Audio</Filter>
</ClInclude>
<ClInclude Include="LowPassFilter.h">
<Filter>Audio</Filter>
</ClInclude>
<ClInclude Include="Equalizer.h">
<Filter>Header Files</Filter>
<Filter>Audio</Filter>
</ClInclude>
<ClInclude Include="blip_buf.h">
<Filter>Audio</Filter>
</ClInclude>
<ClInclude Include="Serializer.h">
<Filter>Misc</Filter>
</ClInclude>
<ClInclude Include="ISerializable.h">
<Filter>Misc</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FolderUtilities.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="miniz.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SimpleLock.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Socket.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UPnPPortMapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UTF8Util.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Timer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CRC32.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PNGHelper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="AutoResetEvent.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="md5.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ZipWriter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ZipReader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="blip_buf.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="xBRZ\xbrz.cpp">
<Filter>xBRZ</Filter>
<Filter>Video\xBRZ</Filter>
</ClCompile>
<ClCompile Include="HQX\hq2x.cpp">
<Filter>HQX</Filter>
<Filter>Video\HQX</Filter>
</ClCompile>
<ClCompile Include="HQX\hq3x.cpp">
<Filter>HQX</Filter>
<Filter>Video\HQX</Filter>
</ClCompile>
<ClCompile Include="HQX\hq4x.cpp">
<Filter>HQX</Filter>
<Filter>Video\HQX</Filter>
</ClCompile>
<ClCompile Include="HQX\init.cpp">
<Filter>HQX</Filter>
<Filter>Video\HQX</Filter>
</ClCompile>
<ClCompile Include="Scale2x\scale2x.cpp">
<Filter>Scale2x</Filter>
<Filter>Video\Scale2x</Filter>
</ClCompile>
<ClCompile Include="Scale2x\scale3x.cpp">
<Filter>Scale2x</Filter>
<Filter>Video\Scale2x</Filter>
</ClCompile>
<ClCompile Include="Scale2x\scalebit.cpp">
<Filter>Scale2x</Filter>
<Filter>Video\Scale2x</Filter>
</ClCompile>
<ClCompile Include="KreedSaiEagle\SuperEagle.cpp">
<Filter>KreedSaiEagle</Filter>
<Filter>Video\KreedSaiEagle</Filter>
</ClCompile>
<ClCompile Include="KreedSaiEagle\Super2xSai.cpp">
<Filter>KreedSaiEagle</Filter>
<Filter>Video\KreedSaiEagle</Filter>
</ClCompile>
<ClCompile Include="KreedSaiEagle\2xSai.cpp">
<Filter>KreedSaiEagle</Filter>
</ClCompile>
<ClCompile Include="SZReader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ArchiveReader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PlatformUtilities.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HexUtilities.cpp">
<Filter>Source Files</Filter>
<Filter>Video\KreedSaiEagle</Filter>
</ClCompile>
<ClCompile Include="ZmbvCodec.cpp">
<Filter>Avi</Filter>
@ -283,20 +240,80 @@
<ClCompile Include="BpsPatcher.cpp">
<Filter>Patches</Filter>
</ClCompile>
<ClCompile Include="md5.cpp">
<Filter>Hashes</Filter>
</ClCompile>
<ClCompile Include="CRC32.cpp">
<Filter>Hashes</Filter>
</ClCompile>
<ClCompile Include="sha1.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="stb_vorbis.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="VirtualFile.cpp">
<Filter>Source Files</Filter>
<Filter>Hashes</Filter>
</ClCompile>
<ClCompile Include="snes_ntsc.cpp">
<Filter>Source Files</Filter>
<Filter>Video</Filter>
</ClCompile>
<ClCompile Include="PNGHelper.cpp">
<Filter>Video</Filter>
</ClCompile>
<ClCompile Include="ArchiveReader.cpp">
<Filter>Archives</Filter>
</ClCompile>
<ClCompile Include="ZipReader.cpp">
<Filter>Archives</Filter>
</ClCompile>
<ClCompile Include="ZipWriter.cpp">
<Filter>Archives</Filter>
</ClCompile>
<ClCompile Include="SZReader.cpp">
<Filter>Archives</Filter>
</ClCompile>
<ClCompile Include="miniz.cpp">
<Filter>Archives</Filter>
</ClCompile>
<ClCompile Include="blip_buf.cpp">
<Filter>Audio</Filter>
</ClCompile>
<ClCompile Include="Equalizer.cpp">
<Filter>Source Files</Filter>
<Filter>Audio</Filter>
</ClCompile>
<ClCompile Include="stb_vorbis.cpp">
<Filter>Audio</Filter>
</ClCompile>
<ClCompile Include="AutoResetEvent.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="FolderUtilities.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="HexUtilities.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="PlatformUtilities.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="SimpleLock.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="Socket.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="stdafx.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="Timer.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="UPnPPortMapper.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="UTF8Util.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="VirtualFile.cpp">
<Filter>Misc</Filter>
</ClCompile>
<ClCompile Include="Serializer.cpp">
<Filter>Misc</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -303,6 +303,16 @@ std::string SHA1::GetHash(vector<uint8_t> &data)
return checksum.final();
}
std::string SHA1::GetHash(uint8_t* data, size_t size)
{
std::stringstream ss;
ss.write((char*)data, size);
SHA1 checksum;
checksum.update(ss);
return checksum.final();
}
std::string SHA1::GetHash(std::istream &stream)
{
SHA1 checksum;

View file

@ -33,6 +33,7 @@ public:
static std::string GetHash(const std::string &filename);
static std::string GetHash(std::istream &stream);
static std::string GetHash(vector<uint8_t> &data);
static std::string GetHash(uint8_t* data, size_t size);
private:
uint32_t digest[5];

View file

@ -14,6 +14,8 @@ using std::shared_ptr;
using std::unique_ptr;
using utf8::ifstream;
using utf8::ofstream;
using std::ostream;
using std::istream;
using std::string;
using std::vector;
using std::atomic;