From e62e98a6cf6364d5dba5e90dd7b1ef2c96df2ce6 Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Wed, 20 Dec 2023 00:04:23 +0300 Subject: [PATCH 1/8] Common: improved Stream::HasErrors(), renamed to GetError() Renamed HasErrors to GetError. Require this method to clear any error flags after reading them, so to actually make this function practical. --- Common/gfx/bitmap.cpp | 2 +- Common/util/filestream.cpp | 8 ++++++-- Common/util/filestream.h | 4 +++- Common/util/stream.h | 5 +++-- Engine/ac/file.cpp | 2 +- Engine/ac/global_file.cpp | 4 ++-- 6 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Common/gfx/bitmap.cpp b/Common/gfx/bitmap.cpp index e731e8b3901..6acba992e6a 100644 --- a/Common/gfx/bitmap.cpp +++ b/Common/gfx/bitmap.cpp @@ -1273,7 +1273,7 @@ bool SaveToFile(Bitmap* bmp, const char *filename, const RGB *pal) String ext = Path::GetFileExtension(filename); SaveBitmap(ext, out.get(), bmp, pal); - return out->HasErrors(); + return out->GetError(); // FIXME: should return a result from SaveBitmap --> SaveFmt } } // namespace BitmapHelper diff --git a/Common/util/filestream.cpp b/Common/util/filestream.cpp index 600fcaf4d77..43170e0a24a 100644 --- a/Common/util/filestream.cpp +++ b/Common/util/filestream.cpp @@ -44,9 +44,13 @@ FileStream::~FileStream() Close(); } -bool FileStream::HasErrors() const +bool FileStream::GetError() const { - return IsValid() && ferror(_file) != 0; + if (!_file) + return false; + bool err = ferror(_file) != 0; + clearerr(_file); + return err; } void FileStream::Close() diff --git a/Common/util/filestream.h b/Common/util/filestream.h index 6296e85b29b..d053726b071 100644 --- a/Common/util/filestream.h +++ b/Common/util/filestream.h @@ -61,7 +61,9 @@ class FileStream : public DataStream FileWorkMode GetWorkMode() const { return _workMode; } - bool HasErrors() const override; + // Tells if there were errors during previous io operation(s); + // the call to GetError() *resets* the error record. + bool GetError() const override; void Close() override; bool Flush() override; diff --git a/Common/util/stream.h b/Common/util/stream.h index eb1e7a51db7..d9c37608ad4 100644 --- a/Common/util/stream.h +++ b/Common/util/stream.h @@ -87,8 +87,9 @@ class Stream : public IStream //----------------------------------------------------- virtual bool IsValid() const = 0; - // Tells if the stream has errors - virtual bool HasErrors() const { return false; } + // Tells if there were errors during previous io operation(s); + // the call to GetError() *resets* the error record. + virtual bool GetError() const { return false; } virtual bool EOS() const = 0; virtual soff_t GetLength() const = 0; virtual soff_t GetPosition() const = 0; diff --git a/Engine/ac/file.cpp b/Engine/ac/file.cpp index 8ab78cce375..b61b4afc7f9 100644 --- a/Engine/ac/file.cpp +++ b/Engine/ac/file.cpp @@ -611,7 +611,7 @@ static int ags_pf_feof(void *userdata) static int ags_pf_ferror(void *userdata) { - return ((AGS_PACKFILE_OBJ*)userdata)->stream->HasErrors() ? 1 : 0; + return ((AGS_PACKFILE_OBJ*)userdata)->stream->GetError() ? 1 : 0; } // Custom PACKFILE callback table diff --git a/Engine/ac/global_file.cpp b/Engine/ac/global_file.cpp index bba30f300a6..af900a38d38 100644 --- a/Engine/ac/global_file.cpp +++ b/Engine/ac/global_file.cpp @@ -95,7 +95,7 @@ int FileIsEOF (int32_t handle) { return 1; // TODO: stream errors - if (stream->HasErrors()) + if (stream->GetError()) return 1; if (stream->GetPosition () >= stream->GetLength()) @@ -106,7 +106,7 @@ int FileIsError(int32_t handle) { Stream *stream = get_file_stream(handle, "FileIsError"); // TODO: stream errors - if (stream->HasErrors()) + if (stream->GetError()) return 1; return 0; From c68e99a3c15df285d46dc4d3c8fc883ddc91859d Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Tue, 19 Dec 2023 03:42:20 +0300 Subject: [PATCH 2/8] Common: implemented StreamMode and GetMode() method for checking stream 1. Merged StreamWorkMode and FileWorkMode into StreamMode enum. 2. Introduced GetMode() method, which reports open stream's capabilities. It replaces virtual methods for IsValid, CanRead, CanWrite and CanSeek (these are kept as simpler helper methods). 3. Stream implementations should report full flag combinations, describing their capabilities, including Seeking. --- Common/core/assetmanager.cpp | 6 ++-- Common/gfx/bitmap.cpp | 4 +-- Common/test/stream_test.cpp | 34 +++++++++--------- Common/util/bufferedstream.cpp | 8 ++--- Common/util/bufferedstream.h | 4 +-- Common/util/file.cpp | 43 ++++++++++++----------- Common/util/file.h | 25 ++++++------- Common/util/filestream.cpp | 43 ++++++++--------------- Common/util/filestream.h | 27 ++++++-------- Common/util/memorystream.cpp | 56 +++++++++--------------------- Common/util/memorystream.h | 16 +++------ Common/util/stream.h | 32 +++++++++++++---- Engine/ac/dynobj/scriptfile.cpp | 6 ++-- Engine/ac/dynobj/scriptfile.h | 3 +- Engine/ac/file.cpp | 4 +-- Engine/ac/global_dynamicsprite.cpp | 2 +- Engine/ac/global_file.cpp | 4 +-- Engine/ac/global_file.h | 2 +- Engine/ac/global_game.cpp | 2 +- Engine/ac/path_helper.h | 3 +- Engine/debug/logfile.cpp | 4 +-- Engine/plugin/agsplugin.cpp | 2 +- Engine/script/cc_instance.cpp | 2 +- 23 files changed, 151 insertions(+), 181 deletions(-) diff --git a/Common/core/assetmanager.cpp b/Common/core/assetmanager.cpp index 9a45533500f..31d2ec34e34 100644 --- a/Common/core/assetmanager.cpp +++ b/Common/core/assetmanager.cpp @@ -55,7 +55,7 @@ bool SortLibsPriorityLib(const AssetLibInfo *lib1, const AssetLibInfo *lib2) /* static */ bool AssetManager::IsDataFile(const String &data_file) { - Stream *in = File::OpenFileCI(data_file, Common::kFile_Open, Common::kFile_Read); + Stream *in = File::OpenFileCI(data_file, Common::kFile_Open, Common::kStream_Read); if (in) { MFLUtil::MFLError err = MFLUtil::TestIsMFL(in, true); @@ -67,7 +67,7 @@ bool SortLibsPriorityLib(const AssetLibInfo *lib1, const AssetLibInfo *lib2) /* static */ AssetError AssetManager::ReadDataFileTOC(const String &data_file, AssetLibInfo &lib) { - Stream *in = File::OpenFileCI(data_file, Common::kFile_Open, Common::kFile_Read); + Stream *in = File::OpenFileCI(data_file, Common::kFile_Open, Common::kStream_Read); if (in) { MFLUtil::MFLError err = MFLUtil::ReadHeader(lib, in); @@ -228,7 +228,7 @@ AssetError AssetManager::RegisterAssetLib(const String &path, AssetLibEx *&out_l // ...else try open a data library else { - Stream *in = File::OpenFileCI(path, Common::kFile_Open, Common::kFile_Read); + Stream *in = File::OpenFileCI(path, Common::kFile_Open, Common::kStream_Read); if (!in) return kAssetErrNoLibFile; // can't be opened, return error code diff --git a/Common/gfx/bitmap.cpp b/Common/gfx/bitmap.cpp index 6acba992e6a..f63c839d936 100644 --- a/Common/gfx/bitmap.cpp +++ b/Common/gfx/bitmap.cpp @@ -89,7 +89,7 @@ Bitmap *CreateBitmapCopy(Bitmap *src, int color_depth) Bitmap *LoadFromFile(const char *filename) { std::unique_ptr in ( - File::OpenFile(filename, FileOpenMode::kFile_Open, FileWorkMode::kFile_Read)); + File::OpenFile(filename, FileOpenMode::kFile_Open, StreamMode::kStream_Read)); if(!in) return nullptr; @@ -1267,7 +1267,7 @@ void SaveBitmap(const String& ext, Stream *out, const Bitmap *bmp, const RGB *pa bool SaveToFile(Bitmap* bmp, const char *filename, const RGB *pal) { std::unique_ptr out ( - File::OpenFile(filename, FileOpenMode::kFile_CreateAlways, FileWorkMode::kFile_Write)); + File::OpenFile(filename, FileOpenMode::kFile_CreateAlways, StreamMode::kStream_Write)); if (!out) return false; diff --git a/Common/test/stream_test.cpp b/Common/test/stream_test.cpp index a9e6dcf4070..1f0240f7816 100644 --- a/Common/test/stream_test.cpp +++ b/Common/test/stream_test.cpp @@ -210,7 +210,7 @@ class FileBasedTest : public ::testing::Test { TEST_F(FileBasedTest, BufferedStreamRead) { //------------------------------------------------------------------------- // Write data into the temp file - FileStream out(DummyFile, kFile_CreateAlways, kFile_Write); + FileStream out(DummyFile, kFile_CreateAlways, kStream_Write); out.WriteInt32(0); out.WriteInt32(1); out.WriteInt32(2); @@ -231,7 +231,7 @@ TEST_F(FileBasedTest, BufferedStreamRead) { //------------------------------------------------------------------------- // Read data back - BufferedStream in(DummyFile, kFile_Open, kFile_Read); + BufferedStream in(DummyFile, kFile_Open, kStream_Read); ASSERT_TRUE(in.CanRead()); ASSERT_EQ(in.GetLength(), file_len); ASSERT_EQ(in.ReadInt32(), 0); @@ -255,7 +255,7 @@ TEST_F(FileBasedTest, BufferedStreamRead) { in.Close(); // Test seeks - BufferedStream in2(DummyFile, kFile_Open, kFile_Read); + BufferedStream in2(DummyFile, kFile_Open, kStream_Read); ASSERT_TRUE(in2.CanRead()); ASSERT_TRUE(in2.CanSeek()); ASSERT_EQ(in2.GetLength(), file_len); @@ -279,7 +279,7 @@ TEST_F(FileBasedTest, BufferedStreamWrite1) { //------------------------------------------------------------------------- // Write data const soff_t file_len = sizeof(int32_t) * 10; - BufferedStream out(DummyFile, kFile_CreateAlways, kFile_Write); + BufferedStream out(DummyFile, kFile_CreateAlways, kStream_Write); ASSERT_TRUE(out.CanWrite()); out.WriteInt32(0); out.WriteInt32(1); @@ -296,7 +296,7 @@ TEST_F(FileBasedTest, BufferedStreamWrite1) { out.Close(); //------------------------------------------------------------------------- // Read data back - FileStream in(DummyFile, kFile_Open, kFile_Read); + FileStream in(DummyFile, kFile_Open, kStream_Read); ASSERT_TRUE(in.CanRead()); ASSERT_TRUE(in.CanSeek()); ASSERT_EQ(in.GetLength(), file_len); @@ -324,7 +324,7 @@ TEST_F(FileBasedTest, BufferedStreamWrite2) { //------------------------------------------------------------------------- // Write data const soff_t file_len = sizeof(int32_t) * 10 + fill_len; - BufferedStream out(DummyFile, kFile_CreateAlways, kFile_Write); + BufferedStream out(DummyFile, kFile_CreateAlways, kStream_Write); ASSERT_TRUE(out.CanWrite()); out.WriteInt32(0); out.WriteInt32(1); @@ -342,7 +342,7 @@ TEST_F(FileBasedTest, BufferedStreamWrite2) { out.Close(); //------------------------------------------------------------------------- // Read data back - FileStream in(DummyFile, kFile_Open, kFile_Read); + FileStream in(DummyFile, kFile_Open, kStream_Read); ASSERT_TRUE(in.CanRead()); ASSERT_TRUE(in.CanSeek()); ASSERT_EQ(in.GetLength(), file_len); @@ -368,7 +368,7 @@ TEST_F(FileBasedTest, BufferedStreamWrite3) { //------------------------------------------------------------------------- // Write data const soff_t file_len = sizeof(int32_t) * 10; - BufferedStream out(DummyFile, kFile_CreateAlways, kFile_Write); + BufferedStream out(DummyFile, kFile_CreateAlways, kStream_Write); ASSERT_TRUE(out.CanWrite()); ASSERT_TRUE(out.CanSeek()); out.WriteInt32(0); @@ -391,7 +391,7 @@ TEST_F(FileBasedTest, BufferedStreamWrite3) { out.Close(); //------------------------------------------------------------------------- // Read data back - FileStream in(DummyFile, kFile_Open, kFile_Read); + FileStream in(DummyFile, kFile_Open, kStream_Read); ASSERT_TRUE(in.CanRead()); ASSERT_EQ(in.GetLength(), file_len); ASSERT_EQ(in.ReadInt32(), 0); @@ -418,7 +418,7 @@ TEST_F(FileBasedTest, BufferedStreamWrite4) { //------------------------------------------------------------------------- // Write data const soff_t file_len = sizeof(int32_t) * 8 + fill_len; - BufferedStream out(DummyFile, kFile_CreateAlways, kFile_Write); + BufferedStream out(DummyFile, kFile_CreateAlways, kStream_Write); ASSERT_TRUE(out.CanWrite()); out.WriteInt32(0); out.WriteInt32(1); @@ -439,7 +439,7 @@ TEST_F(FileBasedTest, BufferedStreamWrite4) { out.Close(); //------------------------------------------------------------------------- // Read data back - FileStream in(DummyFile, kFile_Open, kFile_Read); + FileStream in(DummyFile, kFile_Open, kStream_Read); ASSERT_TRUE(in.CanRead()); ASSERT_TRUE(in.CanSeek()); ASSERT_EQ(in.GetLength(), file_len); @@ -466,7 +466,7 @@ TEST_F(FileBasedTest, BufferedStreamWrite5) { //------------------------------------------------------------------------- // Write data const soff_t file_len = sizeof(int32_t) * 7 + fill_len; - BufferedStream out(DummyFile, kFile_CreateAlways, kFile_Write); + BufferedStream out(DummyFile, kFile_CreateAlways, kStream_Write); ASSERT_TRUE(out.CanWrite()); out.WriteByteCount(0, fill_len); // fill to (nearly) force buffer flush out.WriteInt32(0); @@ -486,7 +486,7 @@ TEST_F(FileBasedTest, BufferedStreamWrite5) { out.Close(); //------------------------------------------------------------------------- // Read data back - FileStream in(DummyFile, kFile_Open, kFile_Read); + FileStream in(DummyFile, kFile_Open, kStream_Read); ASSERT_TRUE(in.CanRead()); ASSERT_TRUE(in.CanSeek()); ASSERT_EQ(in.GetLength(), file_len); @@ -507,7 +507,7 @@ TEST_F(FileBasedTest, BufferedStreamWrite5) { TEST_F(FileBasedTest, BufferedSectionStream) { //------------------------------------------------------------------------- // Write data into the temp file - FileStream out(DummyFile, kFile_CreateAlways, kFile_Write); + FileStream out(DummyFile, kFile_CreateAlways, kStream_Write); out.WriteInt32(0); out.WriteInt32(1); out.WriteInt32(2); @@ -530,7 +530,7 @@ TEST_F(FileBasedTest, BufferedSectionStream) { //------------------------------------------------------------------------- // Read data back from section 1 and test read limits - BufferedSectionStream in(DummyFile, section1_start, section1_end, kFile_Open, kFile_Read); + BufferedSectionStream in(DummyFile, section1_start, section1_end, kFile_Open, kStream_Read); ASSERT_TRUE(in.CanRead()); ASSERT_EQ(in.GetPosition(), 0); ASSERT_EQ(in.GetLength(), section1_end - section1_start); @@ -552,7 +552,7 @@ TEST_F(FileBasedTest, BufferedSectionStream) { // Test limits - reading large chunks: optimized by reading directly // into the provided user's buffer, without use of internal buffer - BufferedSectionStream in3(DummyFile, section1_start, section1_end, kFile_Open, kFile_Read); + BufferedSectionStream in3(DummyFile, section1_start, section1_end, kFile_Open, kStream_Read); const size_t try_read = 4 * sizeof(int32_t) + BufferedStream::BufferSize; const size_t must_read = 4 * sizeof(int32_t); char buf[try_read]; @@ -564,7 +564,7 @@ TEST_F(FileBasedTest, BufferedSectionStream) { in3.Close(); // Test seeks limited to section 1 - BufferedSectionStream in2(DummyFile, section1_start, section2_end, kFile_Open, kFile_Read); + BufferedSectionStream in2(DummyFile, section1_start, section2_end, kFile_Open, kStream_Read); ASSERT_TRUE(in2.CanRead()); ASSERT_TRUE(in2.CanSeek()); ASSERT_EQ(in2.GetPosition(), 0); diff --git a/Common/util/bufferedstream.cpp b/Common/util/bufferedstream.cpp index 1750aadf295..085722eeab7 100644 --- a/Common/util/bufferedstream.cpp +++ b/Common/util/bufferedstream.cpp @@ -30,7 +30,7 @@ namespace Common const size_t BufferedStream::BufferSize; BufferedStream::BufferedStream(const String &file_name, FileOpenMode open_mode, - FileWorkMode work_mode, DataEndianess stream_endianess) + StreamMode work_mode, DataEndianess stream_endianess) : FileStream(file_name, open_mode, work_mode, stream_endianess) { if (FileStream::Seek(0, kSeekEnd)) @@ -92,14 +92,14 @@ soff_t BufferedStream::GetPosition() const void BufferedStream::Close() { - if (GetWorkMode() == kFile_Write) + if (CanWrite()) FlushBuffer(_position); FileStream::Close(); } bool BufferedStream::Flush() { - if (GetWorkMode() == kFile_Write) + if (CanWrite()) FlushBuffer(_position); return FileStream::Flush(); } @@ -203,7 +203,7 @@ bool BufferedStream::Seek(soff_t offset, StreamSeek origin) //----------------------------------------------------------------------------- BufferedSectionStream::BufferedSectionStream(const String &file_name, soff_t start_pos, soff_t end_pos, - FileOpenMode open_mode, FileWorkMode work_mode, DataEndianess stream_endianess) + FileOpenMode open_mode, StreamMode work_mode, DataEndianess stream_endianess) : BufferedStream(file_name, open_mode, work_mode, stream_endianess) { assert(start_pos <= end_pos); diff --git a/Common/util/bufferedstream.h b/Common/util/bufferedstream.h index 5aadc139420..0bb9679b6ca 100644 --- a/Common/util/bufferedstream.h +++ b/Common/util/bufferedstream.h @@ -43,7 +43,7 @@ class BufferedStream : public FileStream // - could not determine the length of the stream // It is recommended to use File::OpenFile to safely construct this object. BufferedStream(const String &file_name, FileOpenMode open_mode, - FileWorkMode work_mode, DataEndianess stream_endianess = kLittleEndian); + StreamMode work_mode, DataEndianess stream_endianess = kLittleEndian); ~BufferedStream(); // Is end of stream @@ -84,7 +84,7 @@ class BufferedSectionStream : public BufferedStream { public: BufferedSectionStream(const String &file_name, soff_t start_pos, soff_t end_pos, - FileOpenMode open_mode, FileWorkMode work_mode, DataEndianess stream_endianess = kLittleEndian); + FileOpenMode open_mode, StreamMode work_mode, DataEndianess stream_endianess = kLittleEndian); }; } // namespace Common diff --git a/Common/util/file.cpp b/Common/util/file.cpp index 5baf5fa2785..ba064b82f3b 100644 --- a/Common/util/file.cpp +++ b/Common/util/file.cpp @@ -134,7 +134,7 @@ bool File::CopyFile(const String &src_path, const String &dst_path, bool overwri return ags_file_copy(src_path.GetCStr(), dst_path.GetCStr(), overwrite) == 0; } -bool File::GetFileModesFromCMode(const String &cmode, FileOpenMode &open_mode, FileWorkMode &work_mode) +bool File::GetFileModesFromCMode(const String &cmode, FileOpenMode &open_mode, StreamMode &work_mode) { // We do not test for 'b' and 't' here, because text mode reading/writing should be done with // the use of ITextReader and ITextWriter implementations. @@ -142,14 +142,14 @@ bool File::GetFileModesFromCMode(const String &cmode, FileOpenMode &open_mode, F bool read_base_mode = false; // Default mode is open/read for safety reasons open_mode = kFile_Open; - work_mode = kFile_Read; + work_mode = kStream_Read; for (size_t c = 0; c < cmode.GetLength(); ++c) { if (read_base_mode) { if (cmode[c] == '+') { - work_mode = kFile_ReadWrite; + work_mode = kStream_ReadWrite; } break; } @@ -158,19 +158,19 @@ bool File::GetFileModesFromCMode(const String &cmode, FileOpenMode &open_mode, F if (cmode[c] == 'r') { open_mode = kFile_Open; - work_mode = kFile_Read; + work_mode = kStream_Read; read_base_mode = true; } else if (cmode[c] == 'a') { open_mode = kFile_Create; - work_mode = kFile_Write; + work_mode = kStream_Write; read_base_mode = true; } else if (cmode[c] == 'w') { open_mode = kFile_CreateAlways; - work_mode = kFile_Write; + work_mode = kStream_Write; read_base_mode = true; } } @@ -178,35 +178,37 @@ bool File::GetFileModesFromCMode(const String &cmode, FileOpenMode &open_mode, F return read_base_mode; } -String File::GetCMode(FileOpenMode open_mode, FileWorkMode work_mode) +String File::GetCMode(FileOpenMode open_mode, StreamMode work_mode) { String mode; + // filter out only read/write flags + work_mode = static_cast(work_mode & kStream_ReadWrite); if (open_mode == kFile_Open) { - if (work_mode == kFile_Read) + if (work_mode == kStream_Read) mode.AppendChar('r'); - else if (work_mode == kFile_Write || work_mode == kFile_ReadWrite) + else if (work_mode == kStream_Write || work_mode == kStream_ReadWrite) mode.Append("r+"); } else if (open_mode == kFile_Create) { - if (work_mode == kFile_Write) + if (work_mode == kStream_Write) mode.AppendChar('a'); - else if (work_mode == kFile_Read || work_mode == kFile_ReadWrite) + else if (work_mode == kStream_Read || work_mode == kStream_ReadWrite) mode.Append("a+"); } else if (open_mode == kFile_CreateAlways) { - if (work_mode == kFile_Write) + if (work_mode == kStream_Write) mode.AppendChar('w'); - else if (work_mode == kFile_Read || work_mode == kFile_ReadWrite) + else if (work_mode == kStream_Read || work_mode == kStream_ReadWrite) mode.Append("w+"); } mode.AppendChar('b'); return mode; } -Stream *File::OpenFile(const String &filename, FileOpenMode open_mode, FileWorkMode work_mode) +Stream *File::OpenFile(const String &filename, FileOpenMode open_mode, StreamMode work_mode) { Stream *fs = nullptr; try { @@ -218,8 +220,9 @@ Stream *File::OpenFile(const String &filename, FileOpenMode open_mode, FileWorkM } catch(std::runtime_error) { fs = nullptr; #if AGS_PLATFORM_OS_ANDROID + // strictly for read-only streams: look into Android Assets too try { - if (work_mode == kFile_Read) // look into Android Assets too + if ((work_mode & kStream_Write) == 0) fs = new AAssetStream(filename, AASSET_MODE_RANDOM); if (fs != nullptr && !fs->IsValid()) { delete fs; @@ -236,17 +239,17 @@ Stream *File::OpenFile(const String &filename, FileOpenMode open_mode, FileWorkM Stream *File::OpenStdin() { - return FileStream::WrapHandle(stdin, kFile_Read, kDefaultSystemEndianess); + return FileStream::WrapHandle(stdin, kStream_Read, kDefaultSystemEndianess); } Stream *File::OpenStdout() { - return FileStream::WrapHandle(stdout, kFile_Write, kDefaultSystemEndianess); + return FileStream::WrapHandle(stdout, kStream_Write, kDefaultSystemEndianess); } Stream *File::OpenStderr() { - return FileStream::WrapHandle(stderr, kFile_Write, kDefaultSystemEndianess); + return FileStream::WrapHandle(stderr, kStream_Write, kDefaultSystemEndianess); } String File::FindFileCI(const String &base_dir, const String &file_name, @@ -365,7 +368,7 @@ String File::FindFileCI(const String &base_dir, const String &file_name, #endif } -Stream *File::OpenFileCI(const String &base_dir, const String &file_name, FileOpenMode open_mode, FileWorkMode work_mode) +Stream *File::OpenFileCI(const String &base_dir, const String &file_name, FileOpenMode open_mode, StreamMode work_mode) { #if !defined (AGS_CASE_SENSITIVE_FILESYSTEM) return File::OpenFile(Path::ConcatPaths(base_dir, file_name), open_mode, work_mode); @@ -383,7 +386,7 @@ Stream *File::OpenFileCI(const String &base_dir, const String &file_name, FileOp Stream *File::OpenFile(const String &filename, soff_t start_off, soff_t end_off) { try { - Stream *fs = new BufferedSectionStream(filename, start_off, end_off, kFile_Open, kFile_Read); + Stream *fs = new BufferedSectionStream(filename, start_off, end_off, kFile_Open, kStream_Read); if (fs != nullptr && !fs->IsValid()) { delete fs; return nullptr; diff --git a/Common/util/file.h b/Common/util/file.h index 852f5eef06a..70ed00327c5 100644 --- a/Common/util/file.h +++ b/Common/util/file.h @@ -19,6 +19,7 @@ #define __AGS_CN_UTIL__FILE_H #include "core/platform.h" +#include "util/stream.h" #include "util/string.h" namespace AGS @@ -31,18 +32,12 @@ class Stream; enum FileOpenMode { + kFile_None = 0, kFile_Open, // Open existing file kFile_Create, // Create new file, or open existing one kFile_CreateAlways // Always create a new file, replacing any existing one }; -enum FileWorkMode -{ - kFile_Read = 0x1, - kFile_Write = 0x2, - kFile_ReadWrite = kFile_Read | kFile_Write -}; - namespace File { // Tells if the given path is a directory @@ -68,29 +63,29 @@ namespace File bool CopyFile(const String &src_path, const String &dst_path, bool overwrite); // Sets FileOpenMode and FileWorkMode values corresponding to C-style file open mode string - bool GetFileModesFromCMode(const String &cmode, FileOpenMode &open_mode, FileWorkMode &work_mode); + bool GetFileModesFromCMode(const String &cmode, FileOpenMode &open_mode, StreamMode &work_mode); // Gets C-style file mode from FileOpenMode and FileWorkMode - String GetCMode(FileOpenMode open_mode, FileWorkMode work_mode); + String GetCMode(FileOpenMode open_mode, StreamMode work_mode); // Opens file in the given mode - Stream *OpenFile(const String &filename, FileOpenMode open_mode, FileWorkMode work_mode); + Stream *OpenFile(const String &filename, FileOpenMode open_mode, StreamMode work_mode); // Opens file for reading restricted to the arbitrary offset range Stream *OpenFile(const String &filename, soff_t start_off, soff_t end_off); // Convenience helpers // Create a totally new file, overwrite existing one inline Stream *CreateFile(const String &filename) { - return OpenFile(filename, kFile_CreateAlways, kFile_Write); + return OpenFile(filename, kFile_CreateAlways, kStream_Write); } // Open existing file for reading inline Stream *OpenFileRead(const String &filename) { - return OpenFile(filename, kFile_Open, kFile_Read); + return OpenFile(filename, kFile_Open, kStream_Read); } // Open existing file for writing (append) or create if it does not exist inline Stream *OpenFileWrite(const String &filename) { - return OpenFile(filename, kFile_Create, kFile_Write); + return OpenFile(filename, kFile_Create, kStream_Write); } // Opens stdin stream for reading Stream *OpenStdin(); @@ -122,10 +117,10 @@ namespace File // Case insensitive file open: looks up for the file using FindFileCI Stream *OpenFileCI(const String &base_dir, const String &file_name, FileOpenMode open_mode = kFile_Open, - FileWorkMode work_mode = kFile_Read); + StreamMode work_mode = kStream_Read); inline Stream *OpenFileCI(const String &file_name, FileOpenMode open_mode = kFile_Open, - FileWorkMode work_mode = kFile_Read) + StreamMode work_mode = kStream_Read) { return OpenFileCI("", file_name, open_mode, work_mode); } diff --git a/Common/util/filestream.cpp b/Common/util/filestream.cpp index 43170e0a24a..29c906e2588 100644 --- a/Common/util/filestream.cpp +++ b/Common/util/filestream.cpp @@ -20,7 +20,9 @@ namespace AGS namespace Common { -FileStream::FileStream(const String &file_name, FileOpenMode open_mode, FileWorkMode work_mode, +FileStream::FFileCloseNotify FileStream::FileCloseNotify = nullptr; + +FileStream::FileStream(const String &file_name, FileOpenMode open_mode, StreamMode work_mode, DataEndianess stream_endianess) : DataStream(stream_endianess) , _file(nullptr) @@ -30,13 +32,16 @@ FileStream::FileStream(const String &file_name, FileOpenMode open_mode, FileWork Open(file_name, open_mode, work_mode); } -FileStream::FileStream(FILE *file, bool own, FileWorkMode work_mode, DataEndianess stream_end) +FileStream::FileStream(FILE *file, bool own, StreamMode work_mode, DataEndianess stream_end) : DataStream(stream_end) , _file(file) , _ownHandle(own) - , _openMode(kFile_Open) - , _workMode(work_mode) { + if (_file) + { + _openMode = kFile_Open; + _workMode = static_cast(work_mode | kStream_Seek); + } } FileStream::~FileStream() @@ -55,7 +60,7 @@ bool FileStream::GetError() const void FileStream::Close() { - if (_ownHandle) + if (_file && _ownHandle) { fclose(_file); } @@ -68,6 +73,8 @@ void FileStream::Close() args.WorkMode = _workMode; FileCloseNotify(args); } + _openMode = kFile_None; + _workMode = kStream_None; } bool FileStream::Flush() @@ -75,11 +82,6 @@ bool FileStream::Flush() return fflush(_file) == 0; } -bool FileStream::IsValid() const -{ - return _file != nullptr; -} - bool FileStream::EOS() const { return feof(_file) != 0; @@ -99,21 +101,6 @@ soff_t FileStream::GetPosition() const return static_cast(ags_ftell(_file)); } -bool FileStream::CanRead() const -{ - return IsValid() && _workMode != kFile_Write; -} - -bool FileStream::CanWrite() const -{ - return IsValid() && _workMode != kFile_Read; -} - -bool FileStream::CanSeek() const -{ - return IsValid(); -} - size_t FileStream::Read(void *buffer, size_t size) { return fread(buffer, sizeof(uint8_t), size, _file); @@ -148,7 +135,7 @@ bool FileStream::Seek(soff_t offset, StreamSeek origin) return ags_fseek(_file, (file_off_t)offset, stdclib_origin) == 0; } -void FileStream::Open(const String &file_name, FileOpenMode open_mode, FileWorkMode work_mode) +void FileStream::Open(const String &file_name, FileOpenMode open_mode, StreamMode work_mode) { String mode = File::GetCMode(open_mode, work_mode); if (mode.IsEmpty()) @@ -158,9 +145,9 @@ void FileStream::Open(const String &file_name, FileOpenMode open_mode, FileWorkM throw std::runtime_error("Error opening file."); _ownHandle = true; _path = file_name; + _openMode = open_mode; + _workMode = static_cast(work_mode | kStream_Seek); } -FileStream::FFileCloseNotify FileStream::FileCloseNotify = nullptr; - } // namespace Common } // namespace AGS diff --git a/Common/util/filestream.h b/Common/util/filestream.h index d053726b071..da7d5f5dbb7 100644 --- a/Common/util/filestream.h +++ b/Common/util/filestream.h @@ -35,7 +35,7 @@ class FileStream : public DataStream struct CloseNotifyArgs { String Filepath; - FileWorkMode WorkMode; + StreamMode WorkMode; }; // definition of function called when file closes @@ -47,19 +47,19 @@ class FileStream : public DataStream // The constructor may raise std::runtime_error if // - there is an issue opening the file (does not exist, locked, permissions, etc) // - the open mode could not be determined - FileStream(const String &file_name, FileOpenMode open_mode, FileWorkMode work_mode, + FileStream(const String &file_name, FileOpenMode open_mode, StreamMode work_mode, DataEndianess stream_endianess = kLittleEndian); // Constructs a file stream over an open FILE handle; // Take an ownership over it and will close upon disposal - static FileStream *OwnHandle(FILE *file, FileWorkMode work_mode, DataEndianess stream_end = kLittleEndian) + static FileStream *OwnHandle(FILE *file, StreamMode work_mode, DataEndianess stream_end = kLittleEndian) { return new FileStream(file, true, work_mode, stream_end); } // Constructs a file stream over an open FILE handle; // does NOT take an ownership over it - static FileStream *WrapHandle(FILE *file, FileWorkMode work_mode, DataEndianess stream_end = kLittleEndian) + static FileStream *WrapHandle(FILE *file, StreamMode work_mode, DataEndianess stream_end = kLittleEndian) { return new FileStream(file, false, work_mode, stream_end); } ~FileStream() override; - FileWorkMode GetWorkMode() const { return _workMode; } + StreamMode GetMode() const override { return _workMode; } // Tells if there were errors during previous io operation(s); // the call to GetError() *resets* the error record. @@ -67,17 +67,12 @@ class FileStream : public DataStream void Close() override; bool Flush() override; - // Is stream valid (underlying data initialized properly) - bool IsValid() const override; // Is end of stream bool EOS() const override; // Total length of stream (if known) soff_t GetLength() const override; // Current position (if known) soff_t GetPosition() const override; - bool CanRead() const override; - bool CanWrite() const override; - bool CanSeek() const override; size_t Read(void *buffer, size_t size) override; int32_t ReadByte() override; @@ -87,13 +82,13 @@ class FileStream : public DataStream bool Seek(soff_t offset, StreamSeek origin) override; private: - FileStream(FILE *file, bool own, FileWorkMode work_mode, DataEndianess stream_end); - void Open(const String &file_name, FileOpenMode open_mode, FileWorkMode work_mode); + FileStream(FILE *file, bool own, StreamMode work_mode, DataEndianess stream_end); + void Open(const String &file_name, FileOpenMode open_mode, StreamMode work_mode); - FILE *_file; - bool _ownHandle; - const FileOpenMode _openMode; - const FileWorkMode _workMode; + FILE *_file = nullptr; + bool _ownHandle = false; + FileOpenMode _openMode = kFile_None; + StreamMode _workMode = kStream_None; }; } // namespace Common diff --git a/Common/util/memorystream.cpp b/Common/util/memorystream.cpp index 0f04e574b2a..0d315fddc31 100644 --- a/Common/util/memorystream.cpp +++ b/Common/util/memorystream.cpp @@ -25,30 +25,33 @@ MemoryStream::MemoryStream(const uint8_t *cbuf, size_t buf_sz, DataEndianess str , _cbuf(cbuf) , _buf_sz(buf_sz) , _len(buf_sz) - , _mode(kStream_Read) , _pos(0) , _buf(nullptr) { + if (cbuf) + _mode = static_cast(kStream_Read | kStream_Seek); } -MemoryStream::MemoryStream(uint8_t *buf, size_t buf_sz, StreamWorkMode mode, DataEndianess stream_endianess) +MemoryStream::MemoryStream(uint8_t *buf, size_t buf_sz, StreamMode mode, DataEndianess stream_endianess) : DataStream(stream_endianess) , _cbuf(nullptr) , _buf_sz(buf_sz) , _len(0) - , _mode(mode) , _pos(0) , _buf(nullptr) { - if (mode == kStream_Read) + if ((mode & kStream_Write) != 0) { - _cbuf = buf; - _len = buf_sz; + _buf = buf; } else { - _buf = buf; + _cbuf = buf; + _len = buf_sz; } + + if (buf) + _mode = static_cast(mode | kStream_Seek); } void MemoryStream::Close() @@ -58,6 +61,7 @@ void MemoryStream::Close() _buf_sz = 0; _len = 0; _pos = 0; + _mode = kStream_None; } bool MemoryStream::Flush() @@ -65,11 +69,6 @@ bool MemoryStream::Flush() return true; } -bool MemoryStream::IsValid() const -{ - return _cbuf != nullptr || _buf != nullptr; -} - bool MemoryStream::EOS() const { return _pos >= _len; @@ -85,21 +84,6 @@ soff_t MemoryStream::GetPosition() const return _pos; } -bool MemoryStream::CanRead() const -{ - return (_cbuf != nullptr) && (_mode == kStream_Read); -} - -bool MemoryStream::CanWrite() const -{ - return (_buf != nullptr) && (_mode == kStream_Write); -} - -bool MemoryStream::CanSeek() const -{ - return true; -} - size_t MemoryStream::Read(void *buffer, size_t size) { if (EOS()) { return 0; } @@ -119,7 +103,6 @@ int32_t MemoryStream::ReadByte() bool MemoryStream::Seek(soff_t offset, StreamSeek origin) { - if (!CanSeek()) { return false; } soff_t pos = 0; switch (origin) { @@ -159,13 +142,16 @@ VectorStream::VectorStream(const std::vector &cbuf, DataEndianess strea : MemoryStream(&cbuf.front(), cbuf.size(), stream_endianess) , _vec(nullptr) { + _mode = static_cast(kStream_Read | kStream_Seek); } -VectorStream::VectorStream(std::vector &buf, StreamWorkMode mode, DataEndianess stream_endianess) - : MemoryStream(((mode == kStream_Read) && (buf.size() > 0)) ? &buf.front() : nullptr, +VectorStream::VectorStream(std::vector &buf, StreamMode mode, DataEndianess stream_endianess) + : MemoryStream((((mode & kStream_ReadWrite) == kStream_Read) && (buf.size() > 0) ? + &buf.front() : nullptr), buf.size(), mode, stream_endianess) , _vec(&buf) { + _mode = static_cast(mode | kStream_Seek); } void VectorStream::Close() @@ -174,16 +160,6 @@ void VectorStream::Close() MemoryStream::Close(); } -bool VectorStream::CanRead() const -{ - return _mode == kStream_Read; -} - -bool VectorStream::CanWrite() const -{ - return _mode == kStream_Write; -} - size_t VectorStream::Write(const void *buffer, size_t size) { if (_pos + size > _len) diff --git a/Common/util/memorystream.h b/Common/util/memorystream.h index 25e5ab18a73..d54d4708ffa 100644 --- a/Common/util/memorystream.h +++ b/Common/util/memorystream.h @@ -44,23 +44,20 @@ class MemoryStream : public DataStream // Construct memory stream in the chosen mode over a given C-buffer; // neither reading nor writing will ever exceed buf_sz bytes; // buffer must persist in memory until the stream is closed. - MemoryStream(uint8_t *buf, size_t buf_sz, StreamWorkMode mode, DataEndianess stream_endianess = kLittleEndian); + MemoryStream(uint8_t *buf, size_t buf_sz, StreamMode mode, DataEndianess stream_endianess = kLittleEndian); ~MemoryStream() override = default; void Close() override; bool Flush() override; - // Is stream valid (underlying data initialized properly) - bool IsValid() const override; + StreamMode GetMode() const override { return _mode; } + bool GetError() const override { return false; } // Is end of stream bool EOS() const override; // Total length of stream (if known) soff_t GetLength() const override; // Current position (if known) soff_t GetPosition() const override; - bool CanRead() const override; - bool CanWrite() const override; - bool CanSeek() const override; size_t Read(void *buffer, size_t size) override; int32_t ReadByte() override; @@ -73,7 +70,7 @@ class MemoryStream : public DataStream const uint8_t *_cbuf = nullptr; // readonly buffer ptr size_t _buf_sz = 0u; // hard buffer limit size_t _len = 0u; // calculated length of stream - const StreamWorkMode _mode; + StreamMode _mode = kStream_None; size_t _pos = 0u; // current stream pos private: @@ -89,14 +86,11 @@ class VectorStream : public MemoryStream VectorStream(const std::vector &cbuf, DataEndianess stream_endianess = kLittleEndian); // Construct memory stream in the chosen mode over a given std::vector; // vector must persist in memory until the stream is closed. - VectorStream(std::vector &buf, StreamWorkMode mode, DataEndianess stream_endianess = kLittleEndian); + VectorStream(std::vector &buf, StreamMode mode, DataEndianess stream_endianess = kLittleEndian); ~VectorStream() override = default; void Close() override; - bool CanRead() const override; - bool CanWrite() const override; - size_t Write(const void *buffer, size_t size) override; int32_t WriteByte(uint8_t b) override; diff --git a/Common/util/stream.h b/Common/util/stream.h index d9c37608ad4..503fde5573c 100644 --- a/Common/util/stream.h +++ b/Common/util/stream.h @@ -34,11 +34,22 @@ namespace AGS namespace Common { +// The mode in which the stream is opened, +// defines acceptable operations and capabilities. // TODO: merge with FileWorkMode (historical mistake) -enum StreamWorkMode +enum StreamMode { - kStream_Read = 0x1, - kStream_Write = 0x2 + // kStream_None defines an invalid (non-functional) stream + kStream_None = 0, + // Following capabilities should be passed as request + // when opening a stream subclass + kStream_Read = 0x01, + kStream_Write = 0x02, + kStream_ReadWrite = kStream_Read | kStream_Write, + + // Following capabilities should not be requested, + // but are defined by each particular stream subclass + kStream_Seek = 0x04 }; enum StreamSeek @@ -86,16 +97,23 @@ class Stream : public IStream // Stream interface //----------------------------------------------------- - virtual bool IsValid() const = 0; + // Tells which mode the stream is working in, which defines + // supported io operations, such as reading, writing, seeking, etc. + // Invalid streams return kStream_None to indicate that they are not functional. + virtual StreamMode GetMode() const = 0; + + // Tells if stream is functional, and which operations are supported + bool IsValid() const { return GetMode() != kStream_None; } + bool CanRead() const { return (GetMode() & kStream_Read) != 0; } + bool CanWrite() const { return (GetMode() & kStream_Write) != 0; } + bool CanSeek() const { return (GetMode() & kStream_Seek) != 0; } + // Tells if there were errors during previous io operation(s); // the call to GetError() *resets* the error record. virtual bool GetError() const { return false; } virtual bool EOS() const = 0; virtual soff_t GetLength() const = 0; virtual soff_t GetPosition() const = 0; - virtual bool CanRead() const = 0; - virtual bool CanWrite() const = 0; - virtual bool CanSeek() const = 0; virtual void Close() = 0; diff --git a/Engine/ac/dynobj/scriptfile.cpp b/Engine/ac/dynobj/scriptfile.cpp index 17cd5fe5f2c..7d93f3fe10d 100644 --- a/Engine/ac/dynobj/scriptfile.cpp +++ b/Engine/ac/dynobj/scriptfile.cpp @@ -14,11 +14,11 @@ #include "ac/dynobj/scriptfile.h" #include "ac/global_file.h" -// CHECKME: actually NULLs here will be equal to kFile_Open & kFile_Read +// CHECKME: actually NULLs here will be equal to kFile_Open & kStream_Read const Common::FileOpenMode sc_File::fopenModes[] = {Common::kFile_Open/*CHECKME, was undefined*/, Common::kFile_Open, Common::kFile_CreateAlways, Common::kFile_Create}; -const Common::FileWorkMode sc_File::fworkModes[] = - {Common::kFile_Read/*CHECKME, was undefined*/, Common::kFile_Read, Common::kFile_Write, Common::kFile_Write}; +const Common::StreamMode sc_File::fworkModes[] = + {Common::kStream_Read/*CHECKME, was undefined*/, Common::kStream_Read, Common::kStream_Write, Common::kStream_Write}; int sc_File::Dispose(void* /*address*/, bool /*force*/) { Close(); diff --git a/Engine/ac/dynobj/scriptfile.h b/Engine/ac/dynobj/scriptfile.h index 821c85e176b..f74130901cb 100644 --- a/Engine/ac/dynobj/scriptfile.h +++ b/Engine/ac/dynobj/scriptfile.h @@ -20,6 +20,7 @@ #include "ac/dynobj/cc_agsdynamicobject.h" #include "util/file.h" +#include "util/stream.h" using namespace AGS; // FIXME later @@ -31,7 +32,7 @@ struct sc_File final : CCBasicObject { int32_t handle; static const Common::FileOpenMode fopenModes[]; - static const Common::FileWorkMode fworkModes[]; + static const Common::StreamMode fworkModes[]; int Dispose(void *address, bool force) override; diff --git a/Engine/ac/file.cpp b/Engine/ac/file.cpp index b61b4afc7f9..07f7078721b 100644 --- a/Engine/ac/file.cpp +++ b/Engine/ac/file.cpp @@ -532,10 +532,10 @@ ResolvedPath ResolveWritePathAndCreateDirs(const String &sc_path) } Stream *ResolveScriptPathAndOpen(const String &sc_path, - FileOpenMode open_mode, FileWorkMode work_mode) + FileOpenMode open_mode, StreamMode work_mode) { ResolvedPath rp; - if (open_mode == kFile_Open && work_mode == kFile_Read) + if (open_mode == kFile_Open && work_mode == kStream_Read) rp = ResolveScriptPathAndFindFile(sc_path, true); else rp = ResolveWritePathAndCreateDirs(sc_path); diff --git a/Engine/ac/global_dynamicsprite.cpp b/Engine/ac/global_dynamicsprite.cpp index 2b6291dc45f..ce294fa2902 100644 --- a/Engine/ac/global_dynamicsprite.cpp +++ b/Engine/ac/global_dynamicsprite.cpp @@ -32,7 +32,7 @@ int LoadImageFile(const char *filename) return 0; std::unique_ptr in( - ResolveScriptPathAndOpen(filename, FileOpenMode::kFile_Open, FileWorkMode::kFile_Read)); + ResolveScriptPathAndOpen(filename, FileOpenMode::kFile_Open, StreamMode::kStream_Read)); if (!in) return 0; diff --git a/Engine/ac/global_file.cpp b/Engine/ac/global_file.cpp index af900a38d38..662442ff55b 100644 --- a/Engine/ac/global_file.cpp +++ b/Engine/ac/global_file.cpp @@ -30,7 +30,7 @@ using namespace AGS::Common; int32_t FileOpenCMode(const char*fnmm, const char* cmode) { Common::FileOpenMode open_mode; - Common::FileWorkMode work_mode; + Common::StreamMode work_mode; // NOTE: here we ignore the text-mode flag. AGS 2.62 did not let // game devs to open files in text mode. The file reading and // writing logic in AGS makes extra control characters added for @@ -43,7 +43,7 @@ int32_t FileOpenCMode(const char*fnmm, const char* cmode) return FileOpen(fnmm, open_mode, work_mode); } -int32_t FileOpen(const char *fnmm, Common::FileOpenMode open_mode, Common::FileWorkMode work_mode) +int32_t FileOpen(const char *fnmm, Common::FileOpenMode open_mode, Common::StreamMode work_mode) { debug_script_print(kDbgMsg_Debug, "FileOpen: request: %s, mode: %s", diff --git a/Engine/ac/global_file.h b/Engine/ac/global_file.h index 73aa17f9c4c..ede833a3c57 100644 --- a/Engine/ac/global_file.h +++ b/Engine/ac/global_file.h @@ -23,7 +23,7 @@ namespace AGS { namespace Common { class Stream; } } using namespace AGS; // FIXME later -int32_t FileOpen(const char*fnmm, Common::FileOpenMode open_mode, Common::FileWorkMode work_mode); +int32_t FileOpen(const char*fnmm, Common::FileOpenMode open_mode, Common::StreamMode work_mode); // NOTE: FileOpenCMode is a backwards-compatible replacement for old-style global script function FileOpen int32_t FileOpenCMode(const char*fnmm, const char* cmode); void FileClose(int32_t handle); diff --git a/Engine/ac/global_game.cpp b/Engine/ac/global_game.cpp index 39689260e67..70bb3be8ab7 100644 --- a/Engine/ac/global_game.cpp +++ b/Engine/ac/global_game.cpp @@ -693,7 +693,7 @@ int SaveScreenShot(const char*namm) { filename = Path::ConcatPaths(svg_dir, namm); } - std::unique_ptr out(File::OpenFileCI(filename, kFile_CreateAlways, kFile_Write)); + std::unique_ptr out(File::OpenFileCI(filename, kFile_CreateAlways, kStream_Write)); if (!out) return 0; std::unique_ptr bmp(CopyScreenIntoBitmap(play.GetMainViewport().GetWidth(), play.GetMainViewport().GetHeight())); diff --git a/Engine/ac/path_helper.h b/Engine/ac/path_helper.h index 5a50e0f5192..91fe576de56 100644 --- a/Engine/ac/path_helper.h +++ b/Engine/ac/path_helper.h @@ -27,6 +27,7 @@ #include "util/file.h" #include "util/path.h" +#include "util/stream.h" using AGS::Common::String; @@ -125,6 +126,6 @@ ResolvedPath ResolveWritePathAndCreateDirs(const String &sc_path); // Fills a full resolved path, if possible. // Returns open stream on success, and null on failure. AGS::Common::Stream *ResolveScriptPathAndOpen(const String &sc_path, - AGS::Common::FileOpenMode open_mode, AGS::Common::FileWorkMode work_mode); + AGS::Common::FileOpenMode open_mode, AGS::Common::StreamMode work_mode); #endif // __AGS_EE_AC__PATHHELPER_H diff --git a/Engine/debug/logfile.cpp b/Engine/debug/logfile.cpp index 0646ff8c757..1096538777f 100644 --- a/Engine/debug/logfile.cpp +++ b/Engine/debug/logfile.cpp @@ -37,7 +37,7 @@ void LogFile::PrintMessage(const DebugMessage &msg) if (_filePath.IsEmpty()) return; _file.reset(File::OpenFile(_filePath, _openMode == kLogFile_Append ? Common::kFile_Create : Common::kFile_CreateAlways, - Common::kFile_Write)); + Common::kStream_Write)); if (!_file) { Debug::Printf("Unable to write log to '%s'.", _filePath.GetCStr()); @@ -73,7 +73,7 @@ bool LogFile::OpenFile(const String &file_path, OpenMode open_mode) { _file.reset(File::OpenFile(file_path, open_mode == kLogFile_Append ? Common::kFile_Create : Common::kFile_CreateAlways, - Common::kFile_Write)); + Common::kStream_Write)); return _file.get() != nullptr; } } diff --git a/Engine/plugin/agsplugin.cpp b/Engine/plugin/agsplugin.cpp index e84298e1573..a24d51af295 100644 --- a/Engine/plugin/agsplugin.cpp +++ b/Engine/plugin/agsplugin.cpp @@ -815,7 +815,7 @@ size_t IAGSEngine::ResolveFilePath(const char *script_path, char *buf, size_t bu ::IAGSStream *IAGSEngine::OpenFileStream(const char *script_path, int file_mode, int work_mode) { std::unique_ptr s(ResolveScriptPathAndOpen(script_path, - static_cast(file_mode), static_cast(work_mode))); + static_cast(file_mode), static_cast(work_mode))); if (!s) return nullptr; int32_t fhandle = add_file_stream(std::move(s), "IAGSEngine::OpenFileStream"); diff --git a/Engine/script/cc_instance.cpp b/Engine/script/cc_instance.cpp index 330624ca7f2..77a16b4d30c 100644 --- a/Engine/script/cc_instance.cpp +++ b/Engine/script/cc_instance.cpp @@ -1690,7 +1690,7 @@ void ccInstance::DumpInstruction(const ScriptOperation &op) const return; } - Stream *data_s = File::OpenFile("script.log", kFile_Create, kFile_Write); + Stream *data_s = File::OpenFile("script.log", kFile_Create, kStream_Write); TextStreamWriter writer(data_s); writer.WriteFormat("Line %3d, IP:%8d (SP:%p) ", line_num, pc, registers[SREG_SP].RValue); From e88c3a03729eaad2e95d9c2810e687ffdb2009cd Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Tue, 19 Dec 2023 03:46:23 +0300 Subject: [PATCH 3/8] Common: expanded IStream interface Expose more methods in the interface: GetPath, EOS, GetError, ReadByte, WriteByte, Flush and Close. --- Common/util/stream.h | 61 ++++++++++++++++++++++++++++++++------------ Engine/ac/file.cpp | 2 +- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/Common/util/stream.h b/Common/util/stream.h index 503fde5573c..d61332a083b 100644 --- a/Common/util/stream.h +++ b/Common/util/stream.h @@ -64,20 +64,47 @@ enum StreamSeek class IStream { public: - // Reads number of bytes into the provided buffer - virtual size_t Read(void *buffer, size_t len) = 0; - // Writes number of bytes from the provided buffer - virtual size_t Write(const void *buffer, size_t len) = 0; + // Tells which mode the stream is working in, which defines + // supported io operations, such as reading, writing, seeking, etc. + // Invalid streams return kStream_None to indicate that they are not functional. + virtual StreamMode GetMode() const = 0; + // Returns an optional path of a stream's source, such as a filepath; + // this is purely for diagnostic purposes + virtual const char *GetPath() const = 0; + // Tells whether this stream's position is at its end; + // note that unlike standard C feof this does not wait for a read attempt + // past the stream end, and reports positive when position = length. + virtual bool EOS() const = 0; + // Tells if there were errors during previous io operation(s); + // the call to GetError() *resets* the error record. + virtual bool GetError() const = 0; // Returns the total stream's length in bytes virtual soff_t GetLength() const = 0; // Returns stream's position virtual soff_t GetPosition() const = 0; + + // Reads number of bytes into the provided buffer + virtual size_t Read(void *buffer, size_t len) = 0; + // ReadByte conforms to standard C fgetc behavior: + // - on success returns an *unsigned char* packed in the int32 + // - on failure (EOS or other error), returns -1 + virtual int32_t ReadByte() = 0; + // Writes number of bytes from the provided buffer + virtual size_t Write(const void *buffer, size_t len) = 0; + // WriteByte conforms to standard C fputc behavior: + // - on success, returns the written unsigned char packed in the int32 + // - on failure, returns -1 + virtual int32_t WriteByte(uint8_t b) = 0; // Seeks to offset from the origin virtual bool Seek(soff_t offset, StreamSeek origin) = 0; + // Flush stream buffer to the underlying device + virtual bool Flush() = 0; + // Closes the stream + virtual void Close() = 0; protected: IStream() = default; - ~IStream() = default; + virtual ~IStream() = default; }; @@ -89,25 +116,27 @@ class Stream : public IStream : _path(path) {} virtual ~Stream() = default; - // Returns an optional path of a stream's source, such as a filepath; - // primarily for diagnostic purposes - const String &GetPath() const { return _path; } - //----------------------------------------------------- - // Stream interface + // Helpers for learning the stream's state and capabilities //----------------------------------------------------- - // Tells which mode the stream is working in, which defines - // supported io operations, such as reading, writing, seeking, etc. - // Invalid streams return kStream_None to indicate that they are not functional. - virtual StreamMode GetMode() const = 0; - // Tells if stream is functional, and which operations are supported bool IsValid() const { return GetMode() != kStream_None; } bool CanRead() const { return (GetMode() & kStream_Read) != 0; } bool CanWrite() const { return (GetMode() & kStream_Write) != 0; } bool CanSeek() const { return (GetMode() & kStream_Seek) != 0; } + //----------------------------------------------------- + // Stream interface + //----------------------------------------------------- + + // Returns an optional path of a stream's source, such as a filepath; + // primarily for diagnostic purposes + const char *GetPath() const override { return _path.GetCStr(); } + // Tells which mode the stream is working in, which defines + // supported io operations, such as reading, writing, seeking, etc. + // Invalid streams return kStream_None to indicate that they are not functional. + virtual StreamMode GetMode() const = 0; // Tells if there were errors during previous io operation(s); // the call to GetError() *resets* the error record. virtual bool GetError() const { return false; } @@ -156,7 +185,7 @@ class Stream : public IStream virtual bool Flush() = 0; //----------------------------------------------------- - // Helper methods + // Helper methods for read and write //----------------------------------------------------- bool ReadBool() { diff --git a/Engine/ac/file.cpp b/Engine/ac/file.cpp index 07f7078721b..693d8d6114e 100644 --- a/Engine/ac/file.cpp +++ b/Engine/ac/file.cpp @@ -694,7 +694,7 @@ class ScriptFileHandle : public IManagedStream } // Returns an optional stream's source description. // This may be a file path, or a resource name, or anything of that kind. - const char *GetPath() override { return _s->GetPath().GetCStr(); } + const char *GetPath() override { return _s->GetPath(); } // Reads number of bytes into the provided buffer virtual size_t Read(void *buffer, size_t len) override { return _s->Read(buffer, len); } // Writes number of bytes from the provided buffer From 780063a8e2016418163412b39fe77c938a3d372c Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Wed, 20 Dec 2023 02:06:18 +0300 Subject: [PATCH 4/8] Engine: rewrote IAGSStream api, pass Stream objects directly to plugin IAGSStream api complies to the internal IStream. The stream object created by the IAGSEngine::OpenFileStream call it passed to the caller directly, without being stored in the engine. --- Common/util/stream.h | 11 ++++++ Engine/ac/file.cpp | 51 ++------------------------ Engine/ac/file.h | 46 ----------------------- Engine/plugin/agsplugin.cpp | 14 ++----- Engine/plugin/agsplugin.h | 73 ++++++++++++++++++++++++++----------- 5 files changed, 70 insertions(+), 125 deletions(-) diff --git a/Common/util/stream.h b/Common/util/stream.h index d61332a083b..bd2c9832068 100644 --- a/Common/util/stream.h +++ b/Common/util/stream.h @@ -102,6 +102,13 @@ class IStream // Closes the stream virtual void Close() = 0; + // Closes the stream and deallocates the object memory. + // NOTE: this effectively deletes the stream object, making it unusable. + // This method is purposed for the plugin API, it should be used instead + // of regular *delete* on stream objects that may have been provided + // by plugins. + virtual void Dispose() = 0; + protected: IStream() = default; virtual ~IStream() = default; @@ -213,6 +220,10 @@ class Stream : public IStream protected: String _path; // optional name of the stream's source (e.g. filepath) + +private: + // Standard streams do not expose this method to avoid incorrect use. + void Dispose() override { delete this; } }; // Copies N bytes from one stream into another; diff --git a/Engine/ac/file.cpp b/Engine/ac/file.cpp index 693d8d6114e..21ac434be7d 100644 --- a/Engine/ac/file.cpp +++ b/Engine/ac/file.cpp @@ -669,9 +669,8 @@ AssetPath get_voice_over_assetpath(const String &filename) //============================================================================= -// ScriptFileHandle is a wrapper over a Stream object, prepared for script -// or plugin. Implements IManagedStream, which is a plugin API contract. -class ScriptFileHandle : public IManagedStream +// ScriptFileHandle is a wrapper over a Stream object, prepared for script. +class ScriptFileHandle { public: ScriptFileHandle() = default; @@ -684,38 +683,6 @@ class ScriptFileHandle : public IManagedStream // Releases Stream ownership; used in case of temporary stream wrap Stream *ReleaseStream() { return _s.release(); } - //------------------------------------------------------------------------- - // IManagedStream implementation - // Flushes and closes the stream, deallocates the stream object. - // After calling this the IAGSStream pointer becomes INVALID. - void Close() override - { - close_file_stream(_handle, "IAGSStream::Close"); // this will dealloc us - } - // Returns an optional stream's source description. - // This may be a file path, or a resource name, or anything of that kind. - const char *GetPath() override { return _s->GetPath(); } - // Reads number of bytes into the provided buffer - virtual size_t Read(void *buffer, size_t len) override { return _s->Read(buffer, len); } - // Writes number of bytes from the provided buffer - virtual size_t Write(void *buffer, size_t len) override { return _s->Write(buffer, len); } - // Returns the total stream's length in bytes - virtual int64_t GetLength() override { return _s->GetLength(); } - // Returns stream's position - virtual int64_t GetPosition() override { return _s->GetPosition(); } - // Tells whether the stream's position is at its end - virtual bool EOS() override { return _s->EOS(); } - // Seeks to offset from the origin, see AGSSTREAM_SEEK_* constants - virtual int64_t Seek(int64_t offset, int origin) override - { - if (_s->Seek(offset, static_cast(origin))) - return _s->GetPosition(); - return -1ll; - } - // Flushes stream, forcing it to write any buffered data to the - // underlying device. Note that the effect may depend on implementation. - virtual void Flush() override { _s->Flush(); } - private: std::unique_ptr _s; int32_t _handle = 0; @@ -763,19 +730,9 @@ Stream *get_file_stream(int32_t fhandle, const char *operation_name) return fh ? fh->GetStream() : nullptr; } -IManagedStream *get_file_stream_iface(int32_t fhandle, const char *operation_name) +IStream *get_file_stream_iface(int32_t fhandle, const char *operation_name) { - return check_file_stream(fhandle, operation_name); -} - -int32_t find_file_stream_handle(AGS::Engine::IManagedStream *iface) -{ - for (auto &pfh : file_streams) - { - if (pfh.get() == iface) - return pfh->GetHandle(); - } - return 0; + return check_file_stream(fhandle, operation_name)->GetStream(); } Stream *release_file_stream(int32_t fhandle, const char *operation_name) diff --git a/Engine/ac/file.h b/Engine/ac/file.h index 823c3808c29..f086baded28 100644 --- a/Engine/ac/file.h +++ b/Engine/ac/file.h @@ -47,56 +47,10 @@ int File_GetPosition(sc_File *fil); //============================================================================= -namespace AGS -{ -namespace Engine -{ - // IManagedStream interface is a contract for the plugin API, - // matching IAGSStream interface there. Having this here is mostly an - // issue of code organization. Perhaps this may be reviewed if we change - // how plugin API is declared and reused within the engine itself. - // NOTE: we cannot use our utility IStream for this, because: - // 1) different, extended expectation for Close function; - // 2) different types in args or return values: we cannot use - // some of them in plugin API. - class IManagedStream - { - public: - // Flushes and closes the stream, deallocates the stream object. - // After calling this the IAGSStream pointer becomes INVALID. - virtual void Close() = 0; - // Returns an optional stream's source description. - // This may be a file path, or a resource name, or anything of that kind. - virtual const char *GetPath() = 0; - // Reads number of bytes into the provided buffer - virtual size_t Read(void *buffer, size_t len) = 0; - // Writes number of bytes from the provided buffer - virtual size_t Write(void *buffer, size_t len) = 0; - // Returns the total stream's length in bytes - virtual int64_t GetLength() = 0; - // Returns stream's position - virtual int64_t GetPosition() = 0; - // Tells whether the stream's position is at its end - virtual bool EOS() = 0; - // Seeks to offset from the origin, returns new position in stream, - // or -1 on error. - virtual int64_t Seek(int64_t offset, int origin) = 0; - // Flushes stream, forcing it to write any buffered data to the - // underlying device. Note that the effect may depend on implementation. - virtual void Flush() = 0; - protected: - IManagedStream() = default; - virtual ~IManagedStream() = default; - }; -} // namespace Engine -} // namespace AGS - // Managed file streams: for script and plugin use int32_t add_file_stream(std::unique_ptr &&stream, const char *operation_name); void close_file_stream(int32_t fhandle, const char *operation_name); AGS::Common::Stream *get_file_stream(int32_t fhandle, const char *operation_name); -AGS::Engine::IManagedStream *get_file_stream_iface(int32_t fhandle, const char *operation_name); -int32_t find_file_stream_handle(AGS::Engine::IManagedStream *iface); AGS::Common::Stream *release_file_stream(int32_t fhandle, const char *operation_name); void close_all_file_streams(); diff --git a/Engine/plugin/agsplugin.cpp b/Engine/plugin/agsplugin.cpp index a24d51af295..ef5276bbc9f 100644 --- a/Engine/plugin/agsplugin.cpp +++ b/Engine/plugin/agsplugin.cpp @@ -814,21 +814,15 @@ size_t IAGSEngine::ResolveFilePath(const char *script_path, char *buf, size_t bu ::IAGSStream *IAGSEngine::OpenFileStream(const char *script_path, int file_mode, int work_mode) { - std::unique_ptr s(ResolveScriptPathAndOpen(script_path, - static_cast(file_mode), static_cast(work_mode))); - if (!s) - return nullptr; - int32_t fhandle = add_file_stream(std::move(s), "IAGSEngine::OpenFileStream"); - if (fhandle <= 0) - return nullptr; - return reinterpret_cast<::IAGSStream*>( - get_file_stream_iface(fhandle, "IAGSEngine::OpenFileStream")); + Stream *s = ResolveScriptPathAndOpen(script_path, + static_cast(file_mode), static_cast(work_mode)); + return reinterpret_cast<::IAGSStream*>(s); } ::IAGSStream *IAGSEngine::GetFileStreamByHandle(int32 fhandle) { return reinterpret_cast<::IAGSStream*>( - get_file_stream_iface(fhandle, "IAGSEngine::GetFileStreamByHandle")); + get_file_stream(fhandle, "IAGSEngine::GetFileStreamByHandle")); } // *********** General plugin implementation ********** diff --git a/Engine/plugin/agsplugin.h b/Engine/plugin/agsplugin.h index 6c77cbee0ba..b9132369459 100644 --- a/Engine/plugin/agsplugin.h +++ b/Engine/plugin/agsplugin.h @@ -296,19 +296,21 @@ struct AGSGameInfo { // File open modes // Opens existing file, fails otherwise -#define AGSSTREAM_FILE_OPEN 0 +#define AGSSTREAM_FILE_OPEN 1 // Opens existing file, creates one if it did not exist -#define AGSSTREAM_FILE_CREATE 1 +#define AGSSTREAM_FILE_CREATE 2 // Always creates a new file, completely overwrites any existing one -#define AGSSTREAM_FILE_CREATEALWAYS 2 +#define AGSSTREAM_FILE_CREATEALWAYS 3 // Stream work modes // Read-only -#define AGSSTREAM_MODE_READ 0x1 +#define AGSSTREAM_MODE_READ 0x01 // Write-only -#define AGSSTREAM_MODE_WRITE 0x2 -// Support both read and write +#define AGSSTREAM_MODE_WRITE 0x02 +// Supports both read and write #define AGSSTREAM_MODE_READWRITE (AGSSTREAM_MODE_READ | AGSSTREAM_MODE_WRITE) +// Supports seeking +#define AGSSTREAM_MODE_SEEK 0x04 // Stream seek origins // Seek from the beginning of a stream (towards positive offset) @@ -320,31 +322,56 @@ struct AGSGameInfo { class IAGSStream { public: - // Flushes and closes the stream, deallocates the stream object. - // After calling this the IAGSStream pointer becomes INVALID. - virtual void Close() = 0; + // Tells which mode the stream is working in, which defines + // supported io operations, such as reading, writing, seeking, etc. + // Returns combination of AGSSTREAM_MODE_* flags. + // Invalid or non-functional streams return 0. + virtual int GetMode() const = 0; // Returns an optional stream's source description. - // This may be a file path, or a resource name, or anything of that kind. - virtual const char *GetPath() = 0; + // This may be a file path, or a resource name, or anything of that kind, + // and is purely for diagnostic purposes. + virtual const char *GetPath() const = 0; + // Tells whether this stream's position is at its end; + // note that unlike standard C feof this does not wait for a read attempt + // past the stream end, and reports positive when position = length. + virtual bool EOS() const = 0; + // Tells if there were errors during previous io operation(s); + // the call to GetError() *resets* the error record. + virtual bool GetError() const = 0; + // Returns the total stream's length in bytes + virtual int64_t GetLength() const = 0; + // Returns stream's position + virtual int64_t GetPosition() const = 0; + // Reads number of bytes into the provided buffer virtual size_t Read(void *buffer, size_t len) = 0; + // ReadByte conforms to standard C fgetc behavior: + // - on success returns an *unsigned char* packed in the int32 + // - on failure (EOS or other error), returns -1 + virtual int32_t ReadByte() = 0; // Writes number of bytes from the provided buffer - virtual size_t Write(void *buffer, size_t len) = 0; - // Returns the total stream's length in bytes - virtual int64_t GetLength() = 0; - // Returns stream's position - virtual int64_t GetPosition() = 0; - // Tells whether the stream's position is at its end - virtual bool EOS() = 0; + virtual size_t Write(const void *buffer, size_t len) = 0; + // WriteByte conforms to standard C fputc behavior: + // - on success, returns the written unsigned char packed in the int32 + // - on failure, returns -1 + virtual int32_t WriteByte(uint8_t b) = 0; // Seeks to offset from the origin defined by AGSSTREAM_SEEK_* constants: // * AGSSTREAM_SEEK_SET - seek from the beginning; // * AGSSTREAM_SEEK_CUR - seek from the current position; // * AGSSTREAM_SEEK_END - seek from the end (pass negative offset) // Returns new position in stream, or -1 on error. - virtual int64_t Seek(int64_t offset, int origin) = 0; + virtual bool Seek(int64_t offset, int origin) = 0; // Flushes stream, forcing it to write any buffered data to the // underlying device. Note that the effect may depend on implementation. - virtual void Flush() = 0; + virtual bool Flush() = 0; + // Flushes and closes the stream. + // Usually you do not have to call this, use Dispose() to close + // and delete stream object instead. + virtual void Close() = 0; + + // Closes the stream and deallocates the stream object. + // After calling this the IAGSStream pointer becomes INVALID. + virtual void Dispose() = 0; protected: IAGSStream() = default; @@ -616,11 +643,13 @@ class IAGSEngine { // Opens a data stream, resolving a script path. // File mode should contain one of the AGSSTREAM_FILE_* values, // work mode should contain flag set of the AGSSTREAM_MODE_* values. - // Returns IAGSStream object, or null on failure. - // IAGSStream must be disposed by calling its Close() function. + // Returns IAGSStream object, or null on failure. The returned stream object + // is owned by the caller, and must be deleted by calling its Dispose() method. AGSIFUNC(IAGSStream*) OpenFileStream(const char *script_path, int file_mode, int work_mode); // Returns IAGSStream object identified by the given stream handle. // This lets to retrieve IAGSStream object from a handle received in a event callback. + // *IMPORTANT*: The returned stream's ownership is NOT passed to the caller; + // this stream should not be closed or disposed, doing so will lead to errors in the engine. // Returns null if handle is invalid. AGSIFUNC(IAGSStream*) GetFileStreamByHandle(int32 fhandle); }; From bbb0def5975a7099e59dbbfdf916341794e9b3a4 Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Wed, 20 Dec 2023 03:59:58 +0300 Subject: [PATCH 5/8] Emscripten: update the use of StreamMode --- Engine/platform/emscripten/acpemscripten.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Engine/platform/emscripten/acpemscripten.cpp b/Engine/platform/emscripten/acpemscripten.cpp index 06ef725dc15..1d6bb50a3ba 100644 --- a/Engine/platform/emscripten/acpemscripten.cpp +++ b/Engine/platform/emscripten/acpemscripten.cpp @@ -31,8 +31,7 @@ #include "platform/base/agsplatformdriver.h" #include "util/filestream.h" -using AGS::Common::String; -using AGS::Common::FileStream; +using namespace AGS::Common; extern AGS::Engine::IGraphicsDriver *gfxDriver; @@ -180,8 +179,11 @@ void AGSEmscripten::MainInit() ags_syncfs_running = false; FileStream::FileCloseNotify = [this](const FileStream::CloseNotifyArgs &args){ - if(args.WorkMode == Common::FileWorkMode::kFile_Read) return; - if(!Common::Path::IsSameOrSubDir(SavedGamesDirectory.FullDir,args.Filepath)) return; + // Only sync if file stream was in write mode + if ((args.WorkMode & StreamMode::kStream_ReadWrite) == StreamMode::kStream_Read) + return; + if (!Path::IsSameOrSubDir(SavedGamesDirectory.FullDir,args.Filepath)) + return; ScheduleSyncFS(); }; } From 060620cf939f5aa926561f8b2dbb80f628bc0d5a Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Wed, 20 Dec 2023 04:27:15 +0300 Subject: [PATCH 6/8] Android: update the use of StreamMode in AAssetStream --- Common/util/aasset_stream.cpp | 26 ++++---------------------- Common/util/aasset_stream.h | 15 +++++++-------- 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/Common/util/aasset_stream.cpp b/Common/util/aasset_stream.cpp index 32d00312c92..de3c24bf36a 100644 --- a/Common/util/aasset_stream.cpp +++ b/Common/util/aasset_stream.cpp @@ -54,17 +54,13 @@ namespace AGS if(_ownHandle) Close(); } - bool AAssetStream::HasErrors() const - { - return !IsValid(); - } - void AAssetStream::Close() { if(_aAsset) { AAsset_close(_aAsset); _aAsset = nullptr; } + _mode = kStream_None; } bool AAssetStream::Flush() @@ -72,9 +68,9 @@ namespace AGS return false; } - bool AAssetStream::IsValid() const + StreamMode AAssetStream::GetMode() const { - return _aAsset != nullptr; + return _mode; } bool AAssetStream::EOS() const @@ -100,21 +96,6 @@ namespace AGS return -1; } - bool AAssetStream::CanRead() const - { - return IsValid(); - } - - bool AAssetStream::CanWrite() const - { - return false; - } - - bool AAssetStream::CanSeek() const - { - return IsValid(); - } - size_t AAssetStream::Read(void *buffer, size_t size) { if(!IsValid()) return -1; @@ -187,6 +168,7 @@ namespace AGS _cur_offset = 0; _start = 0; _end = AAsset_getLength64(_aAsset); + _mode = static_cast(kStream_Read | kStream_Seek); } diff --git a/Common/util/aasset_stream.h b/Common/util/aasset_stream.h index 9ca99bc273a..77f7e9ee779 100644 --- a/Common/util/aasset_stream.h +++ b/Common/util/aasset_stream.h @@ -57,21 +57,19 @@ namespace AGS { return new AAssetStream(aasset, false, asset_mode, stream_end); } ~AAssetStream() override; - bool HasErrors() const override; void Close() override; bool Flush() override; - // Is stream valid (underlying data initialized properly) - bool IsValid() const override; + // Tells which mode the stream is working in, which defines + // supported io operations, such as reading, writing, seeking, etc. + // Invalid streams return kStream_None to indicate that they are not functional. + StreamMode GetMode() const override; // Is end of stream bool EOS() const override; // Total length of stream (if known) soff_t GetLength() const override; // Current position (if known) soff_t GetPosition() const override; - bool CanRead() const override; - bool CanWrite() const override; - bool CanSeek() const override; size_t Read(void *buffer, size_t size) override; int32_t ReadByte() override; @@ -91,8 +89,9 @@ namespace AGS void Open(const String &asset_name, int asset_mode); void OpenSection(const String &asset_name, int asset_mode, soff_t start_pos, soff_t end_pos); - bool _ownHandle; - AAsset *_aAsset = nullptr; + bool _ownHandle = false; + AAsset *_aAsset = nullptr; + StreamMode _mode = kStream_None; }; } // namespace Common From ff5e68619786fa0d173808b4e7dd6c251faf8bd9 Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Wed, 20 Dec 2023 19:38:14 +0300 Subject: [PATCH 7/8] Common: FileStream::GetFileOpenMode() and AAssetStream::GetAssetMode() --- Common/util/aasset_stream.cpp | 1 + Common/util/aasset_stream.h | 9 ++++++--- Common/util/filestream.h | 11 ++++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Common/util/aasset_stream.cpp b/Common/util/aasset_stream.cpp index de3c24bf36a..2280a4812d0 100644 --- a/Common/util/aasset_stream.cpp +++ b/Common/util/aasset_stream.cpp @@ -168,6 +168,7 @@ namespace AGS _cur_offset = 0; _start = 0; _end = AAsset_getLength64(_aAsset); + _assetMode = asset_mode; _mode = static_cast(kStream_Read | kStream_Seek); } diff --git a/Common/util/aasset_stream.h b/Common/util/aasset_stream.h index 77f7e9ee779..cdc482001d8 100644 --- a/Common/util/aasset_stream.h +++ b/Common/util/aasset_stream.h @@ -57,9 +57,8 @@ namespace AGS { return new AAssetStream(aasset, false, asset_mode, stream_end); } ~AAssetStream() override; - void Close() override; - bool Flush() override; - + // Tells the AASSET_MODE this stream is working in. + int GetAssetMode() const { return _assetMode; } // Tells which mode the stream is working in, which defines // supported io operations, such as reading, writing, seeking, etc. // Invalid streams return kStream_None to indicate that they are not functional. @@ -78,6 +77,9 @@ namespace AGS bool Seek(soff_t offset, StreamSeek origin) override; + bool Flush() override; + void Close() override; + protected: soff_t _start = 0; soff_t _end = 0; @@ -91,6 +93,7 @@ namespace AGS bool _ownHandle = false; AAsset *_aAsset = nullptr; + int _assetMode = AASSET_MODE_UNKNOWN; StreamMode _mode = kStream_None; }; diff --git a/Common/util/filestream.h b/Common/util/filestream.h index da7d5f5dbb7..dce258e16b5 100644 --- a/Common/util/filestream.h +++ b/Common/util/filestream.h @@ -59,14 +59,16 @@ class FileStream : public DataStream { return new FileStream(file, false, work_mode, stream_end); } ~FileStream() override; + // Tells which open mode was used when opening a file. + FileOpenMode GetFileOpenMode() const { return _openMode; } + // Tells which mode the stream is working in, which defines + // supported io operations, such as reading, writing, seeking, etc. + // Invalid streams return kStream_None to indicate that they are not functional. StreamMode GetMode() const override { return _workMode; } // Tells if there were errors during previous io operation(s); // the call to GetError() *resets* the error record. bool GetError() const override; - void Close() override; - bool Flush() override; - // Is end of stream bool EOS() const override; // Total length of stream (if known) @@ -81,6 +83,9 @@ class FileStream : public DataStream bool Seek(soff_t offset, StreamSeek origin) override; + bool Flush() override; + void Close() override; + private: FileStream(FILE *file, bool own, StreamMode work_mode, DataEndianess stream_end); void Open(const String &file_name, FileOpenMode open_mode, StreamMode work_mode); From 9ddd7e5902fcd2672b9f547a89def93de72c81a8 Mon Sep 17 00:00:00 2001 From: Ivan Mogilko Date: Thu, 21 Dec 2023 01:49:34 +0300 Subject: [PATCH 8/8] Common: in Stream class remove virtual overrides without implementation This prevents numerous warnings in stricter compilers. --- Common/util/stream.h | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/Common/util/stream.h b/Common/util/stream.h index bd2c9832068..486d04509bc 100644 --- a/Common/util/stream.h +++ b/Common/util/stream.h @@ -96,7 +96,7 @@ class IStream // - on failure, returns -1 virtual int32_t WriteByte(uint8_t b) = 0; // Seeks to offset from the origin - virtual bool Seek(soff_t offset, StreamSeek origin) = 0; + virtual bool Seek(soff_t offset, StreamSeek origin = kSeekCurrent) = 0; // Flush stream buffer to the underlying device virtual bool Flush() = 0; // Closes the stream @@ -126,7 +126,6 @@ class Stream : public IStream //----------------------------------------------------- // Helpers for learning the stream's state and capabilities //----------------------------------------------------- - // Tells if stream is functional, and which operations are supported bool IsValid() const { return GetMode() != kStream_None; } bool CanRead() const { return (GetMode() & kStream_Read) != 0; } @@ -134,38 +133,22 @@ class Stream : public IStream bool CanSeek() const { return (GetMode() & kStream_Seek) != 0; } //----------------------------------------------------- - // Stream interface + // IStream interface //----------------------------------------------------- - // Returns an optional path of a stream's source, such as a filepath; // primarily for diagnostic purposes const char *GetPath() const override { return _path.GetCStr(); } // Tells which mode the stream is working in, which defines // supported io operations, such as reading, writing, seeking, etc. // Invalid streams return kStream_None to indicate that they are not functional. - virtual StreamMode GetMode() const = 0; + StreamMode GetMode() const override { return kStream_None; } // Tells if there were errors during previous io operation(s); // the call to GetError() *resets* the error record. - virtual bool GetError() const { return false; } - virtual bool EOS() const = 0; - virtual soff_t GetLength() const = 0; - virtual soff_t GetPosition() const = 0; - - virtual void Close() = 0; - - // Reads number of bytes in the provided buffer - virtual size_t Read(void *buffer, size_t size) = 0; - // ReadByte conforms to fgetc behavior: - // - if stream is valid, then returns an *unsigned char* packed in the int - // - if EOS, then returns -1 - virtual int32_t ReadByte() = 0; - // Writes number of bytes from the provided buffer - virtual size_t Write(const void *buffer, size_t size) = 0; - // WriteByte conforms to fputc behavior: - // - on success, returns the unsigned char packed in the int - // - on failure, returns -1 - virtual int32_t WriteByte(uint8_t b) = 0; + bool GetError() const override { return false; } + //----------------------------------------------------- + // Values reading and writing interface + //----------------------------------------------------- // Convenience methods for reading values of particular size virtual int8_t ReadInt8() = 0; virtual int16_t ReadInt16() = 0; @@ -186,11 +169,6 @@ class Stream : public IStream virtual size_t WriteArrayOfInt32(const int32_t *buffer, size_t count) = 0; virtual size_t WriteArrayOfInt64(const int64_t *buffer, size_t count) = 0; - virtual bool Seek(soff_t offset, StreamSeek origin = kSeekCurrent) = 0; - - // Flush stream buffer to the underlying device - virtual bool Flush() = 0; - //----------------------------------------------------- // Helper methods for read and write //-----------------------------------------------------