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

Cclcc/create savedata & fstream test #67

Merged
merged 4 commits into from
Sep 27, 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
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::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::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 Expand Up @@ -666,5 +703,5 @@
case SaveFull:
return ((SaveFileEntry*)FullSaveEntries[id])->SaveThumbnail;
}
}

Check warning on line 706 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
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::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 Expand Up @@ -591,5 +591,5 @@
case SaveFull:
return FullSaveEntries[id]->SaveThumbnail;
}
}

Check warning on line 594 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: 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::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 Expand Up @@ -734,5 +734,5 @@
case SaveFull:
return ((SaveFileEntry*)FullSaveEntries[id])->SaveThumbnail;
}
}

Check warning on line 737 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: 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
Loading