mirror of
https://github.com/SourMesen/Mesen.git
synced 2024-06-02 11:28:17 -04:00
Save states: Fixed incorrect timestamp in menu
Also changed save states to contain the raw framebuffer rather than a PNG, which allows displaying the screenshot in the emulator window when loading a state while paused
This commit is contained in:
parent
d6728ee306
commit
06ccf0bdb0
|
@ -2,6 +2,8 @@
|
|||
#include "../Utilities/FolderUtilities.h"
|
||||
#include "../Utilities/ZipWriter.h"
|
||||
#include "../Utilities/ZipReader.h"
|
||||
#include "../Utilities/PNGHelper.h"
|
||||
#include "../Utilities/miniz.h"
|
||||
#include "SaveStateManager.h"
|
||||
#include "MessageManager.h"
|
||||
#include "Console.h"
|
||||
|
@ -10,6 +12,8 @@
|
|||
#include "Debugger.h"
|
||||
#include "MovieManager.h"
|
||||
#include "RomData.h"
|
||||
#include "DefaultVideoFilter.h"
|
||||
#include "PPU.h"
|
||||
|
||||
SaveStateManager::SaveStateManager(shared_ptr<Console> console)
|
||||
{
|
||||
|
@ -24,18 +28,6 @@ string SaveStateManager::GetStateFilepath(int stateIndex)
|
|||
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::SelectSaveSlot(int slotIndex)
|
||||
{
|
||||
_lastIndex = slotIndex;
|
||||
|
@ -79,11 +71,7 @@ void SaveStateManager::GetSaveStateHeader(ostream &stream)
|
|||
string sha1Hash = romInfo.Hash.Sha1;
|
||||
stream.write(sha1Hash.c_str(), sha1Hash.size());
|
||||
|
||||
std::stringstream screenshotStream;
|
||||
_console->GetVideoDecoder()->TakeScreenshot(screenshotStream, true);
|
||||
uint32_t screenshotLength = (uint32_t)screenshotStream.tellp();
|
||||
stream.write((char*)&screenshotLength, sizeof(uint32_t));
|
||||
stream.write(screenshotStream.str().c_str(), screenshotLength);
|
||||
SaveScreenshotData(stream);
|
||||
|
||||
string romName = romInfo.RomName;
|
||||
uint32_t nameLength = (uint32_t)romName.size();
|
||||
|
@ -127,6 +115,33 @@ void SaveStateManager::SaveState(int stateIndex, bool displayMessage)
|
|||
}
|
||||
}
|
||||
|
||||
void SaveStateManager::SaveScreenshotData(ostream &stream)
|
||||
{
|
||||
unsigned long compressedSize = compressBound(PPU::PixelCount * 2);
|
||||
vector<uint8_t> compressedData(compressedSize, 0);
|
||||
compress2(compressedData.data(), &compressedSize, (const unsigned char*)_console->GetPpu()->GetScreenBuffer(true), PPU::PixelCount * 2, MZ_DEFAULT_LEVEL);
|
||||
|
||||
uint32_t screenshotLength = (uint32_t)compressedSize;
|
||||
stream.write((char*)&screenshotLength, sizeof(uint32_t));
|
||||
stream.write((char*)compressedData.data(), screenshotLength);
|
||||
}
|
||||
|
||||
bool SaveStateManager::GetScreenshotData(vector<uint8_t>& out, istream& stream)
|
||||
{
|
||||
uint32_t screenshotLength = 0;
|
||||
stream.read((char*)&screenshotLength, sizeof(uint32_t));
|
||||
|
||||
vector<uint8_t> compressedData(screenshotLength, 0);
|
||||
stream.read((char*)compressedData.data(), screenshotLength);
|
||||
|
||||
out = vector<uint8_t>(PPU::PixelCount * 2, 0);
|
||||
unsigned long decompSize = PPU::PixelCount * 2;
|
||||
if(uncompress(out.data(), &decompSize, compressedData.data(), (unsigned long)compressedData.size()) == MZ_OK) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired)
|
||||
{
|
||||
char header[3];
|
||||
|
@ -158,10 +173,10 @@ bool SaveStateManager::LoadState(istream &stream, bool hashCheckRequired)
|
|||
stream.read(hash, 40);
|
||||
|
||||
if(fileFormatVersion >= 13) {
|
||||
//Skip screenshot data
|
||||
uint32_t screenshotLength = 0;
|
||||
stream.read((char*)&screenshotLength, sizeof(uint32_t));
|
||||
stream.seekg(screenshotLength, std::ios::cur);
|
||||
vector<uint8_t> frameData;
|
||||
if(GetScreenshotData(frameData, stream)) {
|
||||
_console->GetVideoDecoder()->UpdateFrameSync(frameData.data());
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t nameLength = 0;
|
||||
|
@ -312,16 +327,20 @@ int32_t SaveStateManager::GetSaveStatePreview(string saveStatePath, uint8_t* png
|
|||
//Skip some header fields
|
||||
stream.seekg(43, ios::cur);
|
||||
|
||||
uint32_t screenshotLength = 0;
|
||||
stream.read((char*)&screenshotLength, sizeof(screenshotLength));
|
||||
vector<uint8_t> frameData;
|
||||
if(GetScreenshotData(frameData, stream)) {
|
||||
DefaultVideoFilter filter(_console);
|
||||
FrameInfo frameInfo = filter.GetFrameInfo();
|
||||
filter.SendFrame((uint16_t*)frameData.data(), 0);
|
||||
|
||||
if(screenshotLength > 0) {
|
||||
stream.read((char*)pngData, screenshotLength);
|
||||
return screenshotLength;
|
||||
std::stringstream pngStream;
|
||||
PNGHelper::WritePNG(pngStream, filter.GetOutputBuffer(), frameInfo.Width, frameInfo.Height);
|
||||
|
||||
string data = pngStream.str();
|
||||
memcpy(pngData, data.c_str(), data.size());
|
||||
|
||||
return frameData.size();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
|
@ -12,14 +12,14 @@ private:
|
|||
shared_ptr<Console> _console;
|
||||
|
||||
string GetStateFilepath(int stateIndex);
|
||||
void SaveScreenshotData(ostream& stream);
|
||||
bool GetScreenshotData(vector<uint8_t>& out, istream& stream);
|
||||
|
||||
public:
|
||||
static constexpr uint32_t FileFormatVersion = 13;
|
||||
|
||||
SaveStateManager(shared_ptr<Console> console);
|
||||
|
||||
uint64_t GetStateInfo(int stateIndex);
|
||||
|
||||
void SaveState();
|
||||
bool LoadState();
|
||||
|
||||
|
|
|
@ -116,8 +116,8 @@ namespace Mesen.GUI.Controls
|
|||
InteropEmu.LoadRecentGame(_recentGame.FileName, ConfigManager.Config.PreferenceInfo.GameSelectionScreenResetGame);
|
||||
} else {
|
||||
switch(this.Mode) {
|
||||
case GameScreenMode.LoadState: InteropEmu.LoadStateFile(_recentGame.FileName); break;
|
||||
case GameScreenMode.SaveState: InteropEmu.SaveStateFile(_recentGame.FileName); break;
|
||||
case GameScreenMode.LoadState: InteropEmu.LoadState(_recentGame.SaveSlot); break;
|
||||
case GameScreenMode.SaveState: InteropEmu.SaveState(_recentGame.SaveSlot); break;
|
||||
}
|
||||
}
|
||||
OnRecentGameLoaded?.Invoke(_recentGame);
|
||||
|
|
|
@ -163,7 +163,7 @@ namespace Mesen.GUI.Controls
|
|||
|
||||
string romName = InteropEmu.GetRomInfo().GetRomName();
|
||||
for(int i = 0; i < 11; i++) {
|
||||
_recentGames.Add(new RecentGameInfo() { FileName = Path.Combine(ConfigManager.SaveStateFolder, romName + "_" + (i + 1) + ".mst"), Name = i == 10 ? ResourceHelper.GetMessage("AutoSave") : ResourceHelper.GetMessage("SlotNumber", i+1) });
|
||||
_recentGames.Add(new RecentGameInfo() { FileName = Path.Combine(ConfigManager.SaveStateFolder, romName + "_" + (i + 1) + ".mst"), Name = i == 10 ? ResourceHelper.GetMessage("AutoSave") : ResourceHelper.GetMessage("SlotNumber", i+1), SaveSlot = (uint)i+1 });
|
||||
}
|
||||
_recentGames.Add(new RecentGameInfo() { FileName = Path.Combine(ConfigManager.RecentGamesFolder, romName + ".rgd"), Name = ResourceHelper.GetMessage("LastSession") });
|
||||
}
|
||||
|
@ -245,7 +245,9 @@ namespace Mesen.GUI.Controls
|
|||
|
||||
private void RecentGameLoaded(RecentGameInfo gameInfo)
|
||||
{
|
||||
OnRecentGameLoaded?.Invoke(gameInfo);
|
||||
if(this.Mode == GameScreenMode.RecentGames) {
|
||||
OnRecentGameLoaded?.Invoke(gameInfo);
|
||||
}
|
||||
if(this._needResume) {
|
||||
InteropEmu.Resume();
|
||||
}
|
||||
|
@ -327,6 +329,7 @@ namespace Mesen.GUI.Controls
|
|||
{
|
||||
public string FileName { get; set; }
|
||||
public string Name { get; set; }
|
||||
public uint SaveSlot { get; set; }
|
||||
public ResourcePath RomPath { get; set; }
|
||||
}
|
||||
|
||||
|
|
|
@ -20,15 +20,15 @@ namespace Mesen.GUI.Forms
|
|||
this.BeginInvoke((MethodInvoker)(() => this.UpdateStateMenu(menu, forSave)));
|
||||
} else {
|
||||
for(uint i = 1; i <= frmMain.NumberOfSaveSlots + (forSave ? 0 : 1); i++) {
|
||||
Int64 fileTime = InteropEmu.GetStateInfo(i);
|
||||
string statePath = Path.Combine(ConfigManager.SaveStateFolder, InteropEmu.GetRomInfo().GetRomName() + "_" + i + ".mst");
|
||||
string label;
|
||||
bool isAutoSaveSlot = i == NumberOfSaveSlots + 1;
|
||||
string slotName = isAutoSaveSlot ? "Auto" : i.ToString();
|
||||
|
||||
if(fileTime == 0) {
|
||||
if(!File.Exists(statePath)) {
|
||||
label = slotName + ". " + ResourceHelper.GetMessage("EmptyState");
|
||||
} else {
|
||||
DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(fileTime).ToLocalTime();
|
||||
DateTime dateTime = new FileInfo(statePath).LastWriteTime;
|
||||
label = slotName + ". " + dateTime.ToShortDateString() + " " + dateTime.ToShortTimeString();
|
||||
}
|
||||
|
||||
|
|
|
@ -147,12 +147,12 @@ namespace Mesen.GUI
|
|||
[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);
|
||||
|
||||
[DllImport(DLLPath, EntryPoint = "GetSaveStatePreview")] private static extern Int32 GetSaveStatePreviewWrapper([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(UTF8Marshaler))]string saveStatePath, [Out]byte[] imgData);
|
||||
public static Image GetSaveStatePreview(string saveStatePath)
|
||||
{
|
||||
if(File.Exists(saveStatePath)) {
|
||||
byte[] buffer = new byte[new FileInfo(saveStatePath).Length];
|
||||
byte[] buffer = new byte[128000];
|
||||
Int32 size = InteropEmu.GetSaveStatePreviewWrapper(saveStatePath, buffer);
|
||||
if(size > 0) {
|
||||
Array.Resize(ref buffer, size);
|
||||
|
|
|
@ -545,7 +545,6 @@ namespace InteropEmu {
|
|||
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 int32_t __stdcall GetSaveStatePreview(char* saveStatePath, uint8_t* pngData) { return _console->GetSaveStateManager()->GetSaveStatePreview(saveStatePath, pngData); }
|
||||
|
||||
|
|
|
@ -213,12 +213,6 @@ string FolderUtilities::CombinePath(string folder, string filename)
|
|||
return folder + filename;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t FolderUtilities::GetFileModificationTime(string filepath)
|
||||
{
|
||||
std::error_code errorCode;
|
||||
return fs::last_write_time(fs::u8path(filepath), errorCode).time_since_epoch() / std::chrono::seconds(1);
|
||||
}
|
||||
#else
|
||||
|
||||
//Libretro: Avoid using filesystem API.
|
||||
|
@ -267,8 +261,4 @@ string FolderUtilities::CombinePath(string folder, string filename)
|
|||
return folder + filename;
|
||||
}
|
||||
|
||||
int64_t FolderUtilities::GetFileModificationTime(string filepath)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
|
@ -36,7 +36,5 @@ public:
|
|||
|
||||
static void CreateFolder(string folder);
|
||||
|
||||
static int64_t GetFileModificationTime(string filepath);
|
||||
|
||||
static string CombinePath(string folder, string filename);
|
||||
};
|
Loading…
Reference in a new issue