Skip to content

Commit

Permalink
Merge pull request #67 from CommitteeOfZero/cclcc/create-savedata
Browse files Browse the repository at this point in the history
Cclcc/create savedata & fstream over sdl_rw
  • Loading branch information
KKhanhH authored Sep 27, 2024
2 parents e1a6621 + 80b5924 commit d5df3b7
Show file tree
Hide file tree
Showing 14 changed files with 187 additions and 107 deletions.
14 changes: 14 additions & 0 deletions src/data/savesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ using namespace Impacto::Profile::SaveSystem;

void Init() { Configure(); }

SaveError CreateSaveFile() {
if (Implementation)
return Implementation->CreateSaveFile();
else
return SaveOK; // Just so we don't get stuck
}

SaveError CheckSaveFile() {
if (Implementation)
return Implementation->CheckSaveFile();
else
return SaveOK;
}

SaveError MountSaveFile() {
if (Implementation)
return Implementation->MountSaveFile();
Expand Down
4 changes: 4 additions & 0 deletions src/data/savesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class SaveFileEntryBase {

class SaveSystemBase {
public:
virtual SaveError CreateSaveFile() = 0;
virtual SaveError CheckSaveFile() = 0;
virtual SaveError MountSaveFile() = 0;
virtual void SaveMemory() = 0;
virtual void LoadEntry(SaveType type, int id) = 0;
Expand Down Expand Up @@ -105,6 +107,8 @@ inline SaveSystemBase* Implementation = nullptr;

void Init();

SaveError CreateSaveFile();
SaveError CheckSaveFile();
SaveError MountSaveFile();
void SaveMemory();
void LoadEntry(SaveType type, int id);
Expand Down
39 changes: 38 additions & 1 deletion src/games/cclcc/savesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

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

namespace Impacto {
namespace CCLCC {
Expand All @@ -23,6 +24,42 @@ using namespace Impacto::Profile::SaveSystem;
using namespace Impacto::Profile::ScriptVars;
using namespace Impacto::Profile::Vm;

SaveError SaveSystem::CheckSaveFile() {
std::filesystem::path savePath(SaveFilePath);
if (!std::filesystem::exists(savePath)) {
return SaveNotFound;
}
if (std::filesystem::file_size(savePath) != 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) {
return SaveWrongUser;
}
return SaveOK;
}

SaveError SaveSystem::CreateSaveFile() {
Io::Stream* stream;
IoError err = Io::PhysicalFileStream::Create(SaveFilePath, &stream, false);
if (err != IoError_OK) {
return SaveFailed;
}

assert(stream->Meta.Size == 0);
std::vector<uint8_t> emptyData(SaveFileSize, 0);
Io::WriteArrayBE<uint8_t>(emptyData.data(), stream, SaveFileSize);
assert(stream->Position == SaveFileSize);
delete stream;

return MountSaveFile();
}

SaveError SaveSystem::MountSaveFile() {
Io::Stream* stream;
IoError err = Io::PhysicalFileStream::Create(SaveFilePath, &stream);
Expand Down Expand Up @@ -235,7 +272,7 @@ void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id) {

void SaveSystem::WriteSaveFile() {
Io::Stream* stream;
IoError err = Io::PhysicalFileStream::CreateWrite(SaveFilePath, &stream);
IoError err = Io::PhysicalFileStream::Create(SaveFilePath, &stream);
if (err != IoError_OK) {
ImpLog(LL_Error, LC_IO, "Failed to open save file for writing\n");
return;
Expand Down
3 changes: 3 additions & 0 deletions src/games/cclcc/savesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace CCLCC {

using namespace Impacto::SaveSystem;

constexpr int SaveFileSize = 0x1b110 * MaxSaveEntries * 2 + 0x387c;
constexpr int SaveThumbnailWidth = 240;
constexpr int SaveThumbnailHeight = 135;

Expand All @@ -25,6 +26,8 @@ class SaveFileEntry : public SaveFileEntryBase {

class SaveSystem : public SaveSystemBase {
public:
SaveError CreateSaveFile() override;
SaveError CheckSaveFile() override;
SaveError MountSaveFile() override;
void SaveMemory() override;
void LoadEntry(SaveType type, int id) override;
Expand Down
2 changes: 1 addition & 1 deletion src/games/chlcc/savesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id) {

void SaveSystem::WriteSaveFile() {
Io::Stream* stream;
IoError err = Io::PhysicalFileStream::CreateWrite(SaveFilePath, &stream);
IoError err = Io::PhysicalFileStream::Create(SaveFilePath, &stream);
if (err != IoError_OK) {
ImpLog(LL_Error, LC_IO, "Failed to open save file for writing\n");
return;
Expand Down
2 changes: 2 additions & 0 deletions src/games/chlcc/savesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class SaveFileEntry : public SaveFileEntryBase {

class SaveSystem : public SaveSystemBase {
public:
SaveError CreateSaveFile() override { return SaveOK; } // Todo
SaveError CheckSaveFile() override { return SaveOK; } // Todo
SaveError MountSaveFile() override;
void SaveMemory() override;
void LoadEntry(SaveType type, int id) override;
Expand Down
2 changes: 1 addition & 1 deletion src/games/mo6tw/savesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id) {
void SaveSystem::WriteSaveFile() {
Io::PhysicalFileStream* stream;
Io::Stream* instream;
IoError err = Io::PhysicalFileStream::CreateWrite(SaveFilePath, &instream);
IoError err = Io::PhysicalFileStream::Create(SaveFilePath, &instream);
auto err1 = SDL_GetError();
if (err != IoError_OK) {
ImpLog(LL_Error, LC_IO, "Failed to create save file, SDL error: %s\n",
Expand Down
2 changes: 2 additions & 0 deletions src/games/mo6tw/savesystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class SaveFileEntry : public SaveFileEntryBase {

class SaveSystem : public SaveSystemBase {
public:
SaveError CreateSaveFile() override { return SaveOK; } // Todo
SaveError CheckSaveFile() override { return SaveOK; } // Todo
SaveError MountSaveFile() override;
void SaveMemory() override;
void LoadEntry(SaveType type, int id) override;
Expand Down
136 changes: 73 additions & 63 deletions src/io/physicalfilestream.cpp
Original file line number Diff line number Diff line change
@@ -1,52 +1,54 @@
#include "physicalfilestream.h"
#include "../log.h"

#include <cstring>
#include <algorithm>

namespace Impacto {
namespace Io {

PhysicalFileStream::~PhysicalFileStream() { SDL_RWclose(RW); }

IoError PhysicalFileStream::Create(std::string const& fileName, Stream** out) {
SDL_RWops* rw = SDL_RWFromFile(fileName.c_str(), "rb");
if (!rw) return IoError_Fail;
int64_t size = SDL_RWsize(rw);
if (size < 0) return IoError_Fail;
PhysicalFileStream* result = new PhysicalFileStream;
result->RW = rw;
result->Meta.Size = size;
result->SourceFileName = fileName;
result->Meta.FileName = fileName;
*out = (Stream*)result;
return IoError_OK;
}

IoError PhysicalFileStream::CreateWrite(std::string const& fileName,
Stream** out, bool exists) {
SDL_RWops* rw = (exists) ? SDL_RWFromFile(fileName.c_str(), "r+b")
: SDL_RWFromFile(fileName.c_str(), "wb");
if (!rw) return IoError_Fail;
int64_t size = SDL_RWsize(rw);
if (size < 0) return IoError_Fail;
PhysicalFileStream* result = new PhysicalFileStream;
result->RW = rw;
result->Meta.Size = size;
result->SourceFileName = fileName;
result->Meta.FileName = fileName;
result->IsWrite = true;
IoError PhysicalFileStream::Create(std::string const& fileName, Stream** out,
bool exists) {
std::error_code ec;
if (exists && !std::filesystem::exists(fileName, ec)) {
if (ec) {
ImpLog(LL_Error, LC_IO, "Error checking file existence: %s\n",
ec.message().c_str());
return IoError_Fail;
}
return IoError_NotFound;
}
PhysicalFileStream* result = new PhysicalFileStream(fileName, !exists);
if (!result->FileStream) {
ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n",
fileName.c_str(), std::strerror(errno));
delete result;
return IoError_Fail;
}
result->Meta.Size = std::filesystem::file_size(result->SourceFileName, ec);
if (ec) {
ImpLog(LL_Error, LC_IO, "Error getting file size: %s\n",
ec.message().c_str());
delete result;
return IoError_Fail;
}
*out = (Stream*)result;
return IoError_OK;
}

IoError PhysicalFileStream::FillBuffer() {
int64_t read = SDL_RWread(RW, Buffer, 1, PhysicalBufferSize);
if (read < 0) return IoError_Fail;
BufferFill = read;
return IoError_OK;
}

int64_t PhysicalFileStream::Read(void* buffer, int64_t sz) {
return ReadBuffered(buffer, sz);
if (FileStream.eof()) {
return IoError_Eof;
}
FileStream.read((char*)buffer, sz);
if (!FileStream && !FileStream.eof()) {
ImpLog(LL_Error, LC_IO, "Read failed for file \"%s\" with error: \"%s\"\n",
SourceFileName.string().c_str(), std::strerror(errno));
return IoError_Fail;
}
int64_t read = FileStream.gcount();
Position += read;
return read;
}

int64_t PhysicalFileStream::Seek(int64_t offset, int origin) {
Expand All @@ -59,48 +61,56 @@ int64_t PhysicalFileStream::Seek(int64_t offset, int origin) {
absPos = Position + offset;
break;
case RW_SEEK_END:
absPos = Meta.Size - offset;
absPos = Meta.Size + offset;
break;
}
if (absPos < 0 || absPos > Meta.Size) return IoError_Fail;

int64_t err = SeekBuffered(absPos);
if (err < IoError_OK) {
BufferFill = 0;
BufferConsumed = 0;
err = SDL_RWseek(RW, absPos,
RW_SEEK_SET); // TODO: why does SDL_RWtell not match
// Position here sometimes? This causes PNGs
// with iTxT chunk to fail loading :thonk:
if (err < 0) return IoError_Fail;
Position = err;
FileStream.seekg(absPos, std::ios::beg);
if (!FileStream) {
ImpLog(LL_Error, LC_IO, "Seek failed for file \"%s\" with error: \"%s\"\n",
SourceFileName.string().c_str(), std::strerror(errno));
return IoError_Fail;
}
return err;
Position = absPos;
return Position;
}

IoError PhysicalFileStream::Duplicate(Stream** outStream) {
SDL_RWops* rw = SDL_RWFromFile(SourceFileName.c_str(), "rb");
if (!rw) return IoError_Fail;
int64_t filePos = SDL_RWtell(RW);
if (filePos < 0) {
SDL_RWclose(rw);
PhysicalFileStream* result = new PhysicalFileStream(*this);
std::error_code ec;
if (!result->FileStream) {
ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n",
SourceFileName.string().c_str(), std::strerror(errno));
delete result;
return IoError_Fail;
}
int64_t pos = SDL_RWseek(rw, filePos, RW_SEEK_SET);
if (pos != filePos) {
SDL_RWclose(rw);
result->Meta.Size = std::filesystem::file_size(SourceFileName, ec);
if (ec) {
ImpLog(LL_Error, LC_IO, "Error getting file size: %s\n",
ec.message().c_str());
delete result;
return IoError_Fail;
}
if (result->Seek(Position, RW_SEEK_SET) < 0) {
ImpLog(LL_Error, LC_IO, "Seek failed for file \"%s\" with error: \"%s\"\n",
SourceFileName.string().c_str(), std::strerror(errno));
delete result;
return IoError_Fail;
}
PhysicalFileStream* result = new PhysicalFileStream(*this);
result->RW = rw;
*outStream = (Stream*)result;
return IoError_OK;
}

int64_t PhysicalFileStream::Write(void* buffer, int64_t sz, int cnt) {
// Todo: buffered write (SDL_RWwrite doesn't buffer system calls)
int64_t written = SDL_RWwrite(RW, buffer, sz, cnt);
Seek(sz * cnt, SEEK_CUR);
FileStream.write((char*)buffer, sz * cnt);
if (!FileStream) {
ImpLog(LL_Error, LC_IO, "Write failed for file \"%s\" with error: \"%s\"\n",
SourceFileName.string().c_str(), std::strerror(errno));
return IoError_Fail;
}
int64_t written = FileStream.gcount();
Position += written;
Meta.Size = std::max(Position, Meta.Size);
return written;
}

Expand Down
47 changes: 26 additions & 21 deletions src/io/physicalfilestream.h
Original file line number Diff line number Diff line change
@@ -1,39 +1,44 @@
#pragma once

#include "stream.h"
#include <SDL_rwops.h>
#include <fstream>
#include <filesystem>
#include "buffering.h"

namespace Impacto {
namespace Io {

// TODO *optional* buffering

class PhysicalFileStream : public Stream, public Buffering<PhysicalFileStream> {
friend class Buffering<PhysicalFileStream>;

class PhysicalFileStream : public Stream {
public:
~PhysicalFileStream();

static IoError Create(std::string const& fileName, Stream** out);
static IoError CreateWrite(std::string const& fileName, Stream** out,
bool exists = true);
static IoError Create(std::string const& fileName, Stream** out,
bool exists = true);
int64_t Read(void* buffer, int64_t sz) override;
int64_t Seek(int64_t offset, int origin) override;
IoError Duplicate(Stream** outStream) override;
int64_t Write(void* buffer, int64_t sz, int cnt = 1) override;

protected:
static int constexpr PhysicalBufferSize = 16 * 1024;
bool IsWrite = false;
PhysicalFileStream() : Buffering(PhysicalBufferSize) {}
PhysicalFileStream(PhysicalFileStream const& other) = default;

IoError FillBuffer();
IoError FlushBuffer();

SDL_RWops* RW;
std::string SourceFileName;
std::ios_base::openmode GetFileMode(bool truncate) {
// trunc will clear file if it exists, and allows creation of new file
return (truncate) ? std::ios::in | std::ios::out | std::ios::trunc |
std::ios::binary
: std::ios::in | std::ios::out | std::ios::binary;
}
PhysicalFileStream(std::filesystem::path filePath, bool truncate = false)
: Truncate(truncate),
SourceFileName(std::move(filePath)),
FileStream(SourceFileName, GetFileMode(truncate)) {
Meta.FileName = SourceFileName.string();
}

PhysicalFileStream(PhysicalFileStream const& other)
: SourceFileName(other.SourceFileName),
FileStream(other.SourceFileName, GetFileMode(other.Truncate)) {
Meta.FileName = SourceFileName.string();
}
bool Truncate;
std::filesystem::path SourceFileName;
std::fstream FileStream;
};

} // namespace Io
Expand Down
Loading

0 comments on commit d5df3b7

Please sign in to comment.