Skip to content

Commit

Permalink
Merge pull request #71 from CommitteeOfZero/quicksaving
Browse files Browse the repository at this point in the history
Quicksaving + More robust files API
  • Loading branch information
KKhanhH authored Oct 11, 2024
2 parents d42532e + 80a1b01 commit 63a6d8d
Show file tree
Hide file tree
Showing 18 changed files with 387 additions and 113 deletions.
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::ScriptVars;
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 @@ SaveError SaveSystem::MountSaveFile() {
// 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::FlushWorkingSaveEntry(SaveType type, int id) {
}

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
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 @@ SaveError SaveSystem::MountSaveFile() {
// 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 @@ void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id) {
}

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
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 @@ SaveError SaveSystem::MountSaveFile() {
// 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 @@ void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id) {
}

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
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

0 comments on commit 63a6d8d

Please sign in to comment.