Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quicksaving #71

Merged
merged 10 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ set(Impacto_Src
src/renderer/3d/animation.cpp
src/renderer/3d/modelanimator.cpp

src/io/filemeta.cpp
src/io/vfs.cpp
src/io/assetpath.cpp
src/io/memorystream.cpp
Expand Down
13 changes: 7 additions & 6 deletions src/data/savesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ void LoadMemoryNew(LoadProcess load) {
if (Implementation) Implementation->LoadMemoryNew(load);
}

void FlushWorkingSaveEntry(SaveType type, int id) {
if (Implementation) Implementation->FlushWorkingSaveEntry(type, id);
void FlushWorkingSaveEntry(SaveType type, int id, int autoSaveType) {
if (Implementation)
Implementation->FlushWorkingSaveEntry(type, id, autoSaveType);
}

void WriteSaveFile() {
Expand Down Expand Up @@ -172,11 +173,11 @@ void SetCheckpointId(int id) {
if (Implementation) Implementation->SetCheckpointId(id);
}

int GetQuickSaveCount() {
if (Implementation) return Implementation->GetQuickSaveCount();
int GetQuickSaveOpenSlot() {
if (Implementation) return Implementation->GetQuickSaveOpenSlot();
ImpLog(LL_Warning, LC_VMStub,
"%s: save system not implemented, returning 0\n", __func__);
return 0;
"%s: save system not implemented, returning -1\n", __func__);
return -1;
}

Sprite const& GetSaveThumbnail(SaveType type, int id) {
Expand Down
19 changes: 15 additions & 4 deletions src/data/savesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ namespace SaveSystem {

BETTER_ENUM(SaveDataType, int, None, CHLCC, CCLCC, MO6TW)

enum SaveFlagsMode { WriteProtect = 1 };

enum SaveError {
SaveOK = 0,
SaveNotFound = 2,
Expand Down Expand Up @@ -72,7 +74,8 @@ class SaveSystemBase {
virtual void SaveMemory() = 0;
virtual void LoadEntry(SaveType type, int id) = 0;
virtual void LoadMemoryNew(LoadProcess){};
virtual void FlushWorkingSaveEntry(SaveType type, int id) = 0;
virtual void FlushWorkingSaveEntry(SaveType type, int id,
int autoSaveType) = 0;
virtual void WriteSaveFile() = 0;
virtual uint32_t GetSavePlayTime(SaveType type, int id) = 0;
virtual uint8_t GetSaveFlags(SaveType type, int id) = 0;
Expand All @@ -93,7 +96,15 @@ class SaveSystemBase {
virtual bool GetBgmFlag(int id) = 0;
virtual void SetCheckpointId(int id) = 0;
virtual Sprite const& GetSaveThumbnail(SaveType type, int id) = 0;
int GetQuickSaveCount() { return QuickSaveCount; }
int GetQuickSaveOpenSlot() {
for (int i = 0; i < MaxSaveEntries; i++) {
if (QuickSaveEntries[i]->Status == 0) return i;
}
for (int i = 0; i < MaxSaveEntries; i++) {
if (!(GetSaveFlags(SaveQuick, i) & WriteProtect)) return i;
}
return -1;
}
Sprite const& GetWorkingSaveThumbnail() { return WorkingSaveThumbnail; }

protected:
Expand All @@ -113,7 +124,7 @@ SaveError MountSaveFile();
void SaveMemory();
void LoadEntry(SaveType type, int id);
void LoadMemoryNew(LoadProcess process);
void FlushWorkingSaveEntry(SaveType type, int id);
void FlushWorkingSaveEntry(SaveType type, int id, int autoSaveType = 0);
void WriteSaveFile();
uint32_t GetSavePlayTime(SaveType type, int id);
uint8_t GetSaveFlags(SaveType type, int id);
Expand All @@ -130,7 +141,7 @@ void GetEVStatus(int evId, int* totalVariations, int* viewedVariations);
bool GetEVVariationIsUnlocked(int evId, int variationIdx);
bool GetBgmFlag(int id);
void SetCheckpointId(int id);
int GetQuickSaveCount();
int GetQuickSaveOpenSlot();
Sprite const& GetSaveThumbnail(SaveType type, int id);
Sprite const& GetWorkingSaveThumbnail();

Expand Down
38 changes: 33 additions & 5 deletions src/games/cclcc/savemenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,23 +55,51 @@ void SaveMenu::Show() {
State = Showing;
FadeAnimation.StartIn();
int id = 0;
Impacto::SaveSystem::SaveType saveType =
ScrWork[SW_SAVEMENUMODE] == SaveMenuPageType::QuickLoad
? SaveSystem::SaveType::SaveQuick
: SaveSystem::SaveType::SaveFull;

std::array<int, SaveSystem::MaxSaveEntries> saveEntryIds;
for (int i = 0; i < SaveSystem::MaxSaveEntries; i++) {
saveEntryIds[i] = i;
}
if (saveType == SaveSystem::SaveType::SaveQuick) {
// quick saves are sorted by time and status
std::sort(saveEntryIds.begin(), saveEntryIds.end(),
[saveType](int a, int b) {
int statusA = SaveSystem::GetSaveStatus(saveType, a);
int statusB = SaveSystem::GetSaveStatus(saveType, b);
if (statusA == statusB) {
std::tm ta = SaveSystem::GetSaveDate(saveType, a);
std::tm tb = SaveSystem::GetSaveDate(saveType, b);
std::time_t th = std::mktime(&ta);
std::time_t tl = std::mktime(&tb);
if (th == -1 || tl == -1) {
ImpLog(LL_Error, LC_General,
"Failed to convert time to time_t\n");
return statusA > statusB;
}
return difftime(th, tl) > 0;
}
return statusA > statusB;
});
}

for (int p = 0; p < Pages; ++p) {
MainItems[p] = new Widgets::Group(this);
MainItems[p]->WrapFocus = false;

for (int i = 0; i < RowsPerPage; i++) {
// Start on right col
for (int j = EntriesPerRow - 1; j >= 0; j--) {
Impacto::SaveSystem::SaveType saveType =
ScrWork[SW_SAVEMENUMODE] == SaveMenuPageType::QuickLoad
? SaveSystem::SaveType::SaveQuick
: SaveSystem::SaveType::SaveFull;
glm::vec2 buttonPos =
(j == 0)
? glm::vec2{EntryStartXL, EntryStartYL + (i * EntryYPadding)}
: glm::vec2{EntryStartXR, EntryStartYR + (i * EntryYPadding)};
SaveEntryButton* saveEntryButton = new SaveEntryButton(
id, EntryHighlightedBoxSprite[ScrWork[SW_SAVEMENUMODE]],
saveEntryIds[id], id,
EntryHighlightedBoxSprite[ScrWork[SW_SAVEMENUMODE]],
EntryHighlightedTextSprite[ScrWork[SW_SAVEMENUMODE]], p,
buttonPos, SlotLockedSprite[ScrWork[SW_SAVEMENUMODE]], saveType,
NoDataSprite[ScrWork[SW_SAVEMENUMODE]],
Expand Down
59 changes: 43 additions & 16 deletions src/games/cclcc/savesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

#include <cstdint>
#include <ctime>
#include <filesystem>
#include <system_error>

namespace Impacto {
namespace CCLCC {
Expand All @@ -25,28 +25,49 @@
using namespace Impacto::Profile::Vm;

SaveError SaveSystem::CheckSaveFile() {
std::filesystem::path savePath(SaveFilePath);
if (!std::filesystem::exists(savePath)) {
std::error_code ec;
IoError existsState = Io::PathExists(SaveFilePath);
if (existsState == IoError_NotFound) {
return SaveNotFound;
} else if (existsState == IoError_Fail) {
ImpLog(LL_Error, LC_IO,
"Failed to check if save file exists, error: \"%s\"\n",
ec.message().c_str());
return SaveFailed;
}
if (std::filesystem::file_size(savePath) != SaveFileSize) {
auto saveFileSize = Io::GetFileSize(SaveFilePath);
if (saveFileSize == IoError_Fail) {
ImpLog(LL_Error, LC_IO, "Failed to get save file size, error: \"%s\"\n",
ec.message().c_str());
return SaveFailed;
} else if (saveFileSize != SaveFileSize) {
return SaveCorrupted;
}
if (auto perms = std::filesystem::status(savePath).permissions();
to_underlying(perms) &
(to_underlying(std::filesystem::perms::owner_read) |
to_underlying(std::filesystem::perms::owner_write)) == 0 &&
to_underlying(perms) &
(to_underlying(std::filesystem::perms::group_read) |
to_underlying(std::filesystem::perms::group_write)) == 0) {
auto checkPermsBit = [](Io::FilePermissionsFlags perms,
Io::FilePermissionsFlags flag) {
return to_underlying(perms) & to_underlying(flag);
};

Io::FilePermissionsFlags perms;
IoError permsState = Io::GetFilePermissions(SaveFilePath, perms);
if (permsState == IoError_Fail) {
ImpLog(LL_Error, LC_IO,
"Failed to get save file permissions, error: \"%s\"\n",
ec.message().c_str());
return SaveFailed;
} else if ((!checkPermsBit(perms, Io::FilePermissionsFlags::owner_read) ||
!checkPermsBit(perms, Io::FilePermissionsFlags::owner_write))) {
return SaveWrongUser;
}
return SaveOK;
}

SaveError SaveSystem::CreateSaveFile() {
using CF = Io::PhysicalFileStream::CreateFlagsMode;
Io::Stream* stream;
IoError err = Io::PhysicalFileStream::Create(SaveFilePath, &stream, false);
IoError err = Io::PhysicalFileStream::Create(
SaveFilePath, &stream,
CF::CREATE_IF_NOT_EXISTS | CF::CREATE_DIRS | CF::WRITE);
if (err != IoError_OK) {
return SaveFailed;
}
Expand Down Expand Up @@ -232,20 +253,24 @@
// return 0;
//}

void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id) {
void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id,
int autoSaveType) {
SaveFileEntry* entry = 0;
switch (type) {
case SaveQuick:
entry = (SaveFileEntry*)QuickSaveEntries[QuickSaveCount++];
entry = (SaveFileEntry*)QuickSaveEntries[id];
break;
case SaveFull:
entry = (SaveFileEntry*)FullSaveEntries[id];
break;
}

if (entry != 0) {
if (entry != 0 && !(GetSaveFlags(type, id) & WriteProtect)) {
Renderer->FreeTexture(entry->SaveThumbnail.Sheet.Texture);
*entry = *WorkingSaveEntry;
if (type == SaveQuick) {
entry->SaveType = autoSaveType;
}
time_t rawtime;
time(&rawtime);
entry->SaveDate = *localtime(&rawtime);
Expand All @@ -271,8 +296,10 @@
}

void SaveSystem::WriteSaveFile() {
using CF = Io::PhysicalFileStream::CreateFlagsMode;
Io::Stream* stream;
IoError err = Io::PhysicalFileStream::Create(SaveFilePath, &stream);
IoError err =
Io::PhysicalFileStream::Create(SaveFilePath, &stream, CF::WRITE);
if (err != IoError_OK) {
ImpLog(LL_Error, LC_IO, "Failed to open save file for writing\n");
return;
Expand Down Expand Up @@ -703,5 +730,5 @@
case SaveFull:
return ((SaveFileEntry*)FullSaveEntries[id])->SaveThumbnail;
}
}

Check warning on line 733 in src/games/cclcc/savesystem.cpp

View workflow job for this annotation

GitHub Actions / windows

'Impacto::CCLCC::SaveSystem::GetSaveThumbnail': not all control paths return a value

Check warning on line 733 in src/games/cclcc/savesystem.cpp

View workflow job for this annotation

GitHub Actions / linux

control reaches end of non-void function [-Wreturn-type]
} // namespace CCLCC
Expand Down
2 changes: 1 addition & 1 deletion src/games/cclcc/savesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class SaveSystem : public SaveSystemBase {
void SaveMemory() override;
void LoadEntry(SaveType type, int id) override;
void LoadMemoryNew(LoadProcess load) override;
void FlushWorkingSaveEntry(SaveType type, int id) override;
void FlushWorkingSaveEntry(SaveType type, int id, int autoSaveType) override;
void WriteSaveFile() override;
uint32_t GetSavePlayTime(SaveType type, int id) override;
uint8_t GetSaveFlags(SaveType type, int id) override;
Expand Down
8 changes: 6 additions & 2 deletions src/games/chlcc/savesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@
// return 0;
//}

void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id) {
void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id,
int autoSaveType) {
SaveFileEntry* entry = 0;
switch (type) {
case SaveQuick:
Expand All @@ -144,8 +145,11 @@
}

if (WorkingSaveEntry != 0) {
if (entry != 0) {
if (entry != 0 && !(GetSaveFlags(type, id) & WriteProtect)) {
*entry = *WorkingSaveEntry;
if (type == SaveQuick) {
entry->SaveType = autoSaveType;
}
time_t rawtime;
time(&rawtime);
entry->SaveDate = *localtime(&rawtime);
Expand Down Expand Up @@ -591,5 +595,5 @@
case SaveFull:
return FullSaveEntries[id]->SaveThumbnail;
}
}

Check warning on line 598 in src/games/chlcc/savesystem.cpp

View workflow job for this annotation

GitHub Actions / windows

'Impacto::CHLCC::SaveSystem::GetSaveThumbnail': not all control paths return a value

Check warning on line 598 in src/games/chlcc/savesystem.cpp

View workflow job for this annotation

GitHub Actions / linux

control reaches end of non-void function [-Wreturn-type]
} // namespace CHLCC
Expand Down
2 changes: 1 addition & 1 deletion src/games/chlcc/savesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class SaveSystem : public SaveSystemBase {
SaveError MountSaveFile() override;
void SaveMemory() override;
void LoadEntry(SaveType type, int id) override;
void FlushWorkingSaveEntry(SaveType type, int id) override;
void FlushWorkingSaveEntry(SaveType type, int id, int autoSaveType) override;
void WriteSaveFile() override;
uint32_t GetSavePlayTime(SaveType type, int id) override;
uint8_t GetSaveFlags(SaveType type, int id) override;
Expand Down
9 changes: 7 additions & 2 deletions src/games/mo6tw/savesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,8 @@
// return 0;
//}

void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id) {
void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id,
int autoSaveType) {
SaveFileEntry* entry = 0;
switch (type) {
case SaveQuick:
Expand All @@ -202,7 +203,11 @@
}

if (WorkingSaveEntry != 0) {
if (entry != 0) {
if (entry != 0 && !(GetSaveFlags(type, id) & WriteProtect)) {
Renderer->FreeTexture(entry->SaveThumbnail.Sheet.Texture);
if (type == SaveQuick) {
entry->SaveType = autoSaveType;
}
entry->Status = 1;

std::time_t t = std::time(0);
Expand Down Expand Up @@ -734,5 +739,5 @@
case SaveFull:
return ((SaveFileEntry*)FullSaveEntries[id])->SaveThumbnail;
}
}

Check warning on line 742 in src/games/mo6tw/savesystem.cpp

View workflow job for this annotation

GitHub Actions / windows

'Impacto::MO6TW::SaveSystem::GetSaveThumbnail': not all control paths return a value

Check warning on line 742 in src/games/mo6tw/savesystem.cpp

View workflow job for this annotation

GitHub Actions / linux

control reaches end of non-void function [-Wreturn-type]
} // namespace MO6TW
Expand Down
2 changes: 1 addition & 1 deletion src/games/mo6tw/savesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class SaveSystem : public SaveSystemBase {
SaveError MountSaveFile() override;
void SaveMemory() override;
void LoadEntry(SaveType type, int id) override;
void FlushWorkingSaveEntry(SaveType type, int id) override;
void FlushWorkingSaveEntry(SaveType type, int id, int autoSaveType) override;
void WriteSaveFile() override;
uint32_t GetSavePlayTime(SaveType type, int id) override;
uint8_t GetSaveFlags(SaveType type, int id) override;
Expand Down
59 changes: 59 additions & 0 deletions src/io/filemeta.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include "filemeta.h"
#include "../log.h"
#include <system_error>

namespace Impacto {
namespace Io {

int64_t GetFileSize(std::string const& path) {
std::error_code ec;
uintmax_t result = std::filesystem::file_size(path, ec);
if (ec) {
ImpLog(LL_Error, LC_IO,
"Error getting file size of file \"%s\", error: \"%s\"\n",
path.c_str(), ec.message().c_str());
return IoError_Fail;
}
// Hopefully no one has a file of size between int64_t max and uint64_t max
return static_cast<int64_t>(result);
}

IoError PathExists(std::string const& path) {
std::error_code ec;
bool result = std::filesystem::exists(path, ec);
if (ec) {
ImpLog(LL_Error, LC_IO,
"Error checking for file existence for file \"%s\", error: \"%s\"\n",
path.c_str(), ec.message().c_str());
return IoError_Fail;
}
return result == false ? IoError_NotFound : IoError_OK;
}

int8_t CreateDirectories(std::string const& path) {
std::error_code ec;
bool result = std::filesystem::create_directories(path, ec);
if (ec) {
ImpLog(LL_Error, LC_IO,
"Error creating directories for file \"%s\", error: \"%s\"\n",
path.c_str(), ec.message().c_str());
return IoError_Fail;
}
return result;
}

IoError GetFilePermissions(std::string const& path,
FilePermissionsFlags& flags) {
std::error_code ec;
flags = std::filesystem::status(path, ec).permissions();
if (ec) {
ImpLog(LL_Error, LC_IO,
"Error retrieving permissions for file \"%s\", error: \"%s\"\n",
path.c_str(), ec.message().c_str());
return IoError_Fail;
}
return IoError_OK;
}

} // namespace Io
} // namespace Impacto
Loading
Loading