Files
melonDS/src/frontend/qt_sdl/SaveManager.cpp
Jesse Talavera-Greenberg ee55677086 Assorted portability enhancements (#1800)
* Introduce some Platform calls for managing dynamic libraries

* Add Platform::WriteFATSectors

* Introduce some Platform calls for managing dynamic libraries

* Add Platform::WriteFATSectors

* Change includes of "../types.h" to "types.h"

- Makes it easier to directly include these headers in downstream projects

* Change an include of "../Wifi.h" to "Wifi.h"

* Allow CommonFuncs.cpp to compile on Android

* Tidy up some logging calls

- Use Platform::Log in LAN_Socket.cpp
- Soften some warnings to Debug logs (since they don't necessarily represent problems)

* Add Platform::EnterGBAMode

- Gracefully stop the emulator if trying to enter GBA mode

* Soften some logs that most players won't care about

* Soften some more logs

* Introduce Platform wrappers for file operations

* Fix pointer spacing

* Fix more style nits

* Log the errno when ftruncate fails

* Fix FileSeek offset argument

- With an s32 offset, we couldn't access files larger than 2GB

* Revise Platform::StopEmu to address feedback

- Remove Platform::EnterGBAMode in favor of adding a reason to Platform::StopEmu
- Also rename Platform::StopEmu to Platform::SignalStop
- Add an optional argument to NDS::Stop
- Use the new argument everywhere that the console stops itself

* Rename FileGetString to FileReadLine

- It conveys the meaning better

* Rename FileSeekOrigin::Set to Start

- It conveys the meaning better

* Change definition of FileGetString to FileReadLine

- Oops, almost forgot it

* Rename FlushFile to FileFlush

- To remain consistent with the other File functions

* Add a FileType usage

* Fix line break in FileSeekOrigin

* Document Platform::DeInit

* Clarify that StopReason::Unknown doesn't always mean an error

* Move and document FileType::HostFile

* Remove Platform::OpenDataFile

- Nothing currently uses it

* Refactor Platform::OpenFile and Platform::OpenLocalFile to accept a FileMode enum instead of a string

- The enum is converted to fopen flags under the hood
- The file type is used to decide whether to add the "b" flag
- Some helper functions are exposed for the benefit of consistent behavior among frontends
- Equivalent behavior is maintained

* Fix a tab that should be spaces

* Use Windows' 64-bit implementations of fseek/ftell

* Move Platform::IsBinaryFile to Platform.cpp

- It could vary by frontend

* Remove an unused FileType

* Rename an enum constant

* Document various Platform items

* Use Platform::DynamicLibrary to load libandroid

- And clean it up at the end

* Fix a typo

* Pass the correct filetype to FATStorage

- Since it can be used for DSI NAND images or for SD cards

* Remove Platform::FileType
2023-08-18 22:50:57 +02:00

196 lines
4.5 KiB
C++

/*
Copyright 2016-2022 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "SaveManager.h"
#include "Platform.h"
using namespace Platform;
SaveManager::SaveManager(const std::string& path) : QThread()
{
SecondaryBuffer = nullptr;
SecondaryBufferLength = 0;
SecondaryBufferLock = new QMutex();
Running = false;
Path = path;
Buffer = nullptr;
Length = 0;
FlushRequested = false;
FlushVersion = 0;
PreviousFlushVersion = 0;
TimeAtLastFlushRequest = 0;
if (!path.empty())
{
Running = true;
start();
}
}
SaveManager::~SaveManager()
{
if (Running)
{
Running = false;
wait();
FlushSecondaryBuffer();
}
if (SecondaryBuffer) delete[] SecondaryBuffer;
delete SecondaryBufferLock;
if (Buffer) delete[] Buffer;
}
std::string SaveManager::GetPath()
{
return Path;
}
void SaveManager::SetPath(const std::string& path, bool reload)
{
Path = path;
if (reload)
{
FileHandle* f = Platform::OpenFile(Path, FileMode::Read);
if (f)
{
FileRead(Buffer, 1, Length, f);
CloseFile(f);
}
}
else
FlushRequested = true;
}
void SaveManager::RequestFlush(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen)
{
if (Length != savelen)
{
if (Buffer) delete[] Buffer;
Length = savelen;
Buffer = new u8[Length];
memcpy(Buffer, savedata, Length);
}
else
{
if ((writeoffset+writelen) > savelen)
{
u32 len = savelen - writeoffset;
memcpy(&Buffer[writeoffset], &savedata[writeoffset], len);
len = writelen - len;
if (len > savelen) len = savelen;
memcpy(&Buffer[0], &savedata[0], len);
}
else
{
memcpy(&Buffer[writeoffset], &savedata[writeoffset], writelen);
}
}
FlushRequested = true;
}
void SaveManager::CheckFlush()
{
if (!FlushRequested) return;
SecondaryBufferLock->lock();
Log(LogLevel::Info, "SaveManager: Flush requested\n");
if (SecondaryBufferLength != Length)
{
if (SecondaryBuffer) delete[] SecondaryBuffer;
SecondaryBufferLength = Length;
SecondaryBuffer = new u8[SecondaryBufferLength];
}
memcpy(SecondaryBuffer, Buffer, Length);
FlushRequested = false;
FlushVersion++;
TimeAtLastFlushRequest = time(nullptr);
SecondaryBufferLock->unlock();
}
void SaveManager::run()
{
for (;;)
{
QThread::msleep(100);
if (!Running) return;
// We debounce for two seconds after last flush request to ensure that writing has finished.
if (TimeAtLastFlushRequest == 0 || difftime(time(nullptr), TimeAtLastFlushRequest) < 2)
{
continue;
}
FlushSecondaryBuffer();
}
}
void SaveManager::FlushSecondaryBuffer(u8* dst, u32 dstLength)
{
if (!SecondaryBuffer) return;
// When flushing to a file, there's no point in re-writing the exact same data.
if (!dst && !NeedsFlush()) return;
// When flushing to memory, we don't know if dst already has any data so we only check that we CAN flush.
if (dst && dstLength < SecondaryBufferLength) return;
SecondaryBufferLock->lock();
if (dst)
{
memcpy(dst, SecondaryBuffer, SecondaryBufferLength);
}
else
{
FileHandle* f = Platform::OpenFile(Path, FileMode::Write);
if (f)
{
Log(LogLevel::Info, "SaveManager: Written\n");
FileWrite(SecondaryBuffer, SecondaryBufferLength, 1, f);
CloseFile(f);
}
}
PreviousFlushVersion = FlushVersion;
TimeAtLastFlushRequest = 0;
SecondaryBufferLock->unlock();
}
bool SaveManager::NeedsFlush()
{
return FlushVersion != PreviousFlushVersion;
}