Files
fheroes2/src/engine/dir.cpp
T

239 lines
7.4 KiB
C++

/***************************************************************************
* fheroes2: https://github.com/ihhub/fheroes2 *
* Copyright (C) 2019 - 2024 *
* *
* Free Heroes2 Engine: http://sourceforge.net/projects/fheroes2 *
* Copyright (C) 2009 by Andrey Afletdinov <[email protected]> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "dir.h"
#include <utility>
#if defined( TARGET_PS_VITA )
#include <psp2/io/dirent.h>
#else
#include <filesystem>
#include <system_error>
#endif
#if defined( _WIN32 )
#include <cstring>
#else
#include <strings.h>
#endif
#include "system.h"
namespace
{
#if defined( TARGET_PS_VITA )
// On PS Vita, getting a list of files using std::filesystem for some reason works much slower than using the native file system API
class SceUIDWrapper final
{
public:
SceUIDWrapper( const std::string & path )
: uid( sceIoDopen( path.c_str() ) )
{
// Do nothing.
}
SceUIDWrapper( const SceUIDWrapper & ) = delete;
~SceUIDWrapper()
{
if ( !isValid() ) {
return;
}
sceIoDclose( uid );
}
SceUIDWrapper & operator=( const SceUIDWrapper & ) = delete;
bool isValid() const
{
return uid >= 0;
}
SceUID get() const
{
return uid;
}
private:
const SceUID uid;
};
#endif
template <typename F>
bool nameFilter( const std::string & filename, const bool needExactMatch, const std::string & filter, const F & strCmp )
{
if ( !needExactMatch && filter.empty() ) {
return true;
}
if ( filename.length() < filter.length() ) {
return false;
}
if ( needExactMatch && filename.length() != filter.length() ) {
return false;
}
const char * filenamePtr = filename.c_str() + filename.length() - filter.length();
return ( strCmp( filenamePtr, filter.c_str() ) == 0 );
}
void getFilesFromDirectory( const std::string & path, const std::string & filter, const bool needExactMatch, ListFiles & files )
{
std::string correctedPath;
if ( !System::GetCaseInsensitivePath( path, correctedPath ) ) {
return;
}
#if defined( _WIN32 )
auto * const strCmp = _stricmp;
#else
auto * const strCmp = strcasecmp;
#endif
#if defined( TARGET_PS_VITA )
const SceUIDWrapper uid( path );
if ( !uid.isValid() ) {
return;
}
SceIoDirent entry;
while ( sceIoDread( uid.get(), &entry ) > 0 ) {
// Ensure that this directory entry is a regular file
if ( !SCE_S_ISREG( entry.d_stat.st_mode ) ) {
continue;
}
if ( !nameFilter( entry.d_name, needExactMatch, filter, strCmp ) ) {
continue;
}
files.emplace_back( System::concatPath( path, entry.d_name ) );
}
#else
std::error_code ec;
// Using the non-throwing overload
for ( const std::filesystem::directory_entry & entry : std::filesystem::directory_iterator( correctedPath, ec ) ) {
// Using the non-throwing overload
if ( !entry.is_regular_file( ec ) ) {
continue;
}
const std::filesystem::path & entryPath = entry.path();
if ( !nameFilter( System::fsPathToString( entryPath.filename() ), needExactMatch, filter, strCmp ) ) {
continue;
}
files.emplace_back( System::fsPathToString( entryPath ) );
}
#endif
}
void getChildDirectories( const std::string & path, ListDirs & dirs )
{
std::string correctedPath;
if ( !System::GetCaseInsensitivePath( path, correctedPath ) ) {
return;
}
#if defined( TARGET_PS_VITA )
const SceUIDWrapper uid( path );
if ( !uid.isValid() ) {
return;
}
SceIoDirent entry;
while ( sceIoDread( uid.get(), &entry ) > 0 ) {
// Ensure that this directory entry is a regular file
if ( !S_ISDIR( entry.d_stat.st_mode ) ) {
continue;
}
dirs.emplace_back( System::concatPath( path, entry.d_name ) );
}
#else
std::error_code ec;
// Using the non-throwing overload
for ( const std::filesystem::directory_entry & entry : std::filesystem::directory_iterator( correctedPath, ec ) ) {
// Using the non-throwing overload
if ( !entry.is_directory( ec ) ) {
continue;
}
const std::filesystem::path & entryPath = entry.path();
dirs.emplace_back( System::fsPathToString( entryPath.filename() ) );
}
#endif
}
}
void ListFiles::Append( ListFiles && files )
{
for ( std::string & file : files ) {
emplace_back( std::move( file ) );
}
}
void ListFiles::ReadDir( const std::string & path, const std::string & filter )
{
getFilesFromDirectory( path, filter, false, *this );
}
void ListFiles::FindFileInDir( const std::string_view path, const std::string_view fileName )
{
std::string correctedFilePath;
// If the file system is case-sensitive, then here we will get the actual file path using the case-insensitive
// search (if such a file exists). If the file system is case-insensitive, we will just get the passed path to
// the file back intact.
if ( !System::GetCaseInsensitivePath( System::concatPath( path, fileName ), correctedFilePath ) ) {
return;
}
// In case the file system is case-insensitive, we additionally check the file for existence.
if ( !System::IsFile( correctedFilePath ) ) {
return;
}
emplace_back( std::move( correctedFilePath ) );
}
bool ListFiles::IsEmpty( const std::string & path, const std::string & filter )
{
ListFiles list;
list.ReadDir( path, filter );
return list.empty();
}
void ListDirs::ReadDir( const std::string & path )
{
getChildDirectories( path, *this );
}