diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 190f0b1213..8b96b54e4e 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -430,9 +430,9 @@ void Shutdown() static u64 GetDiscEndOffset(const DiscIO::VolumeDisc& disc) { - u64 size = disc.GetSize(); + u64 size = disc.GetDataSize(); - if (disc.IsSizeAccurate()) + if (disc.GetDataSizeType() == DiscIO::DataSizeType::Accurate) { if (size == DiscIO::MINI_DVD_SIZE) return DiscIO::MINI_DVD_SIZE; @@ -464,7 +464,7 @@ void SetDisc(std::unique_ptr disc, if (has_disc) { s_disc_end_offset = GetDiscEndOffset(*disc); - if (!disc->IsSizeAccurate()) + if (disc->GetDataSizeType() != DiscIO::DataSizeType::Accurate) WARN_LOG_FMT(DVDINTERFACE, "Unknown disc size, guessing {0} bytes", s_disc_end_offset); const DiscIO::BlobReader& blob = disc->GetBlobReader(); diff --git a/Source/Core/DiscIO/Blob.h b/Source/Core/DiscIO/Blob.h index d6a81d7c2d..102c151200 100644 --- a/Source/Core/DiscIO/Blob.h +++ b/Source/Core/DiscIO/Blob.h @@ -43,6 +43,18 @@ enum class BlobType NFS, }; +// If you convert an ISO file to another format and then call GetDataSize on it, what is the result? +enum class DataSizeType +{ + // The result is the same as for the ISO. + Accurate, + // The result is not larger than for the ISO. (It's usually a little smaller than for the ISO.) + // Reads to offsets that are larger than the result will return some kind of "blank" data. + LowerBound, + // The result is not smaller than for the ISO. (It's usually much larger than for the ISO.) + UpperBound, +}; + std::string GetName(BlobType blob_type, bool translate); class BlobReader @@ -54,7 +66,7 @@ public: virtual u64 GetRawSize() const = 0; virtual u64 GetDataSize() const = 0; - virtual bool IsDataSizeAccurate() const = 0; + virtual DataSizeType GetDataSizeType() const = 0; // Returns 0 if the format does not use blocks virtual u64 GetBlockSize() const = 0; diff --git a/Source/Core/DiscIO/CISOBlob.h b/Source/Core/DiscIO/CISOBlob.h index e7dacb3647..ddbd5d0719 100644 --- a/Source/Core/DiscIO/CISOBlob.h +++ b/Source/Core/DiscIO/CISOBlob.h @@ -38,10 +38,8 @@ public: BlobType GetBlobType() const override { return BlobType::CISO; } u64 GetRawSize() const override; - // The CISO format does not save the original file size. - // This function returns an upper bound. u64 GetDataSize() const override; - bool IsDataSizeAccurate() const override { return false; } + DataSizeType GetDataSizeType() const override { return DataSizeType::UpperBound; } u64 GetBlockSize() const override { return m_block_size; } bool HasFastRandomAccessInBlock() const override { return true; } diff --git a/Source/Core/DiscIO/CompressedBlob.cpp b/Source/Core/DiscIO/CompressedBlob.cpp index 056c2f4fd3..139a1e6d17 100644 --- a/Source/Core/DiscIO/CompressedBlob.cpp +++ b/Source/Core/DiscIO/CompressedBlob.cpp @@ -273,7 +273,7 @@ bool ConvertToGCZ(BlobReader* infile, const std::string& infile_path, const std::string& outfile_path, u32 sub_type, int block_size, CompressCB callback) { - ASSERT(infile->IsDataSizeAccurate()); + ASSERT(infile->GetDataSizeType() == DataSizeType::Accurate); File::IOFile outfile(outfile_path, "wb"); if (!outfile) diff --git a/Source/Core/DiscIO/CompressedBlob.h b/Source/Core/DiscIO/CompressedBlob.h index e32cc2569d..c828b1ad2b 100644 --- a/Source/Core/DiscIO/CompressedBlob.h +++ b/Source/Core/DiscIO/CompressedBlob.h @@ -53,7 +53,7 @@ public: u64 GetRawSize() const override { return m_file_size; } u64 GetDataSize() const override { return m_header.data_size; } - bool IsDataSizeAccurate() const override { return true; } + DataSizeType GetDataSizeType() const override { return DataSizeType::Accurate; } u64 GetBlockSize() const override { return m_header.block_size; } bool HasFastRandomAccessInBlock() const override { return false; } diff --git a/Source/Core/DiscIO/DirectoryBlob.h b/Source/Core/DiscIO/DirectoryBlob.h index a06e60688f..f616adc876 100644 --- a/Source/Core/DiscIO/DirectoryBlob.h +++ b/Source/Core/DiscIO/DirectoryBlob.h @@ -284,7 +284,7 @@ public: u64 GetRawSize() const override; u64 GetDataSize() const override; - bool IsDataSizeAccurate() const override { return true; } + DataSizeType GetDataSizeType() const override { return DataSizeType::Accurate; } u64 GetBlockSize() const override { return 0; } bool HasFastRandomAccessInBlock() const override { return true; } diff --git a/Source/Core/DiscIO/DiscScrubber.cpp b/Source/Core/DiscIO/DiscScrubber.cpp index b79be16315..690af80045 100644 --- a/Source/Core/DiscIO/DiscScrubber.cpp +++ b/Source/Core/DiscIO/DiscScrubber.cpp @@ -30,7 +30,7 @@ bool DiscScrubber::SetupScrub(const Volume* disc) return false; m_disc = disc; - m_file_size = m_disc->GetSize(); + m_file_size = m_disc->GetDataSize(); // Round up when diving by CLUSTER_SIZE, otherwise MarkAsUsed might write out of bounds const size_t num_clusters = static_cast((m_file_size + CLUSTER_SIZE - 1) / CLUSTER_SIZE); diff --git a/Source/Core/DiscIO/DriveBlob.h b/Source/Core/DiscIO/DriveBlob.h index afe3dc8953..bea2e827e5 100644 --- a/Source/Core/DiscIO/DriveBlob.h +++ b/Source/Core/DiscIO/DriveBlob.h @@ -27,7 +27,7 @@ public: u64 GetRawSize() const override { return m_size; } u64 GetDataSize() const override { return m_size; } - bool IsDataSizeAccurate() const override { return true; } + DataSizeType GetDataSizeType() const override { return DataSizeType::Accurate; } u64 GetBlockSize() const override { return ECC_BLOCK_SIZE; } bool HasFastRandomAccessInBlock() const override { return false; } diff --git a/Source/Core/DiscIO/FileBlob.cpp b/Source/Core/DiscIO/FileBlob.cpp index e185fe9cc2..6011f45ca9 100644 --- a/Source/Core/DiscIO/FileBlob.cpp +++ b/Source/Core/DiscIO/FileBlob.cpp @@ -44,7 +44,7 @@ bool PlainFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr) bool ConvertToPlain(BlobReader* infile, const std::string& infile_path, const std::string& outfile_path, CompressCB callback) { - ASSERT(infile->IsDataSizeAccurate()); + ASSERT(infile->GetDataSizeType() == DataSizeType::Accurate); File::IOFile outfile(outfile_path, "wb"); if (!outfile) diff --git a/Source/Core/DiscIO/FileBlob.h b/Source/Core/DiscIO/FileBlob.h index ea0bd55b89..d478e1a223 100644 --- a/Source/Core/DiscIO/FileBlob.h +++ b/Source/Core/DiscIO/FileBlob.h @@ -22,7 +22,7 @@ public: u64 GetRawSize() const override { return m_size; } u64 GetDataSize() const override { return m_size; } - bool IsDataSizeAccurate() const override { return true; } + DataSizeType GetDataSizeType() const override { return DataSizeType::Accurate; } u64 GetBlockSize() const override { return 0; } bool HasFastRandomAccessInBlock() const override { return true; } diff --git a/Source/Core/DiscIO/NFSBlob.h b/Source/Core/DiscIO/NFSBlob.h index 847c66324a..c17687471d 100644 --- a/Source/Core/DiscIO/NFSBlob.h +++ b/Source/Core/DiscIO/NFSBlob.h @@ -48,7 +48,7 @@ public: u64 GetRawSize() const override; u64 GetDataSize() const override; - bool IsDataSizeAccurate() const override { return false; } + DataSizeType GetDataSizeType() const override { return DataSizeType::LowerBound; } u64 GetBlockSize() const override { return BLOCK_SIZE; } bool HasFastRandomAccessInBlock() const override { return false; } diff --git a/Source/Core/DiscIO/ScrubbedBlob.h b/Source/Core/DiscIO/ScrubbedBlob.h index 6530860afe..fa5648630b 100644 --- a/Source/Core/DiscIO/ScrubbedBlob.h +++ b/Source/Core/DiscIO/ScrubbedBlob.h @@ -22,7 +22,7 @@ public: u64 GetRawSize() const override { return m_blob_reader->GetRawSize(); } u64 GetDataSize() const override { return m_blob_reader->GetDataSize(); } - bool IsDataSizeAccurate() const override { return m_blob_reader->IsDataSizeAccurate(); } + DataSizeType GetDataSizeType() const override { return m_blob_reader->GetDataSizeType(); } u64 GetBlockSize() const override { return m_blob_reader->GetBlockSize(); } bool HasFastRandomAccessInBlock() const override diff --git a/Source/Core/DiscIO/TGCBlob.h b/Source/Core/DiscIO/TGCBlob.h index b6d393ea7d..2db1c9e8b7 100644 --- a/Source/Core/DiscIO/TGCBlob.h +++ b/Source/Core/DiscIO/TGCBlob.h @@ -45,7 +45,7 @@ public: u64 GetRawSize() const override { return m_size; } u64 GetDataSize() const override; - bool IsDataSizeAccurate() const override { return true; } + DataSizeType GetDataSizeType() const override { return DataSizeType::Accurate; } u64 GetBlockSize() const override { return 0; } bool HasFastRandomAccessInBlock() const override { return true; } diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index c49f2ee24e..3747c4c250 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -22,6 +22,7 @@ namespace DiscIO { class BlobReader; enum class BlobType; +enum class DataSizeType; class FileSystem; class VolumeDisc; class VolumeWAD; @@ -137,8 +138,8 @@ public: virtual Country GetCountry(const Partition& partition = PARTITION_NONE) const = 0; virtual BlobType GetBlobType() const = 0; // Size of virtual disc (may be inaccurate depending on the blob type) - virtual u64 GetSize() const = 0; - virtual bool IsSizeAccurate() const = 0; + virtual u64 GetDataSize() const = 0; + virtual DataSizeType GetDataSizeType() const = 0; // Size on disc (compressed size) virtual u64 GetRawSize() const = 0; virtual const BlobReader& GetBlobReader() const = 0; diff --git a/Source/Core/DiscIO/VolumeFileBlobReader.h b/Source/Core/DiscIO/VolumeFileBlobReader.h index 88b0929cbc..89b87a9470 100644 --- a/Source/Core/DiscIO/VolumeFileBlobReader.h +++ b/Source/Core/DiscIO/VolumeFileBlobReader.h @@ -25,7 +25,7 @@ public: u64 GetRawSize() const override; u64 GetDataSize() const override; - bool IsDataSizeAccurate() const override { return true; } + DataSizeType GetDataSizeType() const override { return DataSizeType::Accurate; } u64 GetBlockSize() const override; bool HasFastRandomAccessInBlock() const override; diff --git a/Source/Core/DiscIO/VolumeGC.cpp b/Source/Core/DiscIO/VolumeGC.cpp index 8279b84820..1517207f95 100644 --- a/Source/Core/DiscIO/VolumeGC.cpp +++ b/Source/Core/DiscIO/VolumeGC.cpp @@ -119,14 +119,14 @@ BlobType VolumeGC::GetBlobType() const return m_reader->GetBlobType(); } -u64 VolumeGC::GetSize() const +u64 VolumeGC::GetDataSize() const { return m_reader->GetDataSize(); } -bool VolumeGC::IsSizeAccurate() const +DataSizeType VolumeGC::GetDataSizeType() const { - return m_reader->IsDataSizeAccurate(); + return m_reader->GetDataSizeType(); } u64 VolumeGC::GetRawSize() const diff --git a/Source/Core/DiscIO/VolumeGC.h b/Source/Core/DiscIO/VolumeGC.h index 4dfe64cac4..a87ccd44d8 100644 --- a/Source/Core/DiscIO/VolumeGC.h +++ b/Source/Core/DiscIO/VolumeGC.h @@ -45,8 +45,8 @@ public: bool IsDatelDisc() const override; Region GetRegion() const override; BlobType GetBlobType() const override; - u64 GetSize() const override; - bool IsSizeAccurate() const override; + u64 GetDataSize() const override; + DataSizeType GetDataSizeType() const override; u64 GetRawSize() const override; const BlobReader& GetBlobReader() const override; diff --git a/Source/Core/DiscIO/VolumeVerifier.cpp b/Source/Core/DiscIO/VolumeVerifier.cpp index 460fd2a46b..bc86a40681 100644 --- a/Source/Core/DiscIO/VolumeVerifier.cpp +++ b/Source/Core/DiscIO/VolumeVerifier.cpp @@ -62,7 +62,7 @@ void RedumpVerifier::Start(const Volume& volume) m_revision = volume.GetRevision().value_or(0); m_disc_number = volume.GetDiscNumber().value_or(0); - m_size = volume.GetSize(); + m_size = volume.GetDataSize(); const DiscIO::Platform platform = volume.GetVolumeType(); @@ -364,7 +364,7 @@ VolumeVerifier::VolumeVerifier(const Volume& volume, bool redump_verification, m_hashes_to_calculate(hashes_to_calculate), m_calculating_any_hash(hashes_to_calculate.crc32 || hashes_to_calculate.md5 || hashes_to_calculate.sha1), - m_max_progress(volume.GetSize()) + m_max_progress(volume.GetDataSize()) { if (!m_calculating_any_hash) m_redump_verification = false; @@ -758,10 +758,10 @@ bool VolumeVerifier::ShouldBeDualLayer() const void VolumeVerifier::CheckVolumeSize() { - u64 volume_size = m_volume.GetSize(); + u64 volume_size = m_volume.GetDataSize(); const bool is_disc = IsDisc(m_volume.GetVolumeType()); const bool should_be_dual_layer = is_disc && ShouldBeDualLayer(); - const bool is_size_accurate = m_volume.IsSizeAccurate(); + const bool is_size_accurate = m_volume.GetDataSizeType() != DiscIO::DataSizeType::Accurate; bool volume_size_roughly_known = is_size_accurate; if (should_be_dual_layer && m_biggest_referenced_offset <= SL_DVD_R_SIZE) diff --git a/Source/Core/DiscIO/VolumeWad.cpp b/Source/Core/DiscIO/VolumeWad.cpp index edfd4c76ee..81e2912671 100644 --- a/Source/Core/DiscIO/VolumeWad.cpp +++ b/Source/Core/DiscIO/VolumeWad.cpp @@ -318,14 +318,14 @@ BlobType VolumeWAD::GetBlobType() const return m_reader->GetBlobType(); } -u64 VolumeWAD::GetSize() const +u64 VolumeWAD::GetDataSize() const { return m_reader->GetDataSize(); } -bool VolumeWAD::IsSizeAccurate() const +DataSizeType VolumeWAD::GetDataSizeType() const { - return m_reader->IsDataSizeAccurate(); + return m_reader->GetDataSizeType(); } u64 VolumeWAD::GetRawSize() const diff --git a/Source/Core/DiscIO/VolumeWad.h b/Source/Core/DiscIO/VolumeWad.h index ac18b65271..3b723b7ce9 100644 --- a/Source/Core/DiscIO/VolumeWad.h +++ b/Source/Core/DiscIO/VolumeWad.h @@ -64,8 +64,8 @@ public: Country GetCountry(const Partition& partition = PARTITION_NONE) const override; BlobType GetBlobType() const override; - u64 GetSize() const override; - bool IsSizeAccurate() const override; + u64 GetDataSize() const override; + DataSizeType GetDataSizeType() const override; u64 GetRawSize() const override; const BlobReader& GetBlobReader() const override; diff --git a/Source/Core/DiscIO/VolumeWii.cpp b/Source/Core/DiscIO/VolumeWii.cpp index 1b56e61ed6..eca23fb4f9 100644 --- a/Source/Core/DiscIO/VolumeWii.cpp +++ b/Source/Core/DiscIO/VolumeWii.cpp @@ -369,14 +369,14 @@ BlobType VolumeWii::GetBlobType() const return m_reader->GetBlobType(); } -u64 VolumeWii::GetSize() const +u64 VolumeWii::GetDataSize() const { return m_reader->GetDataSize(); } -bool VolumeWii::IsSizeAccurate() const +DataSizeType VolumeWii::GetDataSizeType() const { - return m_reader->IsDataSizeAccurate(); + return m_reader->GetDataSizeType(); } u64 VolumeWii::GetRawSize() const diff --git a/Source/Core/DiscIO/VolumeWii.h b/Source/Core/DiscIO/VolumeWii.h index 05b43acaff..b1ed4b9789 100644 --- a/Source/Core/DiscIO/VolumeWii.h +++ b/Source/Core/DiscIO/VolumeWii.h @@ -86,8 +86,8 @@ public: Region GetRegion() const override; BlobType GetBlobType() const override; - u64 GetSize() const override; - bool IsSizeAccurate() const override; + u64 GetDataSize() const override; + DataSizeType GetDataSizeType() const override; u64 GetRawSize() const override; const BlobReader& GetBlobReader() const override; std::array GetSyncHash() const override; diff --git a/Source/Core/DiscIO/WIABlob.cpp b/Source/Core/DiscIO/WIABlob.cpp index 77accc590d..b80753120b 100644 --- a/Source/Core/DiscIO/WIABlob.cpp +++ b/Source/Core/DiscIO/WIABlob.cpp @@ -1731,7 +1731,7 @@ WIARVZFileReader::Convert(BlobReader* infile, const VolumeDisc* infile_volu File::IOFile* outfile, WIARVZCompressionType compression_type, int compression_level, int chunk_size, CompressCB callback) { - ASSERT(infile->IsDataSizeAccurate()); + ASSERT(infile->GetDataSizeType() == DataSizeType::Accurate); ASSERT(chunk_size > 0); const u64 iso_size = infile->GetDataSize(); diff --git a/Source/Core/DiscIO/WIABlob.h b/Source/Core/DiscIO/WIABlob.h index d998005d06..633c5801ce 100644 --- a/Source/Core/DiscIO/WIABlob.h +++ b/Source/Core/DiscIO/WIABlob.h @@ -52,7 +52,7 @@ public: u64 GetRawSize() const override { return Common::swap64(m_header_1.wia_file_size); } u64 GetDataSize() const override { return Common::swap64(m_header_1.iso_file_size); } - bool IsDataSizeAccurate() const override { return true; } + DataSizeType GetDataSizeType() const override { return DataSizeType::Accurate; } u64 GetBlockSize() const override { return Common::swap32(m_header_2.chunk_size); } bool HasFastRandomAccessInBlock() const override { return false; } diff --git a/Source/Core/DiscIO/WbfsBlob.h b/Source/Core/DiscIO/WbfsBlob.h index 30db2a7d4c..a981790f1c 100644 --- a/Source/Core/DiscIO/WbfsBlob.h +++ b/Source/Core/DiscIO/WbfsBlob.h @@ -25,11 +25,8 @@ public: BlobType GetBlobType() const override { return BlobType::WBFS; } u64 GetRawSize() const override { return m_size; } - // The WBFS format does not save the original file size. - // This function returns a constant upper bound - // (the size of a double-layer Wii disc). u64 GetDataSize() const override; - bool IsDataSizeAccurate() const override { return false; } + DataSizeType GetDataSizeType() const override { return DataSizeType::UpperBound; } u64 GetBlockSize() const override { return m_wbfs_sector_size; } bool HasFastRandomAccessInBlock() const override { return true; } diff --git a/Source/Core/DolphinTool/ConvertCommand.cpp b/Source/Core/DolphinTool/ConvertCommand.cpp index 4d5e2062fa..94b7f7fd11 100644 --- a/Source/Core/DolphinTool/ConvertCommand.cpp +++ b/Source/Core/DolphinTool/ConvertCommand.cpp @@ -212,7 +212,7 @@ int ConvertCommand::Main(const std::vector& args) } if (format == DiscIO::BlobType::GCZ && volume && - !DiscIO::IsGCZBlockSizeLegacyCompatible(block_size_o.value(), volume->GetSize())) + !DiscIO::IsGCZBlockSizeLegacyCompatible(block_size_o.value(), volume->GetDataSize())) { std::cerr << "Warning: For GCZs to be compatible with Dolphin < 5.0-11893, " "the file size must be an integer multiple of the block size " diff --git a/Source/Core/UICommon/GameFile.cpp b/Source/Core/UICommon/GameFile.cpp index 6e33f480e0..368eab3c1e 100644 --- a/Source/Core/UICommon/GameFile.cpp +++ b/Source/Core/UICommon/GameFile.cpp @@ -133,8 +133,8 @@ GameFile::GameFile(std::string path) : m_file_path(std::move(path)) m_block_size = volume->GetBlobReader().GetBlockSize(); m_compression_method = volume->GetBlobReader().GetCompressionMethod(); m_file_size = volume->GetRawSize(); - m_volume_size = volume->GetSize(); - m_volume_size_is_accurate = volume->IsSizeAccurate(); + m_volume_size = volume->GetDataSize(); + m_volume_size_type = volume->GetDataSizeType(); m_is_datel_disc = volume->IsDatelDisc(); m_is_nkit = volume->IsNKit(); @@ -158,7 +158,7 @@ GameFile::GameFile(std::string path) : m_file_path(std::move(path)) m_valid = true; m_file_size = m_volume_size = File::GetSize(m_file_path); m_game_id = SConfig::MakeGameID(m_file_name); - m_volume_size_is_accurate = true; + m_volume_size_type = DiscIO::DataSizeType::Accurate; m_is_datel_disc = false; m_is_nkit = false; m_platform = DiscIO::Platform::ELFOrDOL; @@ -349,7 +349,7 @@ void GameFile::DoState(PointerWrap& p) p.Do(m_file_size); p.Do(m_volume_size); - p.Do(m_volume_size_is_accurate); + p.Do(m_volume_size_type); p.Do(m_is_datel_disc); p.Do(m_is_nkit); @@ -827,7 +827,7 @@ std::string GameFile::GetFileFormatName() const bool GameFile::ShouldAllowConversion() const { - return DiscIO::IsDisc(m_platform) && m_volume_size_is_accurate; + return DiscIO::IsDisc(m_platform) && m_volume_size_type == DiscIO::DataSizeType::Accurate; } bool GameFile::IsModDescriptor() const diff --git a/Source/Core/UICommon/GameFile.h b/Source/Core/UICommon/GameFile.h index eb1151a28a..fca89073a1 100644 --- a/Source/Core/UICommon/GameFile.h +++ b/Source/Core/UICommon/GameFile.h @@ -104,7 +104,7 @@ public: const std::string& GetApploaderDate() const { return m_apploader_date; } u64 GetFileSize() const { return m_file_size; } u64 GetVolumeSize() const { return m_volume_size; } - bool IsVolumeSizeAccurate() const { return m_volume_size_is_accurate; } + DiscIO::DataSizeType GetVolumeSizeType() const { return m_volume_size_type; } bool IsDatelDisc() const { return m_is_datel_disc; } bool IsNKit() const { return m_is_nkit; } bool IsModDescriptor() const; @@ -145,7 +145,7 @@ private: u64 m_file_size{}; u64 m_volume_size{}; - bool m_volume_size_is_accurate{}; + DiscIO::DataSizeType m_volume_size_type{}; bool m_is_datel_disc{}; bool m_is_nkit{}; diff --git a/Source/Core/UICommon/GameFileCache.cpp b/Source/Core/UICommon/GameFileCache.cpp index 675ec0dc43..f9b26d344d 100644 --- a/Source/Core/UICommon/GameFileCache.cpp +++ b/Source/Core/UICommon/GameFileCache.cpp @@ -27,7 +27,7 @@ namespace UICommon { -static constexpr u32 CACHE_REVISION = 22; // Last changed in PR 10932 +static constexpr u32 CACHE_REVISION = 23; // Last changed in PR 10932 std::vector FindAllGamePaths(const std::vector& directories_to_scan, bool recursive_scan)