From c4de1858dd730a1202ded6c6b3305e56de97da98 Mon Sep 17 00:00:00 2001 From: Dextinfire <> Date: Sun, 29 Sep 2024 01:54:03 -0700 Subject: [PATCH 01/10] Quick Saving: Create quick load menu by sorting entries by date time Add auto quicksaving and quick saving TODO: Somehow check if quick save already happened so qs button can't get spammed. --- src/data/savesystem.cpp | 13 +++--- src/data/savesystem.h | 17 ++++++-- src/games/cclcc/savemenu.cpp | 38 +++++++++++++++--- src/games/cclcc/savesystem.cpp | 10 +++-- src/games/cclcc/savesystem.h | 2 +- src/games/chlcc/savesystem.cpp | 8 +++- src/games/chlcc/savesystem.h | 2 +- src/games/mo6tw/savesystem.cpp | 9 ++++- src/games/mo6tw/savesystem.h | 2 +- src/ui/widgets/cclcc/saveentrybutton.cpp | 8 ++-- src/ui/widgets/cclcc/saveentrybutton.h | 8 ++-- src/vm/inst_dialogue.cpp | 27 ++++++++++++- src/vm/inst_system.cpp | 50 +++++++++++++++--------- 13 files changed, 141 insertions(+), 53 deletions(-) diff --git a/src/data/savesystem.cpp b/src/data/savesystem.cpp index c6636768..879ae13d 100644 --- a/src/data/savesystem.cpp +++ b/src/data/savesystem.cpp @@ -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() { @@ -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) { diff --git a/src/data/savesystem.h b/src/data/savesystem.h index efb10543..f0d75f7c 100644 --- a/src/data/savesystem.h +++ b/src/data/savesystem.h @@ -72,7 +72,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; @@ -93,7 +94,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) != 1) return i; + } + return -1; + } Sprite const& GetWorkingSaveThumbnail() { return WorkingSaveThumbnail; } protected: @@ -113,7 +122,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); @@ -130,7 +139,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(); diff --git a/src/games/cclcc/savemenu.cpp b/src/games/cclcc/savemenu.cpp index e9e7c124..984728b0 100644 --- a/src/games/cclcc/savemenu.cpp +++ b/src/games/cclcc/savemenu.cpp @@ -55,6 +55,37 @@ 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 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; @@ -62,16 +93,13 @@ void SaveMenu::Show() { 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]], diff --git a/src/games/cclcc/savesystem.cpp b/src/games/cclcc/savesystem.cpp index 9b55a9c2..504187bb 100644 --- a/src/games/cclcc/savesystem.cpp +++ b/src/games/cclcc/savesystem.cpp @@ -232,20 +232,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) != 1) { Renderer->FreeTexture(entry->SaveThumbnail.Sheet.Texture); *entry = *WorkingSaveEntry; + if (type == SaveQuick) { + entry->SaveType = autoSaveType; + } time_t rawtime; time(&rawtime); entry->SaveDate = *localtime(&rawtime); diff --git a/src/games/cclcc/savesystem.h b/src/games/cclcc/savesystem.h index f0a86545..098068d9 100644 --- a/src/games/cclcc/savesystem.h +++ b/src/games/cclcc/savesystem.h @@ -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; diff --git a/src/games/chlcc/savesystem.cpp b/src/games/chlcc/savesystem.cpp index 8e79cc8c..93f1ebba 100644 --- a/src/games/chlcc/savesystem.cpp +++ b/src/games/chlcc/savesystem.cpp @@ -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: @@ -144,8 +145,11 @@ void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id) { } if (WorkingSaveEntry != 0) { - if (entry != 0) { + if (entry != 0 && GetSaveFlags(type, id) != 1) { *entry = *WorkingSaveEntry; + if (type == SaveQuick) { + entry->SaveType = autoSaveType; + } time_t rawtime; time(&rawtime); entry->SaveDate = *localtime(&rawtime); diff --git a/src/games/chlcc/savesystem.h b/src/games/chlcc/savesystem.h index 6917c26e..75977cd8 100644 --- a/src/games/chlcc/savesystem.h +++ b/src/games/chlcc/savesystem.h @@ -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; diff --git a/src/games/mo6tw/savesystem.cpp b/src/games/mo6tw/savesystem.cpp index f4c025a6..60e9b943 100644 --- a/src/games/mo6tw/savesystem.cpp +++ b/src/games/mo6tw/savesystem.cpp @@ -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: @@ -202,7 +203,11 @@ void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id) { } if (WorkingSaveEntry != 0) { - if (entry != 0) { + if (entry != 0 && GetSaveFlags(type, id) != 1) { + Renderer->FreeTexture(entry->SaveThumbnail.Sheet.Texture); + if (type == SaveQuick) { + entry->SaveType = autoSaveType; + } entry->Status = 1; std::time_t t = std::time(0); diff --git a/src/games/mo6tw/savesystem.h b/src/games/mo6tw/savesystem.h index 54c3c8ea..ddb376bb 100644 --- a/src/games/mo6tw/savesystem.h +++ b/src/games/mo6tw/savesystem.h @@ -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; diff --git a/src/ui/widgets/cclcc/saveentrybutton.cpp b/src/ui/widgets/cclcc/saveentrybutton.cpp index 7c6ec238..db8582cb 100644 --- a/src/ui/widgets/cclcc/saveentrybutton.cpp +++ b/src/ui/widgets/cclcc/saveentrybutton.cpp @@ -20,19 +20,17 @@ using namespace Impacto::SaveSystem; glm::vec4 SaveEntryButton::FocusedAlpha = glm::vec4(1.0f); Animation SaveEntryButton::FocusedAlphaFade; -SaveEntryButton::SaveEntryButton(int id, Sprite const& focusedBox, +SaveEntryButton::SaveEntryButton(int id, int index, Sprite const& focusedBox, Sprite const& focusedText, int page, glm::vec2 pos, Sprite lockedSymbol, SaveSystem::SaveType saveType, Sprite NoDataSprite, Sprite BrokenDataSprite) : Widgets::Button( - (saveType == SaveSystem::SaveType::SaveFull - ? id - : SaveSystem::GetQuickSaveCount() - id - 1), + id, Sprite(SpriteSheet(), focusedBox.Bounds.X, focusedBox.Bounds.Y, focusedBox.Bounds.Width, focusedBox.Bounds.Height), Sprite(SpriteSheet(), 0, 0, 0, 0), focusedBox, pos), - Index(id), + Index(index), Page(page), FocusedSpriteLabel(focusedText, glm::vec2{pos.x, pos.y - 34}), LockedSymbol(lockedSymbol, diff --git a/src/ui/widgets/cclcc/saveentrybutton.h b/src/ui/widgets/cclcc/saveentrybutton.h index fe74c3a8..8091776b 100644 --- a/src/ui/widgets/cclcc/saveentrybutton.h +++ b/src/ui/widgets/cclcc/saveentrybutton.h @@ -14,10 +14,10 @@ namespace CCLCC { class SaveEntryButton : public Widgets::Button { public: - SaveEntryButton(int id, Sprite const& focusedBox, Sprite const& focusedText, - int page, glm::vec2 pos, Sprite lockedSymbol, - SaveSystem::SaveType saveType, Sprite noDataSprite, - Sprite brokenDataSprite); + SaveEntryButton(int id, int index, Sprite const& focusedBox, + Sprite const& focusedText, int page, glm::vec2 pos, + Sprite lockedSymbol, SaveSystem::SaveType saveType, + Sprite noDataSprite, Sprite brokenDataSprite); void Render() override; int GetPage() const; void AddNormalSpriteLabel(Sprite norm, glm::vec2 pos); diff --git a/src/vm/inst_dialogue.cpp b/src/vm/inst_dialogue.cpp index 10bffefd..05d847bc 100644 --- a/src/vm/inst_dialogue.cpp +++ b/src/vm/inst_dialogue.cpp @@ -371,7 +371,32 @@ VmInstruction(InstSelect) { switch (type) { case 0: { UI::SelectionMenuPtr->Show(); - SaveIconDisplay::ShowFor(2.4f); + bool flag = GetFlag(1282); + int unk = ScrWork[2108]; + if (unk < 0x100) { + ScrWork[2108] = unk + 0x10; + SetFlag(1286, 1); + ResetInstruction; + BlockThread; + } + SetFlag(thread->DialoguePageId + 1213, 0); + if (ScrWork[2112] == 2) { + thread->Ip += 12; + return; + } else { + SaveSystem::SaveMemory(); + int quicksaveEntries = SaveSystem::GetQuickSaveOpenSlot(); + if (!flag && quicksaveEntries != -1) { + SaveIconDisplay::ShowFor(2.4f); + SaveSystem::FlushWorkingSaveEntry(SaveSystem::SaveType::SaveQuick, + quicksaveEntries, 2); + } + ScrWork[2112] = 0; + if (quicksaveEntries == -1) { + thread->Ip += 12; + } + } + SetFlag(1286, 0); } break; case 1: { if (!UI::SelectionMenuPtr->ChoiceMade) { diff --git a/src/vm/inst_system.cpp b/src/vm/inst_system.cpp index 495e99cb..200a766f 100644 --- a/src/vm/inst_system.cpp +++ b/src/vm/inst_system.cpp @@ -713,38 +713,49 @@ VmInstruction(InstDebugData) { } VmInstruction(InstAutoSave) { StartInstruction; + auto quickSave = [&](int autosaveRestartCheck, int saveType) { + SaveSystem::SaveMemory(); + if (ScrWork[2112] != autosaveRestartCheck) { + int quicksaveEntries = SaveSystem::GetQuickSaveOpenSlot(); + if (quicksaveEntries != -1) { + SaveIconDisplay::ShowFor(2.4f); + SaveSystem::FlushWorkingSaveEntry(SaveSystem::SaveType::SaveQuick, + quicksaveEntries, saveType); + } + } + SetFlag(1285, 1); + ScrWork[2112] = 0; + }; + PopUint8(type); switch (type) { case 0: // QuickSave - SaveIconDisplay::ShowFor(2.4f); + case 20: if (ScrWork[SW_TITLE] == 0xffff) break; - SaveSystem::SaveMemory(); - if (ScrWork[2112] != 1) { - // TODO: Quicksave(1) - } - SetFlag(1285, 1); - ScrWork[2112] = 0; + quickSave(1, 1); ImpLogSlow(LL_Warning, LC_VMStub, "STUB instruction AutoSave(type: QuickSave)\n"); break; case 1: // AutoSaveRestart (?) + case 21: if (ScrWork[SW_TITLE] == 0xffff) break; - SaveSystem::SaveMemory(); - if ((ScrWork[2112] != 3)) { - // TODO: Quicksave(3) - } - SetFlag(1285, 1); - ScrWork[2112] = 0; + quickSave(3, 3); ImpLogSlow(LL_Warning, LC_VMStub, "STUB instruction AutoSave(type: %i)\n", type); break; - case 3: // DisableAutoSave - // Check quicksave, quicksave(0) + case 3: { // DisableAutoSave + int quicksaveEntries = SaveSystem::GetQuickSaveOpenSlot(); + if (quicksaveEntries != -1) { + SaveIconDisplay::ShowFor(2.4f); + SaveSystem::FlushWorkingSaveEntry(SaveSystem::SaveType::SaveQuick, + quicksaveEntries, 0); + } + SetFlag(1285, 0); ImpLogSlow(LL_Warning, LC_VMStub, "STUB instruction AutoSave(type: %i)\n", type); - break; - case 5: // EnableAutoSave + } break; + case 5: { // EnableAutoSave ImpLogSlow(LL_Warning, LC_VMStub, "STUB instruction AutoSave(type: %i)\n", type); if (ScrWork[SW_TITLE] != 0xffff) { @@ -752,7 +763,7 @@ VmInstruction(InstAutoSave) { SetFlag(1285, 1); ScrWork[2112] = 0; } - break; + } break; case 10: { // SetCheckpointId if (Profile::Vm::UseReturnIds) { PopUint16(checkpointId); @@ -774,6 +785,9 @@ VmInstruction(InstAutoSave) { ImpLogSlow(LL_Warning, LC_VMStub, "STUB instruction AutoSave(arg1: %i)\n", type); break; + case 0xff: { + BlockThread; + } break; default: // More quicksave cases here break; From 8efa7406f11636e3cd6c2468159057c6d36a8409 Mon Sep 17 00:00:00 2001 From: Dextinfire <> Date: Fri, 4 Oct 2024 02:53:28 -0700 Subject: [PATCH 02/10] Return to title actually writes to save --- src/vm/inst_system.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vm/inst_system.cpp b/src/vm/inst_system.cpp index 200a766f..aa778eda 100644 --- a/src/vm/inst_system.cpp +++ b/src/vm/inst_system.cpp @@ -223,6 +223,9 @@ VmInstruction(InstSave) { switch (type) { // TODO: Types 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // 16, 20, 21, 72, 30, 31, 32, 33, 34, 35, 41, 50, 51, 66, // 67, 70, 71, 74, 76 + case 0: { + // TODO, System Save only + } break; case 3: break; case 4: { @@ -255,6 +258,9 @@ VmInstruction(InstSave) { break; case 51: break; + case 60: + SaveSystem::WriteSaveFile(); + break; case 70: if (Profile::Vm::GameInstructionSet == +InstructionSet::CC) { LoadSaveFile(); From 684f91cbcca07bf71bbbea8a312f38924b311fa3 Mon Sep 17 00:00:00 2001 From: Dextinfire <> Date: Sat, 5 Oct 2024 00:14:17 -0700 Subject: [PATCH 03/10] Physical file stream api with more specific flags, create parent directories, better error printing --- src/io/physicalfilestream.cpp | 123 +++++++++++++++++++++++++++++----- src/io/physicalfilestream.h | 39 +++++++---- 2 files changed, 131 insertions(+), 31 deletions(-) diff --git a/src/io/physicalfilestream.cpp b/src/io/physicalfilestream.cpp index 41648673..0194bab4 100644 --- a/src/io/physicalfilestream.cpp +++ b/src/io/physicalfilestream.cpp @@ -3,25 +3,84 @@ #include #include +#include namespace Impacto { namespace Io { +std::ios_base::openmode PhysicalFileStream::PrepareFileOpenMode( + CreateFlags flags, std::error_code& ec) { + std::ios_base::openmode mode = std::ios::binary; + bool fileExists = std::filesystem::exists(SourceFileName, ec); + if (ec) { + ImpLog(LL_Error, LC_IO, + "Failed to check whether file exists \"%s\", error: \"%s\"\n", + SourceFileName.generic_string().c_str(), ec.message().c_str()); + return {}; + } -IoError PhysicalFileStream::Create(std::string const& fileName, Stream** out, - bool exists) { - std::error_code ec; - if (exists && !std::filesystem::exists(fileName, ec)) { + bool writeNoExistNoCreate = + (flags & WRITE) && !fileExists && !(flags & CREATE_IF_NOT_EXISTS); + bool readNoExist = !(flags & WRITE) && (flags & READ) && !fileExists; + if (writeNoExistNoCreate || readNoExist) { + ec = std::make_error_code(std::errc::no_such_file_or_directory); + ImpLog(LL_Error, LC_IO, "Failed to open stream \"%s\", error: \"%s\"\n", + SourceFileName.generic_string().c_str(), ec.message().c_str()); + return {}; + } + + bool writeExistNoOverwrite = + (flags & WRITE) && fileExists && !(flags & TRUNCATE) && !(flags & APPEND); + if (writeExistNoOverwrite) { + // avoid truncating when in write only mode without truncate flag to + // preserve file size + // I think this is more intuitive than making it an error + // or letting the truncate happen + flags |= CreateFlagsMode::READ; + } + + // require write for create/overwrite flags + assert((flags & WRITE) || !(flags & CREATE_DIRS)); + assert((flags & WRITE) || !(flags & CREATE_IF_NOT_EXISTS)); + + // truncate is only needed when creating nonexistent if also reading + bool truncFlag = (flags & TRUNCATE) || + ((flags & READ) && !fileExists && + (flags & CREATE_IF_NOT_EXISTS) && !(flags & APPEND)); + + // trunc and append are mutually exclusive + assert(!truncFlag || !(flags & APPEND)); + + if (flags & READ) mode |= std::ios::in; + if (flags & WRITE) mode |= std::ios::out; + if (flags & APPEND) mode |= std::ios::app; + if (truncFlag) mode |= std::ios::trunc; + if (flags & CREATE_DIRS) { + std::filesystem::create_directories(SourceFileName.parent_path(), ec); if (ec) { - ImpLog(LL_Error, LC_IO, "Error checking file existence: %s\n", - ec.message().c_str()); - return IoError_Fail; + ImpLog(LL_Error, LC_IO, + "Failed to create directories for file \"%s\", error: \"%s\"\n", + SourceFileName.generic_string().c_str(), ec.message().c_str()); + return {}; } - return IoError_NotFound; } - PhysicalFileStream* result = new PhysicalFileStream(fileName, !exists); + return mode; +} + +IoError PhysicalFileStream::Create(std::string const& fileName, Stream** out, + CreateFlags flags) { + std::error_code ec; + PhysicalFileStream* result = new PhysicalFileStream(fileName, flags); + if (result->ErrorCode) { + ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n", + fileName.c_str(), result->ErrorCode.message().c_str()); + delete result; + return result->ErrorCode == std::errc::no_such_file_or_directory + ? IoError_NotFound + : IoError_Fail; + } if (!result->FileStream) { ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n", - fileName.c_str(), std::strerror(errno)); + fileName.c_str(), std::generic_category().message(errno).c_str()); delete result; return IoError_Fail; } @@ -37,13 +96,18 @@ IoError PhysicalFileStream::Create(std::string const& fileName, Stream** out, } int64_t PhysicalFileStream::Read(void* buffer, int64_t sz) { - if (sz < 0) return IoError_Fail; - if (Position == Meta.Size) return IoError_Eof; + if (sz < 0 || !(Flags & READ)) return IoError_Fail; + if (Position >= Meta.Size || FileStream.eof()) { + FileStream.clear(FileStream.rdstate() & ~std::ios::failbit & + ~std::ios::eofbit); // Clear only failbit and eofbit + return IoError_Eof; + }; int bytesToRead = std::min(sz, Meta.Size - Position); FileStream.read((char*)buffer, bytesToRead); if (!FileStream) { ImpLog(LL_Error, LC_IO, "Read failed for file \"%s\" with error: \"%s\"\n", - SourceFileName.string().c_str(), std::strerror(errno)); + SourceFileName.string().c_str(), + std::generic_category().message(errno).c_str()); FileStream.clear(FileStream.rdstate() & ~std::ios::failbit & ~std::ios::eofbit); // Clear only failbit and eofbit return IoError_Fail; @@ -54,6 +118,10 @@ int64_t PhysicalFileStream::Read(void* buffer, int64_t sz) { } int64_t PhysicalFileStream::Seek(int64_t offset, int origin) { + if (!(Flags & READ) && (Flags & APPEND)) { + // seeking is useless in readless append mode + return IoError_Fail; + } int64_t absPos; switch (origin) { case RW_SEEK_SET: @@ -66,11 +134,15 @@ int64_t PhysicalFileStream::Seek(int64_t offset, int origin) { absPos = Meta.Size - offset; break; } - if (absPos < 0 || absPos > Meta.Size) return IoError_Fail; + + // seeking past EOF is a legal operation, after write past EOF, the gap + // between prev file size and write pos is zero padded + if (absPos < 0) return IoError_Fail; FileStream.seekg(absPos, std::ios::beg); if (!FileStream && !FileStream.eof()) { ImpLog(LL_Error, LC_IO, "Seek failed for file \"%s\" with error: \"%s\"\n", - SourceFileName.string().c_str(), std::strerror(errno)); + SourceFileName.string().c_str(), + std::generic_category().message(errno).c_str()); FileStream.clear(FileStream.rdstate() & ~std::ios::failbit & ~std::ios::eofbit); // Clear only failbit and eofbit return IoError_Fail; @@ -82,9 +154,19 @@ int64_t PhysicalFileStream::Seek(int64_t offset, int origin) { IoError PhysicalFileStream::Duplicate(Stream** outStream) { PhysicalFileStream* result = new PhysicalFileStream(*this); std::error_code ec; + if (result->ErrorCode) { + ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n", + SourceFileName.string().c_str(), + result->ErrorCode.message().c_str()); + delete result; + return result->ErrorCode == std::errc::no_such_file_or_directory + ? IoError_NotFound + : IoError_Fail; + } if (!result->FileStream) { ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n", - SourceFileName.string().c_str(), std::strerror(errno)); + SourceFileName.string().c_str(), + std::generic_category().message(errno).c_str()); delete result; return IoError_Fail; } @@ -97,7 +179,8 @@ IoError PhysicalFileStream::Duplicate(Stream** outStream) { } 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)); + SourceFileName.string().c_str(), + std::generic_category().message(errno).c_str()); delete result; return IoError_Fail; } @@ -106,10 +189,14 @@ IoError PhysicalFileStream::Duplicate(Stream** outStream) { } int64_t PhysicalFileStream::Write(void* buffer, int64_t sz, int cnt) { + if (!(Flags & WRITE)) { + return IoError_Fail; + } 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)); + SourceFileName.string().c_str(), + std::generic_category().message(errno).c_str()); FileStream.clear(FileStream.rdstate() & ~std::ios::failbit & ~std::ios::eofbit); // Clear only failbit and eofbit return IoError_Fail; diff --git a/src/io/physicalfilestream.h b/src/io/physicalfilestream.h index e140d095..42bdddb7 100644 --- a/src/io/physicalfilestream.h +++ b/src/io/physicalfilestream.h @@ -3,6 +3,7 @@ #include "stream.h" #include #include +#include #include "buffering.h" namespace Impacto { @@ -10,33 +11,45 @@ namespace Io { class PhysicalFileStream : public Stream { public: + enum CreateFlagsMode { + READ = 1, + WRITE = READ << 1, + CREATE_IF_NOT_EXISTS = WRITE << 1, + TRUNCATE = CREATE_IF_NOT_EXISTS << 1, + APPEND = TRUNCATE << 1, + CREATE_DIRS = CREATE_IF_NOT_EXISTS << 1 + }; + using CreateFlags = int; static IoError Create(std::string const& fileName, Stream** out, - bool exists = true); + CreateFlags flags = CreateFlagsMode::READ); 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: - 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), + std::ios_base::openmode PrepareFileOpenMode(CreateFlags flags, + std::error_code& ec); + + PhysicalFileStream(std::filesystem::path filePath, CreateFlags flags) + : Flags(flags), SourceFileName(std::move(filePath)), - FileStream(SourceFileName, GetFileMode(truncate)) { + FileStream(SourceFileName, PrepareFileOpenMode(flags, ErrorCode)) { Meta.FileName = SourceFileName.string(); } + PhysicalFileStream(std::filesystem::path filePath) + : PhysicalFileStream(std::move(filePath), CreateFlagsMode::READ) {} + PhysicalFileStream(PhysicalFileStream const& other) - : SourceFileName(other.SourceFileName), - FileStream(other.SourceFileName, GetFileMode(other.Truncate)) { + : Flags(other.Flags), + SourceFileName(other.SourceFileName), + FileStream(other.SourceFileName, + PrepareFileOpenMode(Flags, ErrorCode)) { Meta.FileName = SourceFileName.string(); } - bool Truncate; + std::error_code ErrorCode; + CreateFlags Flags; std::filesystem::path SourceFileName; std::fstream FileStream; }; From 53e2bb94d4fb33db5dcfb8121810f3cf511e2a6e Mon Sep 17 00:00:00 2001 From: Dextinfire <> Date: Sat, 5 Oct 2024 00:15:16 -0700 Subject: [PATCH 04/10] Create save directory, improved the permissions check, savefile error checks --- src/games/cclcc/savesystem.cpp | 47 ++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/src/games/cclcc/savesystem.cpp b/src/games/cclcc/savesystem.cpp index 504187bb..79c8a242 100644 --- a/src/games/cclcc/savesystem.cpp +++ b/src/games/cclcc/savesystem.cpp @@ -15,6 +15,7 @@ #include #include #include +#include namespace Impacto { namespace CCLCC { @@ -26,27 +27,49 @@ using namespace Impacto::Profile::Vm; SaveError SaveSystem::CheckSaveFile() { std::filesystem::path savePath(SaveFilePath); - if (!std::filesystem::exists(savePath)) { + std::error_code ec; + if (!std::filesystem::exists(savePath, ec)) { + if (ec) { + ImpLog(LL_Error, LC_IO, + "Failed to check if save file exists, error: \"%s\"\n", + ec.message().c_str()); + return SaveFailed; + } return SaveNotFound; } - if (std::filesystem::file_size(savePath) != SaveFileSize) { + if (std::filesystem::file_size(savePath, ec) != SaveFileSize) { + if (ec) { + ImpLog(LL_Error, LC_IO, "Failed to get save file size, error: \"%s\"\n", + ec.message().c_str()); + return SaveFailed; + } 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) { + using PermsFlag = std::filesystem::perms; + auto checkPermsBit = [](PermsFlag perms, PermsFlag flag) { + return to_underlying(perms) & to_underlying(flag); + }; + + if (auto perms = std::filesystem::status(savePath, ec).permissions(); + (!checkPermsBit(perms, PermsFlag::owner_read) || + !checkPermsBit(perms, PermsFlag::owner_write))) { + if (ec) { + ImpLog(LL_Error, LC_IO, + "Failed to get save file permissions, error: \"%s\"\n", + ec.message().c_str()); + return SaveFailed; + } 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; } @@ -275,8 +298,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; From 5d71f1dc8f71b58dd3bc89e41476412afbd32e38 Mon Sep 17 00:00:00 2001 From: Dextinfire <> Date: Sat, 5 Oct 2024 10:00:24 -0700 Subject: [PATCH 05/10] Wrap the filesystem stuff --- CMakeLists.txt | 1 + src/games/cclcc/savesystem.cpp | 50 +++++++++++++--------------- src/io/filemeta.cpp | 61 ++++++++++++++++++++++++++++++++++ src/io/filemeta.h | 11 +++++- src/io/physicalfilestream.cpp | 40 +++++++++------------- src/io/physicalfilestream.h | 12 +++---- 6 files changed, 117 insertions(+), 58 deletions(-) create mode 100644 src/io/filemeta.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a7e9930..0bfc2542 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/src/games/cclcc/savesystem.cpp b/src/games/cclcc/savesystem.cpp index 79c8a242..2e604fb1 100644 --- a/src/games/cclcc/savesystem.cpp +++ b/src/games/cclcc/savesystem.cpp @@ -14,7 +14,6 @@ #include #include -#include #include namespace Impacto { @@ -26,39 +25,38 @@ using namespace Impacto::Profile::ScriptVars; using namespace Impacto::Profile::Vm; SaveError SaveSystem::CheckSaveFile() { - std::filesystem::path savePath(SaveFilePath); std::error_code ec; - if (!std::filesystem::exists(savePath, ec)) { - if (ec) { - ImpLog(LL_Error, LC_IO, - "Failed to check if save file exists, error: \"%s\"\n", - ec.message().c_str()); - return SaveFailed; - } + 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, ec) != SaveFileSize) { - if (ec) { - ImpLog(LL_Error, LC_IO, "Failed to get save file size, error: \"%s\"\n", - ec.message().c_str()); - return SaveFailed; - } + 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; } - using PermsFlag = std::filesystem::perms; - auto checkPermsBit = [](PermsFlag perms, PermsFlag flag) { + auto checkPermsBit = [](Io::FilePermissionsFlags perms, + Io::FilePermissionsFlags flag) { return to_underlying(perms) & to_underlying(flag); }; - if (auto perms = std::filesystem::status(savePath, ec).permissions(); - (!checkPermsBit(perms, PermsFlag::owner_read) || - !checkPermsBit(perms, PermsFlag::owner_write))) { - if (ec) { - ImpLog(LL_Error, LC_IO, - "Failed to get save file permissions, error: \"%s\"\n", - ec.message().c_str()); - return SaveFailed; - } + 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; diff --git a/src/io/filemeta.cpp b/src/io/filemeta.cpp new file mode 100644 index 00000000..ab44f6da --- /dev/null +++ b/src/io/filemeta.cpp @@ -0,0 +1,61 @@ + + +#include "filemeta.h" +#include "../log.h" +#include + +namespace Impacto { +namespace Io { + +inline 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(result); +} + +inline 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; +} + +inline 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; +} + +inline 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 \ No newline at end of file diff --git a/src/io/filemeta.h b/src/io/filemeta.h index 6245d416..d1ae400c 100644 --- a/src/io/filemeta.h +++ b/src/io/filemeta.h @@ -2,7 +2,8 @@ #include #include - +#include +#include "io.h" namespace Impacto { namespace Io { @@ -14,5 +15,13 @@ struct FileMeta { int64_t Size = 0; }; +using FilePermissionsFlags = std::filesystem::perms; + +int64_t GetFileSize(std::string const& path); +IoError PathExists(std::string const& path); +int8_t CreateDirectories(std::string const& path); +IoError GetFilePermissions(std::string const& path, + FilePermissionsFlags& flags); + } // namespace Io } // namespace Impacto \ No newline at end of file diff --git a/src/io/physicalfilestream.cpp b/src/io/physicalfilestream.cpp index 0194bab4..5212d5d2 100644 --- a/src/io/physicalfilestream.cpp +++ b/src/io/physicalfilestream.cpp @@ -10,11 +10,11 @@ namespace Io { std::ios_base::openmode PhysicalFileStream::PrepareFileOpenMode( CreateFlags flags, std::error_code& ec) { std::ios_base::openmode mode = std::ios::binary; - bool fileExists = std::filesystem::exists(SourceFileName, ec); + IoError fileExists = PathExists(SourceFileName); if (ec) { ImpLog(LL_Error, LC_IO, "Failed to check whether file exists \"%s\", error: \"%s\"\n", - SourceFileName.generic_string().c_str(), ec.message().c_str()); + SourceFileName.c_str(), ec.message().c_str()); return {}; } @@ -24,7 +24,7 @@ std::ios_base::openmode PhysicalFileStream::PrepareFileOpenMode( if (writeNoExistNoCreate || readNoExist) { ec = std::make_error_code(std::errc::no_such_file_or_directory); ImpLog(LL_Error, LC_IO, "Failed to open stream \"%s\", error: \"%s\"\n", - SourceFileName.generic_string().c_str(), ec.message().c_str()); + SourceFileName.c_str(), ec.message().c_str()); return {}; } @@ -55,11 +55,9 @@ std::ios_base::openmode PhysicalFileStream::PrepareFileOpenMode( if (flags & APPEND) mode |= std::ios::app; if (truncFlag) mode |= std::ios::trunc; if (flags & CREATE_DIRS) { - std::filesystem::create_directories(SourceFileName.parent_path(), ec); - if (ec) { - ImpLog(LL_Error, LC_IO, - "Failed to create directories for file \"%s\", error: \"%s\"\n", - SourceFileName.generic_string().c_str(), ec.message().c_str()); + auto result = Io::CreateDirectories(SourceFileName); + if (result == IoError_Fail) { + ErrorCode = std::make_error_code(std::errc::io_error); return {}; } } @@ -68,7 +66,6 @@ std::ios_base::openmode PhysicalFileStream::PrepareFileOpenMode( IoError PhysicalFileStream::Create(std::string const& fileName, Stream** out, CreateFlags flags) { - std::error_code ec; PhysicalFileStream* result = new PhysicalFileStream(fileName, flags); if (result->ErrorCode) { ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n", @@ -84,10 +81,8 @@ IoError PhysicalFileStream::Create(std::string const& fileName, Stream** out, 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()); + result->Meta.Size = GetFileSize(result->SourceFileName); + if (result->Meta.Size == IoError_Fail) { delete result; return IoError_Fail; } @@ -106,7 +101,7 @@ int64_t PhysicalFileStream::Read(void* buffer, int64_t sz) { FileStream.read((char*)buffer, bytesToRead); if (!FileStream) { ImpLog(LL_Error, LC_IO, "Read failed for file \"%s\" with error: \"%s\"\n", - SourceFileName.string().c_str(), + SourceFileName.c_str(), std::generic_category().message(errno).c_str()); FileStream.clear(FileStream.rdstate() & ~std::ios::failbit & ~std::ios::eofbit); // Clear only failbit and eofbit @@ -141,7 +136,7 @@ int64_t PhysicalFileStream::Seek(int64_t offset, int origin) { FileStream.seekg(absPos, std::ios::beg); if (!FileStream && !FileStream.eof()) { ImpLog(LL_Error, LC_IO, "Seek failed for file \"%s\" with error: \"%s\"\n", - SourceFileName.string().c_str(), + SourceFileName.c_str(), std::generic_category().message(errno).c_str()); FileStream.clear(FileStream.rdstate() & ~std::ios::failbit & ~std::ios::eofbit); // Clear only failbit and eofbit @@ -156,8 +151,7 @@ IoError PhysicalFileStream::Duplicate(Stream** outStream) { std::error_code ec; if (result->ErrorCode) { ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n", - SourceFileName.string().c_str(), - result->ErrorCode.message().c_str()); + SourceFileName.c_str(), result->ErrorCode.message().c_str()); delete result; return result->ErrorCode == std::errc::no_such_file_or_directory ? IoError_NotFound @@ -165,21 +159,19 @@ IoError PhysicalFileStream::Duplicate(Stream** outStream) { } if (!result->FileStream) { ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n", - SourceFileName.string().c_str(), + SourceFileName.c_str(), std::generic_category().message(errno).c_str()); delete result; return IoError_Fail; } - 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()); + result->Meta.Size = GetFileSize(result->SourceFileName); + if (result->Meta.Size == IoError_Fail) { 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(), + SourceFileName.c_str(), std::generic_category().message(errno).c_str()); delete result; return IoError_Fail; @@ -195,7 +187,7 @@ int64_t PhysicalFileStream::Write(void* buffer, int64_t sz, int cnt) { 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(), + SourceFileName.c_str(), std::generic_category().message(errno).c_str()); FileStream.clear(FileStream.rdstate() & ~std::ios::failbit & ~std::ios::eofbit); // Clear only failbit and eofbit diff --git a/src/io/physicalfilestream.h b/src/io/physicalfilestream.h index 42bdddb7..90aeb594 100644 --- a/src/io/physicalfilestream.h +++ b/src/io/physicalfilestream.h @@ -2,9 +2,7 @@ #include "stream.h" #include -#include #include -#include "buffering.h" namespace Impacto { namespace Io { @@ -31,14 +29,14 @@ class PhysicalFileStream : public Stream { std::ios_base::openmode PrepareFileOpenMode(CreateFlags flags, std::error_code& ec); - PhysicalFileStream(std::filesystem::path filePath, CreateFlags flags) + PhysicalFileStream(std::string filePath, CreateFlags flags) : Flags(flags), SourceFileName(std::move(filePath)), FileStream(SourceFileName, PrepareFileOpenMode(flags, ErrorCode)) { - Meta.FileName = SourceFileName.string(); + Meta.FileName = SourceFileName; } - PhysicalFileStream(std::filesystem::path filePath) + PhysicalFileStream(std::string filePath) : PhysicalFileStream(std::move(filePath), CreateFlagsMode::READ) {} PhysicalFileStream(PhysicalFileStream const& other) @@ -46,11 +44,11 @@ class PhysicalFileStream : public Stream { SourceFileName(other.SourceFileName), FileStream(other.SourceFileName, PrepareFileOpenMode(Flags, ErrorCode)) { - Meta.FileName = SourceFileName.string(); + Meta.FileName = SourceFileName; } std::error_code ErrorCode; CreateFlags Flags; - std::filesystem::path SourceFileName; + std::string SourceFileName; std::fstream FileStream; }; From 6783067bd80de31dd3fab2ca98ebc645c0d6ef15 Mon Sep 17 00:00:00 2001 From: Dextinfire <> Date: Sat, 5 Oct 2024 10:13:54 -0700 Subject: [PATCH 06/10] Use IoError in place of std::error_code in physical file stream --- src/io/physicalfilestream.cpp | 36 ++++++++++++++++------------------- src/io/physicalfilestream.h | 11 ++++------- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/io/physicalfilestream.cpp b/src/io/physicalfilestream.cpp index 5212d5d2..6a0dbda5 100644 --- a/src/io/physicalfilestream.cpp +++ b/src/io/physicalfilestream.cpp @@ -8,13 +8,11 @@ namespace Impacto { namespace Io { std::ios_base::openmode PhysicalFileStream::PrepareFileOpenMode( - CreateFlags flags, std::error_code& ec) { + CreateFlags flags) { std::ios_base::openmode mode = std::ios::binary; IoError fileExists = PathExists(SourceFileName); - if (ec) { - ImpLog(LL_Error, LC_IO, - "Failed to check whether file exists \"%s\", error: \"%s\"\n", - SourceFileName.c_str(), ec.message().c_str()); + if (fileExists == IoError_Fail) { + ErrorCode = IoError_Fail; return {}; } @@ -22,9 +20,11 @@ std::ios_base::openmode PhysicalFileStream::PrepareFileOpenMode( (flags & WRITE) && !fileExists && !(flags & CREATE_IF_NOT_EXISTS); bool readNoExist = !(flags & WRITE) && (flags & READ) && !fileExists; if (writeNoExistNoCreate || readNoExist) { - ec = std::make_error_code(std::errc::no_such_file_or_directory); + ErrorCode = IoError_NotFound; + std::string errMsg = + std::make_error_code(std::errc::no_such_file_or_directory).message(); ImpLog(LL_Error, LC_IO, "Failed to open stream \"%s\", error: \"%s\"\n", - SourceFileName.c_str(), ec.message().c_str()); + SourceFileName.c_str(), errMsg.c_str()); return {}; } @@ -57,23 +57,21 @@ std::ios_base::openmode PhysicalFileStream::PrepareFileOpenMode( if (flags & CREATE_DIRS) { auto result = Io::CreateDirectories(SourceFileName); if (result == IoError_Fail) { - ErrorCode = std::make_error_code(std::errc::io_error); + ErrorCode = IoError_Fail; return {}; } } + ErrorCode = IoError_OK; return mode; } IoError PhysicalFileStream::Create(std::string const& fileName, Stream** out, CreateFlags flags) { PhysicalFileStream* result = new PhysicalFileStream(fileName, flags); - if (result->ErrorCode) { - ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n", - fileName.c_str(), result->ErrorCode.message().c_str()); + if (result->ErrorCode != IoError_OK) { + ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\"\n", fileName.c_str()); delete result; - return result->ErrorCode == std::errc::no_such_file_or_directory - ? IoError_NotFound - : IoError_Fail; + return result->ErrorCode; } if (!result->FileStream) { ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n", @@ -149,13 +147,11 @@ int64_t PhysicalFileStream::Seek(int64_t offset, int origin) { IoError PhysicalFileStream::Duplicate(Stream** outStream) { PhysicalFileStream* result = new PhysicalFileStream(*this); std::error_code ec; - if (result->ErrorCode) { - ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n", - SourceFileName.c_str(), result->ErrorCode.message().c_str()); + if (result->ErrorCode != IoError_OK) { + ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\"\n", + SourceFileName.c_str()); delete result; - return result->ErrorCode == std::errc::no_such_file_or_directory - ? IoError_NotFound - : IoError_Fail; + return result->ErrorCode; } if (!result->FileStream) { ImpLog(LL_Error, LC_IO, "Failed to open file \"%s\", error: \"%s\"\n", diff --git a/src/io/physicalfilestream.h b/src/io/physicalfilestream.h index 90aeb594..898905d3 100644 --- a/src/io/physicalfilestream.h +++ b/src/io/physicalfilestream.h @@ -2,7 +2,6 @@ #include "stream.h" #include -#include namespace Impacto { namespace Io { @@ -26,13 +25,12 @@ class PhysicalFileStream : public Stream { int64_t Write(void* buffer, int64_t sz, int cnt = 1) override; protected: - std::ios_base::openmode PrepareFileOpenMode(CreateFlags flags, - std::error_code& ec); + std::ios_base::openmode PrepareFileOpenMode(CreateFlags flags); PhysicalFileStream(std::string filePath, CreateFlags flags) : Flags(flags), SourceFileName(std::move(filePath)), - FileStream(SourceFileName, PrepareFileOpenMode(flags, ErrorCode)) { + FileStream(SourceFileName, PrepareFileOpenMode(flags)) { Meta.FileName = SourceFileName; } @@ -42,11 +40,10 @@ class PhysicalFileStream : public Stream { PhysicalFileStream(PhysicalFileStream const& other) : Flags(other.Flags), SourceFileName(other.SourceFileName), - FileStream(other.SourceFileName, - PrepareFileOpenMode(Flags, ErrorCode)) { + FileStream(other.SourceFileName, PrepareFileOpenMode(Flags)) { Meta.FileName = SourceFileName; } - std::error_code ErrorCode; + IoError ErrorCode = IoError_OK; CreateFlags Flags; std::string SourceFileName; std::fstream FileStream; From 3fecc24436453ecb3e6c963fe3959bfa0025a405 Mon Sep 17 00:00:00 2001 From: Dextinfire <> Date: Sat, 5 Oct 2024 10:15:54 -0700 Subject: [PATCH 07/10] no inline --- src/io/filemeta.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/io/filemeta.cpp b/src/io/filemeta.cpp index ab44f6da..fe7741b4 100644 --- a/src/io/filemeta.cpp +++ b/src/io/filemeta.cpp @@ -7,7 +7,7 @@ namespace Impacto { namespace Io { -inline int64_t GetFileSize(std::string const& path) { +int64_t GetFileSize(std::string const& path) { std::error_code ec; uintmax_t result = std::filesystem::file_size(path, ec); if (ec) { @@ -20,7 +20,7 @@ inline int64_t GetFileSize(std::string const& path) { return static_cast(result); } -inline IoError PathExists(std::string const& path) { +IoError PathExists(std::string const& path) { std::error_code ec; bool result = std::filesystem::exists(path, ec); if (ec) { @@ -32,7 +32,7 @@ inline IoError PathExists(std::string const& path) { return result == false ? IoError_NotFound : IoError_OK; } -inline int8_t CreateDirectories(std::string const& path) { +int8_t CreateDirectories(std::string const& path) { std::error_code ec; bool result = std::filesystem::create_directories(path, ec); if (ec) { @@ -44,8 +44,8 @@ inline int8_t CreateDirectories(std::string const& path) { return result; } -inline IoError GetFilePermissions(std::string const& path, - FilePermissionsFlags& flags) { +IoError GetFilePermissions(std::string const& path, + FilePermissionsFlags& flags) { std::error_code ec; flags = std::filesystem::status(path, ec).permissions(); if (ec) { From f56035a2ce220ee454c7aa1b569da31b19649aed Mon Sep 17 00:00:00 2001 From: Dextinfire <> Date: Sat, 5 Oct 2024 10:20:24 -0700 Subject: [PATCH 08/10] Use bit mask instead for writeprotect check --- src/data/savesystem.h | 4 +++- src/games/cclcc/savesystem.cpp | 2 +- src/games/chlcc/savesystem.cpp | 2 +- src/games/mo6tw/savesystem.cpp | 2 +- src/ui/widgets/cclcc/saveentrybutton.cpp | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/data/savesystem.h b/src/data/savesystem.h index f0d75f7c..1d1b1950 100644 --- a/src/data/savesystem.h +++ b/src/data/savesystem.h @@ -12,6 +12,8 @@ namespace SaveSystem { BETTER_ENUM(SaveDataType, int, None, CHLCC, CCLCC, MO6TW) +enum SaveFlagsMode { WriteProtect = 1 }; + enum SaveError { SaveOK = 0, SaveNotFound = 2, @@ -99,7 +101,7 @@ class SaveSystemBase { if (QuickSaveEntries[i]->Status == 0) return i; } for (int i = 0; i < MaxSaveEntries; i++) { - if (GetSaveFlags(SaveQuick, i) != 1) return i; + if (!(GetSaveFlags(SaveQuick, i) & WriteProtect)) return i; } return -1; } diff --git a/src/games/cclcc/savesystem.cpp b/src/games/cclcc/savesystem.cpp index 2e604fb1..fbe132b4 100644 --- a/src/games/cclcc/savesystem.cpp +++ b/src/games/cclcc/savesystem.cpp @@ -265,7 +265,7 @@ void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id, break; } - if (entry != 0 && GetSaveFlags(type, id) != 1) { + if (entry != 0 && !(GetSaveFlags(type, id) & WriteProtect)) { Renderer->FreeTexture(entry->SaveThumbnail.Sheet.Texture); *entry = *WorkingSaveEntry; if (type == SaveQuick) { diff --git a/src/games/chlcc/savesystem.cpp b/src/games/chlcc/savesystem.cpp index 93f1ebba..fe904d92 100644 --- a/src/games/chlcc/savesystem.cpp +++ b/src/games/chlcc/savesystem.cpp @@ -145,7 +145,7 @@ void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id, } if (WorkingSaveEntry != 0) { - if (entry != 0 && GetSaveFlags(type, id) != 1) { + if (entry != 0 && !(GetSaveFlags(type, id) & WriteProtect)) { *entry = *WorkingSaveEntry; if (type == SaveQuick) { entry->SaveType = autoSaveType; diff --git a/src/games/mo6tw/savesystem.cpp b/src/games/mo6tw/savesystem.cpp index 60e9b943..ef8b7d6b 100644 --- a/src/games/mo6tw/savesystem.cpp +++ b/src/games/mo6tw/savesystem.cpp @@ -203,7 +203,7 @@ void SaveSystem::FlushWorkingSaveEntry(SaveType type, int id, } if (WorkingSaveEntry != 0) { - if (entry != 0 && GetSaveFlags(type, id) != 1) { + if (entry != 0 && !(GetSaveFlags(type, id) & WriteProtect)) { Renderer->FreeTexture(entry->SaveThumbnail.Sheet.Texture); if (type == SaveQuick) { entry->SaveType = autoSaveType; diff --git a/src/ui/widgets/cclcc/saveentrybutton.cpp b/src/ui/widgets/cclcc/saveentrybutton.cpp index db8582cb..557e7e0d 100644 --- a/src/ui/widgets/cclcc/saveentrybutton.cpp +++ b/src/ui/widgets/cclcc/saveentrybutton.cpp @@ -155,7 +155,7 @@ void SaveEntryButton::RefreshSaveDateText() { // TODO: Make this only refresh when saved void SaveEntryButton::Update(float dt) { SaveStatus = SaveSystem::GetSaveStatus(Type, Id); - IsLocked = SaveSystem::GetSaveFlags(Type, Id) == 1; + IsLocked = SaveSystem::GetSaveFlags(Type, Id) & WriteProtect; if (SaveStatus == 1) { auto strIndex = (SaveSystem::GetSaveTitle(Type, Id) * 2); if (strIndex > 40) { From 91444b07586d52f038d9afd227d3e74459f71f31 Mon Sep 17 00:00:00 2001 From: Dextinfire <> Date: Sat, 5 Oct 2024 10:32:27 -0700 Subject: [PATCH 09/10] Fix the exists check --- src/io/physicalfilestream.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/io/physicalfilestream.cpp b/src/io/physicalfilestream.cpp index 6a0dbda5..823bc8c1 100644 --- a/src/io/physicalfilestream.cpp +++ b/src/io/physicalfilestream.cpp @@ -16,9 +16,11 @@ std::ios_base::openmode PhysicalFileStream::PrepareFileOpenMode( return {}; } - bool writeNoExistNoCreate = - (flags & WRITE) && !fileExists && !(flags & CREATE_IF_NOT_EXISTS); - bool readNoExist = !(flags & WRITE) && (flags & READ) && !fileExists; + bool writeNoExistNoCreate = (flags & WRITE) && + (fileExists == IoError_NotFound) && + !(flags & CREATE_IF_NOT_EXISTS); + bool readNoExist = + !(flags & WRITE) && (flags & READ) && (fileExists == IoError_NotFound); if (writeNoExistNoCreate || readNoExist) { ErrorCode = IoError_NotFound; std::string errMsg = From 80a1b0172add993aa5edc93f29a7bf189e9a7d84 Mon Sep 17 00:00:00 2001 From: Dextinfire <> Date: Sat, 5 Oct 2024 11:08:43 -0700 Subject: [PATCH 10/10] Add todo and remove whitespace --- src/io/filemeta.cpp | 2 -- src/io/filemeta.h | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/io/filemeta.cpp b/src/io/filemeta.cpp index fe7741b4..501d2f44 100644 --- a/src/io/filemeta.cpp +++ b/src/io/filemeta.cpp @@ -1,5 +1,3 @@ - - #include "filemeta.h" #include "../log.h" #include diff --git a/src/io/filemeta.h b/src/io/filemeta.h index d1ae400c..c02f40ab 100644 --- a/src/io/filemeta.h +++ b/src/io/filemeta.h @@ -15,6 +15,7 @@ struct FileMeta { int64_t Size = 0; }; +// TODO: use our own perms enum using FilePermissionsFlags = std::filesystem::perms; int64_t GetFileSize(std::string const& path);