Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
828
Src/external_dependencies/openmpt-trunk/include/unrar/file.cpp
vendored
Normal file
828
Src/external_dependencies/openmpt-trunk/include/unrar/file.cpp
vendored
Normal file
|
@ -0,0 +1,828 @@
|
|||
#include "rar.hpp"
|
||||
|
||||
File::File()
|
||||
{
|
||||
hFile=FILE_BAD_HANDLE;
|
||||
*FileName=0;
|
||||
NewFile=false;
|
||||
LastWrite=false;
|
||||
HandleType=FILE_HANDLENORMAL;
|
||||
LineInput=false;
|
||||
SkipClose=false;
|
||||
ErrorType=FILE_SUCCESS;
|
||||
OpenShared=false;
|
||||
AllowDelete=true;
|
||||
AllowExceptions=true;
|
||||
PreserveAtime=false;
|
||||
#ifdef _WIN_ALL
|
||||
CreateMode=FMF_UNDEFINED;
|
||||
#endif
|
||||
ReadErrorMode=FREM_ASK;
|
||||
TruncatedAfterReadError=false;
|
||||
CurFilePos=0;
|
||||
}
|
||||
|
||||
|
||||
File::~File()
|
||||
{
|
||||
if (hFile!=FILE_BAD_HANDLE && !SkipClose)
|
||||
if (NewFile)
|
||||
Delete();
|
||||
else
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
void File::operator = (File &SrcFile)
|
||||
{
|
||||
hFile=SrcFile.hFile;
|
||||
NewFile=SrcFile.NewFile;
|
||||
LastWrite=SrcFile.LastWrite;
|
||||
HandleType=SrcFile.HandleType;
|
||||
TruncatedAfterReadError=SrcFile.TruncatedAfterReadError;
|
||||
wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName));
|
||||
SrcFile.SkipClose=true;
|
||||
}
|
||||
|
||||
|
||||
bool File::Open(const wchar *Name,uint Mode)
|
||||
{
|
||||
ErrorType=FILE_SUCCESS;
|
||||
FileHandle hNewFile;
|
||||
bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0;
|
||||
bool UpdateMode=(Mode & FMF_UPDATE)!=0;
|
||||
bool WriteMode=(Mode & FMF_WRITE)!=0;
|
||||
#ifdef _WIN_ALL
|
||||
uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ;
|
||||
if (UpdateMode)
|
||||
Access|=GENERIC_WRITE;
|
||||
uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ;
|
||||
if (OpenShared)
|
||||
ShareMode|=FILE_SHARE_WRITE;
|
||||
uint Flags=FILE_FLAG_SEQUENTIAL_SCAN;
|
||||
FindData FD;
|
||||
if (PreserveAtime)
|
||||
Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime.
|
||||
hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
|
||||
|
||||
DWORD LastError;
|
||||
if (hNewFile==FILE_BAD_HANDLE)
|
||||
{
|
||||
LastError=GetLastError();
|
||||
|
||||
wchar LongName[NM];
|
||||
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
|
||||
{
|
||||
hNewFile=CreateFile(LongName,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
|
||||
|
||||
// For archive names longer than 260 characters first CreateFile
|
||||
// (without \\?\) fails and sets LastError to 3 (access denied).
|
||||
// We need the correct "file not found" error code to decide
|
||||
// if we create a new archive or quit with "cannot create" error.
|
||||
// So we need to check the error code after \\?\ CreateFile again,
|
||||
// otherwise we'll fail to create new archives with long names.
|
||||
// But we cannot simply assign the new code to LastError,
|
||||
// because it would break "..\arcname.rar" relative names processing.
|
||||
// First CreateFile returns the correct "file not found" code for such
|
||||
// names, but "\\?\" CreateFile returns ERROR_INVALID_NAME treating
|
||||
// dots as a directory name. So we check only for "file not found"
|
||||
// error here and for other errors use the first CreateFile result.
|
||||
if (GetLastError()==ERROR_FILE_NOT_FOUND)
|
||||
LastError=ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND)
|
||||
ErrorType=FILE_NOTFOUND;
|
||||
if (PreserveAtime && hNewFile!=FILE_BAD_HANDLE)
|
||||
{
|
||||
FILETIME ft={0xffffffff,0xffffffff}; // This value prevents atime modification.
|
||||
SetFileTime(hNewFile,NULL,&ft,NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY);
|
||||
#ifdef O_BINARY
|
||||
flags|=O_BINARY;
|
||||
#if defined(_AIX) && defined(_LARGE_FILE_API)
|
||||
flags|=O_LARGEFILE;
|
||||
#endif
|
||||
#endif
|
||||
// NDK r20 has O_NOATIME, but fails to create files with it in Android 7+.
|
||||
#if defined(O_NOATIME)
|
||||
if (PreserveAtime)
|
||||
flags|=O_NOATIME;
|
||||
#endif
|
||||
char NameA[NM];
|
||||
WideToChar(Name,NameA,ASIZE(NameA));
|
||||
|
||||
int handle=open(NameA,flags);
|
||||
#ifdef LOCK_EX
|
||||
|
||||
#ifdef _OSF_SOURCE
|
||||
extern "C" int flock(int, int);
|
||||
#endif
|
||||
if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1)
|
||||
{
|
||||
close(handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
if (handle==-1)
|
||||
hNewFile=FILE_BAD_HANDLE;
|
||||
else
|
||||
{
|
||||
#ifdef FILE_USE_OPEN
|
||||
hNewFile=handle;
|
||||
#else
|
||||
hNewFile=fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY);
|
||||
#endif
|
||||
}
|
||||
if (hNewFile==FILE_BAD_HANDLE && errno==ENOENT)
|
||||
ErrorType=FILE_NOTFOUND;
|
||||
#endif
|
||||
NewFile=false;
|
||||
HandleType=FILE_HANDLENORMAL;
|
||||
SkipClose=false;
|
||||
bool Success=hNewFile!=FILE_BAD_HANDLE;
|
||||
if (Success)
|
||||
{
|
||||
hFile=hNewFile;
|
||||
wcsncpyz(FileName,Name,ASIZE(FileName));
|
||||
TruncatedAfterReadError=false;
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(SFX_MODULE)
|
||||
void File::TOpen(const wchar *Name)
|
||||
{
|
||||
if (!WOpen(Name))
|
||||
ErrHandler.Exit(RARX_OPEN);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool File::WOpen(const wchar *Name)
|
||||
{
|
||||
if (Open(Name))
|
||||
return true;
|
||||
ErrHandler.OpenErrorMsg(Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool File::Create(const wchar *Name,uint Mode)
|
||||
{
|
||||
return false; /* // OPENMPT ADDITION
|
||||
// OpenIndiana based NAS and CIFS shares fail to set the file time if file
|
||||
// was created in read+write mode and some data was written and not flushed
|
||||
// before SetFileTime call. So we should use the write only mode if we plan
|
||||
// SetFileTime call and do not need to read from file.
|
||||
bool WriteMode=(Mode & FMF_WRITE)!=0;
|
||||
bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared;
|
||||
#ifdef _WIN_ALL
|
||||
CreateMode=Mode;
|
||||
uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE;
|
||||
DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0;
|
||||
|
||||
// Windows automatically removes dots and spaces in the end of file name,
|
||||
// So we detect such names and process them with \\?\ prefix.
|
||||
wchar *LastChar=PointToLastChar(Name);
|
||||
bool Special=*LastChar=='.' || *LastChar==' ';
|
||||
|
||||
if (Special && (Mode & FMF_STANDARDNAMES)==0)
|
||||
hFile=FILE_BAD_HANDLE;
|
||||
else
|
||||
hFile=CreateFile(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
|
||||
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
{
|
||||
wchar LongName[NM];
|
||||
if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
|
||||
hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
|
||||
}
|
||||
|
||||
#else
|
||||
char NameA[NM];
|
||||
WideToChar(Name,NameA,ASIZE(NameA));
|
||||
#ifdef FILE_USE_OPEN
|
||||
hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666);
|
||||
#else
|
||||
hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY);
|
||||
#endif
|
||||
#endif
|
||||
NewFile=true;
|
||||
HandleType=FILE_HANDLENORMAL;
|
||||
SkipClose=false;
|
||||
wcsncpyz(FileName,Name,ASIZE(FileName));
|
||||
return hFile!=FILE_BAD_HANDLE;
|
||||
*/ // OPENMPT ADDITION
|
||||
}
|
||||
|
||||
|
||||
#if !defined(SFX_MODULE)
|
||||
void File::TCreate(const wchar *Name,uint Mode)
|
||||
{
|
||||
if (!WCreate(Name,Mode))
|
||||
ErrHandler.Exit(RARX_FATAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool File::WCreate(const wchar *Name,uint Mode)
|
||||
{
|
||||
if (Create(Name,Mode))
|
||||
return true;
|
||||
ErrHandler.CreateErrorMsg(Name);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool File::Close()
|
||||
{
|
||||
bool Success=true;
|
||||
|
||||
if (hFile!=FILE_BAD_HANDLE)
|
||||
{
|
||||
if (!SkipClose)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// We use the standard system handle for stdout in Windows
|
||||
// and it must not be closed here.
|
||||
if (HandleType==FILE_HANDLENORMAL)
|
||||
Success=CloseHandle(hFile)==TRUE;
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
Success=close(hFile)!=-1;
|
||||
#else
|
||||
Success=fclose(hFile)!=EOF;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
hFile=FILE_BAD_HANDLE;
|
||||
}
|
||||
HandleType=FILE_HANDLENORMAL;
|
||||
if (!Success && AllowExceptions)
|
||||
ErrHandler.CloseError(FileName);
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
bool File::Delete()
|
||||
{
|
||||
if (HandleType!=FILE_HANDLENORMAL)
|
||||
return false;
|
||||
if (hFile!=FILE_BAD_HANDLE)
|
||||
Close();
|
||||
if (!AllowDelete)
|
||||
return false;
|
||||
return DelFile(FileName);
|
||||
}
|
||||
|
||||
|
||||
bool File::Rename(const wchar *NewName)
|
||||
{
|
||||
// No need to rename if names are already same.
|
||||
bool Success=wcscmp(FileName,NewName)==0;
|
||||
|
||||
if (!Success)
|
||||
Success=RenameFile(FileName,NewName);
|
||||
|
||||
if (Success)
|
||||
wcsncpyz(FileName,NewName,ASIZE(FileName));
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
||||
bool File::Write(const void *Data,size_t Size)
|
||||
{
|
||||
return true; /* // OPENMPT ADDITION
|
||||
if (Size==0)
|
||||
return true;
|
||||
if (HandleType==FILE_HANDLESTD)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
hFile=GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
#else
|
||||
// Cannot use the standard stdout here, because it already has wide orientation.
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
{
|
||||
#ifdef FILE_USE_OPEN
|
||||
hFile=dup(STDOUT_FILENO); // Open new stdout stream.
|
||||
#else
|
||||
hFile=fdopen(dup(STDOUT_FILENO),"w"); // Open new stdout stream.
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
bool Success;
|
||||
while (1)
|
||||
{
|
||||
Success=false;
|
||||
#ifdef _WIN_ALL
|
||||
DWORD Written=0;
|
||||
if (HandleType!=FILE_HANDLENORMAL)
|
||||
{
|
||||
// writing to stdout can fail in old Windows if data block is too large
|
||||
const size_t MaxSize=0x4000;
|
||||
for (size_t I=0;I<Size;I+=MaxSize)
|
||||
{
|
||||
Success=WriteFile(hFile,(byte *)Data+I,(DWORD)Min(Size-I,MaxSize),&Written,NULL)==TRUE;
|
||||
if (!Success)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
Success=WriteFile(hFile,Data,(DWORD)Size,&Written,NULL)==TRUE;
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
ssize_t Written=write(hFile,Data,Size);
|
||||
Success=Written==Size;
|
||||
#else
|
||||
int Written=fwrite(Data,1,Size,hFile);
|
||||
Success=Written==Size && !ferror(hFile);
|
||||
#endif
|
||||
#endif
|
||||
if (!Success && AllowExceptions && HandleType==FILE_HANDLENORMAL)
|
||||
{
|
||||
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL)
|
||||
int ErrCode=GetLastError();
|
||||
int64 FilePos=Tell();
|
||||
uint64 FreeSize=GetFreeDisk(FileName);
|
||||
SetLastError(ErrCode);
|
||||
if (FreeSize>Size && FilePos-Size<=0xffffffff && FilePos+Size>0xffffffff)
|
||||
ErrHandler.WriteErrorFAT(FileName);
|
||||
#endif
|
||||
if (ErrHandler.AskRepeatWrite(FileName,false))
|
||||
{
|
||||
#if !defined(_WIN_ALL) && !defined(FILE_USE_OPEN)
|
||||
clearerr(hFile);
|
||||
#endif
|
||||
if (Written<Size && Written>0)
|
||||
Seek(Tell()-Written,SEEK_SET);
|
||||
continue;
|
||||
}
|
||||
ErrHandler.WriteError(NULL,FileName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
LastWrite=true;
|
||||
return Success; // It can return false only if AllowExceptions is disabled.
|
||||
*/ // OPENMPT ADDITION
|
||||
}
|
||||
|
||||
|
||||
int File::Read(void *Data,size_t Size)
|
||||
{
|
||||
if (TruncatedAfterReadError)
|
||||
return 0;
|
||||
|
||||
int64 FilePos=0; // Initialized only to suppress some compilers warning.
|
||||
|
||||
if (ReadErrorMode==FREM_IGNORE)
|
||||
FilePos=Tell();
|
||||
int TotalRead=0;
|
||||
while (true)
|
||||
{
|
||||
int ReadSize=DirectRead(Data,Size);
|
||||
|
||||
if (ReadSize==-1)
|
||||
{
|
||||
ErrorType=FILE_READERROR;
|
||||
if (AllowExceptions)
|
||||
if (ReadErrorMode==FREM_IGNORE)
|
||||
{
|
||||
ReadSize=0;
|
||||
for (size_t I=0;I<Size;I+=512)
|
||||
{
|
||||
Seek(FilePos+I,SEEK_SET);
|
||||
size_t SizeToRead=Min(Size-I,512);
|
||||
int ReadCode=DirectRead(Data,SizeToRead);
|
||||
ReadSize+=(ReadCode==-1) ? 512:ReadCode;
|
||||
if (ReadSize!=-1)
|
||||
TotalRead+=ReadSize;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool Ignore=false,Retry=false,Quit=false;
|
||||
if (ReadErrorMode==FREM_ASK && HandleType==FILE_HANDLENORMAL)
|
||||
{
|
||||
ErrHandler.AskRepeatRead(FileName,Ignore,Retry,Quit);
|
||||
if (Retry)
|
||||
continue;
|
||||
}
|
||||
if (Ignore || ReadErrorMode==FREM_TRUNCATE)
|
||||
{
|
||||
TruncatedAfterReadError=true;
|
||||
return 0;
|
||||
}
|
||||
ErrHandler.ReadError(FileName);
|
||||
}
|
||||
}
|
||||
TotalRead+=ReadSize; // If ReadSize is -1, TotalRead is also set to -1 here.
|
||||
|
||||
if (HandleType==FILE_HANDLESTD && !LineInput && ReadSize>0 && (uint)ReadSize<Size)
|
||||
{
|
||||
// Unlike regular files, for pipe we can read only as much as was
|
||||
// written at the other end of pipe. We had seen data coming in small
|
||||
// ~80 byte chunks when piping from 'type arc.rar'. Extraction code
|
||||
// would fail if we read an incomplete archive header from stdin.
|
||||
// So here we ensure that requested size is completely read.
|
||||
// But we return the available data immediately in "line input" mode,
|
||||
// when processing user's input in console prompts. Otherwise apps
|
||||
// piping user responses to multiple Ask() prompts can hang if no more
|
||||
// data is available yet and pipe isn't closed.
|
||||
Data=(byte*)Data+ReadSize;
|
||||
Size-=ReadSize;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (TotalRead>0) // Can be -1 for error and AllowExceptions disabled.
|
||||
CurFilePos+=TotalRead;
|
||||
return TotalRead; // It can return -1 only if AllowExceptions is disabled.
|
||||
}
|
||||
|
||||
|
||||
// Returns -1 in case of error.
|
||||
int File::DirectRead(void *Data,size_t Size)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
const size_t MaxDeviceRead=20000;
|
||||
const size_t MaxLockedRead=32768;
|
||||
#endif
|
||||
if (HandleType==FILE_HANDLESTD)
|
||||
{
|
||||
#ifdef _WIN_ALL
|
||||
// if (Size>MaxDeviceRead)
|
||||
// Size=MaxDeviceRead;
|
||||
hFile=GetStdHandle(STD_INPUT_HANDLE);
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
hFile=STDIN_FILENO;
|
||||
#else
|
||||
hFile=stdin;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#ifdef _WIN_ALL
|
||||
// For pipes like 'type file.txt | rar -si arcname' ReadFile may return
|
||||
// data in small ~4KB blocks. It may slightly reduce the compression ratio.
|
||||
DWORD Read;
|
||||
if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL))
|
||||
{
|
||||
if (IsDevice() && Size>MaxDeviceRead)
|
||||
return DirectRead(Data,MaxDeviceRead);
|
||||
if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE)
|
||||
return 0;
|
||||
|
||||
// We had a bug report about failure to archive 1C database lock file
|
||||
// 1Cv8tmp.1CL, which is a zero length file with a region above 200 KB
|
||||
// permanently locked. If our first read request uses too large buffer
|
||||
// and if we are in -dh mode, so we were able to open the file,
|
||||
// we'll fail with "Read error". So now we use try a smaller buffer size
|
||||
// in case of lock error.
|
||||
if (HandleType==FILE_HANDLENORMAL && Size>MaxLockedRead &&
|
||||
GetLastError()==ERROR_LOCK_VIOLATION)
|
||||
return DirectRead(Data,MaxLockedRead);
|
||||
|
||||
return -1;
|
||||
}
|
||||
return Read;
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
ssize_t ReadSize=read(hFile,Data,Size);
|
||||
if (ReadSize==-1)
|
||||
return -1;
|
||||
return (int)ReadSize;
|
||||
#else
|
||||
if (LastWrite)
|
||||
{
|
||||
fflush(hFile);
|
||||
LastWrite=false;
|
||||
}
|
||||
clearerr(hFile);
|
||||
size_t ReadSize=fread(Data,1,Size,hFile);
|
||||
if (ferror(hFile))
|
||||
return -1;
|
||||
return (int)ReadSize;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::Seek(int64 Offset,int Method)
|
||||
{
|
||||
if (!RawSeek(Offset,Method) && AllowExceptions)
|
||||
ErrHandler.SeekError(FileName);
|
||||
}
|
||||
|
||||
|
||||
bool File::RawSeek(int64 Offset,int Method)
|
||||
{
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
return true;
|
||||
if (!IsSeekable())
|
||||
{
|
||||
if (Method==SEEK_CUR)
|
||||
{
|
||||
Offset+=CurFilePos;
|
||||
Method=SEEK_SET;
|
||||
}
|
||||
if (Method==SEEK_SET && Offset>=CurFilePos) // Reading for seek forward.
|
||||
{
|
||||
uint64 SkipSize=Offset-CurFilePos;
|
||||
while (SkipSize>0)
|
||||
{
|
||||
byte Buf[4096];
|
||||
int ReadSize=Read(Buf,(size_t)Min(SkipSize,ASIZE(Buf)));
|
||||
if (ReadSize<=0)
|
||||
return false;
|
||||
SkipSize-=ReadSize;
|
||||
}
|
||||
CurFilePos=Offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false; // Backward or end of file seek on unseekable file.
|
||||
}
|
||||
if (Offset<0 && Method!=SEEK_SET)
|
||||
{
|
||||
Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset;
|
||||
Method=SEEK_SET;
|
||||
}
|
||||
#ifdef _WIN_ALL
|
||||
LONG HighDist=(LONG)(Offset>>32);
|
||||
if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff &&
|
||||
GetLastError()!=NO_ERROR)
|
||||
return false;
|
||||
#else
|
||||
LastWrite=false;
|
||||
#ifdef FILE_USE_OPEN
|
||||
if (lseek(hFile,(off_t)Offset,Method)==-1)
|
||||
return false;
|
||||
#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS)
|
||||
if (fseeko(hFile,Offset,Method)!=0)
|
||||
return false;
|
||||
#else
|
||||
if (fseek(hFile,(long)Offset,Method)!=0)
|
||||
return false;
|
||||
#endif
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int64 File::Tell()
|
||||
{
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
if (AllowExceptions)
|
||||
ErrHandler.SeekError(FileName);
|
||||
else
|
||||
return -1;
|
||||
if (!IsSeekable())
|
||||
return CurFilePos;
|
||||
#ifdef _WIN_ALL
|
||||
LONG HighDist=0;
|
||||
uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT);
|
||||
if (LowDist==0xffffffff && GetLastError()!=NO_ERROR)
|
||||
if (AllowExceptions)
|
||||
ErrHandler.SeekError(FileName);
|
||||
else
|
||||
return -1;
|
||||
return INT32TO64(HighDist,LowDist);
|
||||
#else
|
||||
#ifdef FILE_USE_OPEN
|
||||
return lseek(hFile,0,SEEK_CUR);
|
||||
#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE)
|
||||
return ftello(hFile);
|
||||
#else
|
||||
return ftell(hFile);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void File::Prealloc(int64 Size)
|
||||
{
|
||||
/* // OPENMPT ADDITION
|
||||
#ifdef _WIN_ALL
|
||||
if (RawSeek(Size,SEEK_SET))
|
||||
{
|
||||
Truncate();
|
||||
Seek(0,SEEK_SET);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_UNIX) && defined(USE_FALLOCATE)
|
||||
// fallocate is rather new call. Only latest kernels support it.
|
||||
// So we are not using it by default yet.
|
||||
int fd = GetFD();
|
||||
if (fd >= 0)
|
||||
fallocate(fd, 0, 0, Size);
|
||||
#endif
|
||||
*/ // OPENMPT ADDITION
|
||||
}
|
||||
|
||||
|
||||
byte File::GetByte()
|
||||
{
|
||||
byte Byte=0;
|
||||
Read(&Byte,1);
|
||||
return Byte;
|
||||
}
|
||||
|
||||
|
||||
void File::PutByte(byte Byte)
|
||||
{
|
||||
Write(&Byte,1);
|
||||
}
|
||||
|
||||
|
||||
bool File::Truncate()
|
||||
{
|
||||
return false; /* // OPENMPT ADDITION
|
||||
#ifdef _WIN_ALL
|
||||
return SetEndOfFile(hFile)==TRUE;
|
||||
#else
|
||||
return ftruncate(GetFD(),(off_t)Tell())==0;
|
||||
#endif
|
||||
*/ // OPENMPT ADDITION
|
||||
}
|
||||
|
||||
|
||||
void File::Flush()
|
||||
{
|
||||
return; /* // OPENMPT ADDITION
|
||||
#ifdef _WIN_ALL
|
||||
FlushFileBuffers(hFile);
|
||||
#else
|
||||
#ifndef FILE_USE_OPEN
|
||||
fflush(hFile);
|
||||
#endif
|
||||
fsync(GetFD());
|
||||
#endif
|
||||
*/ // OPENMPT EDDITION
|
||||
}
|
||||
|
||||
|
||||
void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
|
||||
{
|
||||
/* // OPENMPT ADDITION
|
||||
#ifdef _WIN_ALL
|
||||
// Workaround for OpenIndiana NAS time bug. If we cannot create a file
|
||||
// in write only mode, we need to flush the write buffer before calling
|
||||
// SetFileTime or file time will not be changed.
|
||||
if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0)
|
||||
FlushFileBuffers(hFile);
|
||||
|
||||
bool sm=ftm!=NULL && ftm->IsSet();
|
||||
bool sc=ftc!=NULL && ftc->IsSet();
|
||||
bool sa=fta!=NULL && fta->IsSet();
|
||||
FILETIME fm,fc,fa;
|
||||
if (sm)
|
||||
ftm->GetWinFT(&fm);
|
||||
if (sc)
|
||||
ftc->GetWinFT(&fc);
|
||||
if (sa)
|
||||
fta->GetWinFT(&fa);
|
||||
SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
|
||||
#endif
|
||||
*/ // OPENMPT ADDITION
|
||||
}
|
||||
|
||||
|
||||
void File::SetCloseFileTime(RarTime *ftm,RarTime *fta)
|
||||
{
|
||||
/* // OPENMPT ADDITION
|
||||
// Android APP_PLATFORM := android-14 does not support futimens and futimes.
|
||||
// Newer platforms support futimens, but fail on Android 4.2.
|
||||
// We have to use utime for Android.
|
||||
// Also we noticed futimens fail to set timestamps on NTFS partition
|
||||
// mounted to virtual Linux x86 machine, but utimensat worked correctly.
|
||||
// So we set timestamps for already closed files in Unix.
|
||||
#ifdef _UNIX
|
||||
SetCloseFileTimeByName(FileName,ftm,fta);
|
||||
#endif
|
||||
*/ // OPENMPT ADDITION
|
||||
}
|
||||
|
||||
|
||||
void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta)
|
||||
{
|
||||
/* // OPENMPT ADDITION
|
||||
#ifdef _UNIX
|
||||
bool setm=ftm!=NULL && ftm->IsSet();
|
||||
bool seta=fta!=NULL && fta->IsSet();
|
||||
if (setm || seta)
|
||||
{
|
||||
char NameA[NM];
|
||||
WideToChar(Name,NameA,ASIZE(NameA));
|
||||
|
||||
#ifdef UNIX_TIME_NS
|
||||
timespec times[2];
|
||||
times[0].tv_sec=seta ? fta->GetUnix() : 0;
|
||||
times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW;
|
||||
times[1].tv_sec=setm ? ftm->GetUnix() : 0;
|
||||
times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW;
|
||||
utimensat(AT_FDCWD,NameA,times,0);
|
||||
#else
|
||||
utimbuf ut;
|
||||
if (setm)
|
||||
ut.modtime=ftm->GetUnix();
|
||||
else
|
||||
ut.modtime=fta->GetUnix(); // Need to set something, cannot left it 0.
|
||||
if (seta)
|
||||
ut.actime=fta->GetUnix();
|
||||
else
|
||||
ut.actime=ut.modtime; // Need to set something, cannot left it 0.
|
||||
utime(NameA,&ut);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
*/ // OPENMPT ADDITION
|
||||
}
|
||||
|
||||
|
||||
void File::GetOpenFileTime(RarTime *ft)
|
||||
{
|
||||
/* // OPENMPT ADDITION
|
||||
#ifdef _WIN_ALL
|
||||
FILETIME FileTime;
|
||||
GetFileTime(hFile,NULL,NULL,&FileTime);
|
||||
ft->SetWinFT(&FileTime);
|
||||
#endif
|
||||
#if defined(_UNIX) || defined(_EMX)
|
||||
struct stat st;
|
||||
fstat(GetFD(),&st);
|
||||
ft->SetUnix(st.st_mtime);
|
||||
#endif
|
||||
*/ // OPENMPT ADDITION
|
||||
}
|
||||
|
||||
|
||||
int64 File::FileLength()
|
||||
{
|
||||
int64 SavePos=Tell();
|
||||
Seek(0,SEEK_END);
|
||||
int64 Length=Tell();
|
||||
Seek(SavePos,SEEK_SET);
|
||||
return Length;
|
||||
}
|
||||
|
||||
|
||||
bool File::IsDevice()
|
||||
{
|
||||
if (hFile==FILE_BAD_HANDLE)
|
||||
return false;
|
||||
#ifdef _WIN_ALL
|
||||
uint Type=GetFileType(hFile);
|
||||
return Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE;
|
||||
#else
|
||||
return isatty(GetFD());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifndef SFX_MODULE
|
||||
int64 File::Copy(File &Dest,int64 Length)
|
||||
{
|
||||
Array<byte> Buffer(File::CopyBufferSize());
|
||||
int64 CopySize=0;
|
||||
bool CopyAll=(Length==INT64NDF);
|
||||
|
||||
while (CopyAll || Length>0)
|
||||
{
|
||||
Wait();
|
||||
size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size();
|
||||
byte *Buf=&Buffer[0];
|
||||
int ReadSize=Read(Buf,SizeToRead);
|
||||
if (ReadSize==0)
|
||||
break;
|
||||
size_t WriteSize=ReadSize;
|
||||
#ifdef _WIN_ALL
|
||||
// For FAT32 USB flash drives in Windows if first write is 4 KB or more,
|
||||
// write caching is disabled and "write through" is enabled, resulting
|
||||
// in bad performance, especially for many small files. It happens when
|
||||
// we create SFX archive on USB drive, because SFX module is written first.
|
||||
// So we split the first write to small 1 KB followed by rest of data.
|
||||
if (CopySize==0 && WriteSize>=4096)
|
||||
{
|
||||
const size_t FirstWrite=1024;
|
||||
Dest.Write(Buf,FirstWrite);
|
||||
Buf+=FirstWrite;
|
||||
WriteSize-=FirstWrite;
|
||||
}
|
||||
#endif
|
||||
Dest.Write(Buf,WriteSize);
|
||||
CopySize+=ReadSize;
|
||||
if (!CopyAll)
|
||||
Length-=ReadSize;
|
||||
}
|
||||
return CopySize;
|
||||
}
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue