1// FileStreams.cpp
2
3#include "StdAfx.h"
4
5#ifndef _WIN32
6#include <fcntl.h>
7#include <unistd.h>
8#include <errno.h>
9#endif
10
11#ifdef SUPPORT_DEVICE_FILE
12#include "../../../C/Alloc.h"
13#include "../../Common/Defs.h"
14#endif
15
16#include "FileStreams.h"
17
18static inline HRESULT ConvertBoolToHRESULT(bool result)
19{
20  #ifdef _WIN32
21  if (result)
22    return S_OK;
23  DWORD lastError = ::GetLastError();
24  if (lastError == 0)
25    return E_FAIL;
26  return HRESULT_FROM_WIN32(lastError);
27  #else
28  return result ? S_OK: E_FAIL;
29  #endif
30}
31
32bool CInFileStream::Open(LPCTSTR fileName)
33{
34  return File.Open(fileName);
35}
36
37#ifdef USE_WIN_FILE
38#ifndef _UNICODE
39bool CInFileStream::Open(LPCWSTR fileName)
40{
41  return File.Open(fileName);
42}
43#endif
44#endif
45
46bool CInFileStream::OpenShared(LPCTSTR fileName, bool shareForWrite)
47{
48  return File.OpenShared(fileName, shareForWrite);
49}
50
51#ifdef USE_WIN_FILE
52#ifndef _UNICODE
53bool CInFileStream::OpenShared(LPCWSTR fileName, bool shareForWrite)
54{
55  return File.OpenShared(fileName, shareForWrite);
56}
57#endif
58#endif
59
60#ifdef SUPPORT_DEVICE_FILE
61
62static const UInt32 kClusterSize = 1 << 18;
63CInFileStream::CInFileStream():
64  VirtPos(0),
65  PhyPos(0),
66  Buffer(0),
67  BufferSize(0)
68{
69}
70
71#endif
72
73CInFileStream::~CInFileStream()
74{
75  #ifdef SUPPORT_DEVICE_FILE
76  MidFree(Buffer);
77  #endif
78}
79
80STDMETHODIMP CInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
81{
82  #ifdef USE_WIN_FILE
83
84  #ifdef SUPPORT_DEVICE_FILE
85  if (processedSize != NULL)
86    *processedSize = 0;
87  if (size == 0)
88    return S_OK;
89  if (File.IsDeviceFile)
90  {
91    if (File.LengthDefined)
92    {
93      if (VirtPos >= File.Length)
94        return VirtPos == File.Length ? S_OK : E_FAIL;
95      UInt64 rem = File.Length - VirtPos;
96      if (size > rem)
97        size = (UInt32)rem;
98    }
99    for (;;)
100    {
101      const UInt32 mask = kClusterSize - 1;
102      UInt64 mask2 = ~(UInt64)mask;
103      UInt64 alignedPos = VirtPos & mask2;
104      if (BufferSize > 0 && BufferStartPos == alignedPos)
105      {
106        UInt32 pos = (UInt32)VirtPos & mask;
107        if (pos >= BufferSize)
108          return S_OK;
109        UInt32 rem = MyMin(BufferSize - pos, size);
110        memcpy(data, Buffer + pos, rem);
111        VirtPos += rem;
112        if (processedSize != NULL)
113          *processedSize += rem;
114        return S_OK;
115      }
116
117      bool useBuffer = false;
118      if ((VirtPos & mask) != 0 || ((ptrdiff_t)data & mask) != 0 )
119        useBuffer = true;
120      else
121      {
122        UInt64 end = VirtPos + size;
123        if ((end & mask) != 0)
124        {
125          end &= mask2;
126          if (end <= VirtPos)
127            useBuffer = true;
128          else
129            size = (UInt32)(end - VirtPos);
130        }
131      }
132      if (!useBuffer)
133        break;
134      if (alignedPos != PhyPos)
135      {
136        UInt64 realNewPosition;
137        bool result = File.Seek(alignedPos, FILE_BEGIN, realNewPosition);
138        if (!result)
139          return ConvertBoolToHRESULT(result);
140        PhyPos = realNewPosition;
141      }
142
143      BufferStartPos = alignedPos;
144      UInt32 readSize = kClusterSize;
145      if (File.LengthDefined)
146        readSize = (UInt32)MyMin(File.Length - PhyPos, (UInt64)kClusterSize);
147
148      if (Buffer == 0)
149      {
150        Buffer = (Byte *)MidAlloc(kClusterSize);
151        if (Buffer == 0)
152          return E_OUTOFMEMORY;
153      }
154      bool result = File.Read1(Buffer, readSize, BufferSize);
155      if (!result)
156        return ConvertBoolToHRESULT(result);
157
158      if (BufferSize == 0)
159        return S_OK;
160      PhyPos += BufferSize;
161    }
162
163    if (VirtPos != PhyPos)
164    {
165      UInt64 realNewPosition;
166      bool result = File.Seek(VirtPos, FILE_BEGIN, realNewPosition);
167      if (!result)
168        return ConvertBoolToHRESULT(result);
169      PhyPos = VirtPos = realNewPosition;
170    }
171  }
172  #endif
173
174  UInt32 realProcessedSize;
175  bool result = File.ReadPart(data, size, realProcessedSize);
176  if (processedSize != NULL)
177    *processedSize = realProcessedSize;
178  #ifdef SUPPORT_DEVICE_FILE
179  VirtPos += realProcessedSize;
180  PhyPos += realProcessedSize;
181  #endif
182  return ConvertBoolToHRESULT(result);
183
184  #else
185
186  if (processedSize != NULL)
187    *processedSize = 0;
188  ssize_t res = File.Read(data, (size_t)size);
189  if (res == -1)
190    return E_FAIL;
191  if (processedSize != NULL)
192    *processedSize = (UInt32)res;
193  return S_OK;
194
195  #endif
196}
197
198#ifdef UNDER_CE
199STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
200{
201  size_t s2 = fread(data, 1, size, stdout);
202  if (processedSize != 0)
203    *processedSize = s2;
204  return (s2 = size) ? S_OK : E_FAIL;
205}
206#else
207STDMETHODIMP CStdInFileStream::Read(void *data, UInt32 size, UInt32 *processedSize)
208{
209  #ifdef _WIN32
210
211  DWORD realProcessedSize;
212  UInt32 sizeTemp = (1 << 20);
213  if (sizeTemp > size)
214    sizeTemp = size;
215  BOOL res = ::ReadFile(GetStdHandle(STD_INPUT_HANDLE), data, sizeTemp, &realProcessedSize, NULL);
216  if (processedSize != NULL)
217    *processedSize = realProcessedSize;
218  if (res == FALSE && GetLastError() == ERROR_BROKEN_PIPE)
219    return S_OK;
220  return ConvertBoolToHRESULT(res != FALSE);
221
222  #else
223
224  if (processedSize != NULL)
225    *processedSize = 0;
226  ssize_t res;
227  do
228  {
229    res = read(0, data, (size_t)size);
230  }
231  while (res < 0 && (errno == EINTR));
232  if (res == -1)
233    return E_FAIL;
234  if (processedSize != NULL)
235    *processedSize = (UInt32)res;
236  return S_OK;
237
238  #endif
239}
240
241#endif
242
243STDMETHODIMP CInFileStream::Seek(Int64 offset, UInt32 seekOrigin,
244    UInt64 *newPosition)
245{
246  if (seekOrigin >= 3)
247    return STG_E_INVALIDFUNCTION;
248
249  #ifdef USE_WIN_FILE
250
251  #ifdef SUPPORT_DEVICE_FILE
252  if (File.IsDeviceFile)
253  {
254    UInt64 newVirtPos = offset;
255    switch(seekOrigin)
256    {
257      case STREAM_SEEK_SET: break;
258      case STREAM_SEEK_CUR: newVirtPos += VirtPos; break;
259      case STREAM_SEEK_END: newVirtPos += File.Length; break;
260      default: return STG_E_INVALIDFUNCTION;
261    }
262    VirtPos = newVirtPos;
263    if (newPosition)
264      *newPosition = newVirtPos;
265    return S_OK;
266  }
267  #endif
268
269  UInt64 realNewPosition;
270  bool result = File.Seek(offset, seekOrigin, realNewPosition);
271
272  #ifdef SUPPORT_DEVICE_FILE
273  PhyPos = VirtPos = realNewPosition;
274  #endif
275
276  if (newPosition != NULL)
277    *newPosition = realNewPosition;
278  return ConvertBoolToHRESULT(result);
279
280  #else
281
282  off_t res = File.Seek(offset, seekOrigin);
283  if (res == -1)
284    return E_FAIL;
285  if (newPosition != NULL)
286    *newPosition = (UInt64)res;
287  return S_OK;
288
289  #endif
290}
291
292STDMETHODIMP CInFileStream::GetSize(UInt64 *size)
293{
294  return ConvertBoolToHRESULT(File.GetLength(*size));
295}
296
297
298//////////////////////////
299// COutFileStream
300
301HRESULT COutFileStream::Close()
302{
303  return ConvertBoolToHRESULT(File.Close());
304}
305
306STDMETHODIMP COutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
307{
308  #ifdef USE_WIN_FILE
309
310  UInt32 realProcessedSize;
311  bool result = File.WritePart(data, size, realProcessedSize);
312  ProcessedSize += realProcessedSize;
313  if (processedSize != NULL)
314    *processedSize = realProcessedSize;
315  return ConvertBoolToHRESULT(result);
316
317  #else
318
319  if (processedSize != NULL)
320    *processedSize = 0;
321  ssize_t res = File.Write(data, (size_t)size);
322  if (res == -1)
323    return E_FAIL;
324  if (processedSize != NULL)
325    *processedSize = (UInt32)res;
326  ProcessedSize += res;
327  return S_OK;
328
329  #endif
330}
331
332STDMETHODIMP COutFileStream::Seek(Int64 offset, UInt32 seekOrigin, UInt64 *newPosition)
333{
334  if (seekOrigin >= 3)
335    return STG_E_INVALIDFUNCTION;
336  #ifdef USE_WIN_FILE
337
338  UInt64 realNewPosition;
339  bool result = File.Seek(offset, seekOrigin, realNewPosition);
340  if (newPosition != NULL)
341    *newPosition = realNewPosition;
342  return ConvertBoolToHRESULT(result);
343
344  #else
345
346  off_t res = File.Seek(offset, seekOrigin);
347  if (res == -1)
348    return E_FAIL;
349  if (newPosition != NULL)
350    *newPosition = (UInt64)res;
351  return S_OK;
352
353  #endif
354}
355
356STDMETHODIMP COutFileStream::SetSize(UInt64 newSize)
357{
358  #ifdef USE_WIN_FILE
359  UInt64 currentPos;
360  if (!File.Seek(0, FILE_CURRENT, currentPos))
361    return E_FAIL;
362  bool result = File.SetLength(newSize);
363  UInt64 currentPos2;
364  result = result && File.Seek(currentPos, currentPos2);
365  return result ? S_OK : E_FAIL;
366  #else
367  return E_FAIL;
368  #endif
369}
370
371#ifdef UNDER_CE
372STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
373{
374  size_t s2 = fwrite(data, 1, size, stdout);
375  if (processedSize != 0)
376    *processedSize = s2;
377  return (s2 = size) ? S_OK : E_FAIL;
378}
379#else
380STDMETHODIMP CStdOutFileStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
381{
382  if (processedSize != NULL)
383    *processedSize = 0;
384
385  #ifdef _WIN32
386  UInt32 realProcessedSize;
387  BOOL res = TRUE;
388  if (size > 0)
389  {
390    // Seems that Windows doesn't like big amounts writing to stdout.
391    // So we limit portions by 32KB.
392    UInt32 sizeTemp = (1 << 15);
393    if (sizeTemp > size)
394      sizeTemp = size;
395    res = ::WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
396        data, sizeTemp, (DWORD *)&realProcessedSize, NULL);
397    size -= realProcessedSize;
398    data = (const void *)((const Byte *)data + realProcessedSize);
399    if (processedSize != NULL)
400      *processedSize += realProcessedSize;
401  }
402  return ConvertBoolToHRESULT(res != FALSE);
403
404  #else
405
406  ssize_t res;
407  do
408  {
409    res = write(1, data, (size_t)size);
410  }
411  while (res < 0 && (errno == EINTR));
412  if (res == -1)
413    return E_FAIL;
414  if (processedSize != NULL)
415    *processedSize = (UInt32)res;
416  return S_OK;
417
418  return S_OK;
419  #endif
420}
421
422#endif
423