1 /*****************************************************************************/
2 /* FileStream.cpp                         Copyright (c) Ladislav Zezula 2010 */
3 /*---------------------------------------------------------------------------*/
4 /* File stream support for StormLib                                          */
5 /*                                                                           */
6 /* Windows support: Written by Ladislav Zezula                               */
7 /* Mac support:     Written by Sam Wilkins                                   */
8 /* Linux support:   Written by Sam Wilkins and Ivan Komissarov               */
9 /* Big-endian:      Written & debugged by Sam Wilkins                        */
10 /*---------------------------------------------------------------------------*/
11 /*   Date    Ver   Who  Comment                                              */
12 /* --------  ----  ---  -------                                              */
13 /* 11.06.10  1.00  Lad  Derived from StormPortMac.cpp and StormPortLinux.cpp */
14 /*****************************************************************************/
15 
16 #define __STORMLIB_SELF__
17 #include "StormLib.h"
18 #include "StormCommon.h"
19 #include "FileStream.h"
20 
21 #ifdef __vita__
22 #include <psp2/rtc.h>
23 #define mmap(ptr, size, c, d, e, f) malloc(size)
24 #define munmap(ptr, size) free(ptr)
25 #define PROT_READ 0
26 #define MAP_PRIVATE 0
27 #endif
28 
29 
30 
31 #ifdef _MSC_VER
32 #pragma comment(lib, "wininet.lib")             // Internet functions for HTTP stream
33 #pragma warning(disable: 4800)                  // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning)
34 #endif
35 
36 //-----------------------------------------------------------------------------
37 // Local defines
38 
39 #ifndef INVALID_HANDLE_VALUE
40 #define INVALID_HANDLE_VALUE ((HANDLE)-1)
41 #endif
42 
43 //-----------------------------------------------------------------------------
44 // Local functions - platform-specific functions
45 
46 #ifdef FULL
47 #ifndef PLATFORM_WINDOWS
48 static DWORD nLastError = ERROR_SUCCESS;
49 
GetLastError()50 DWORD GetLastError()
51 {
52     return nLastError;
53 }
54 
SetLastError(DWORD nError)55 void SetLastError(DWORD nError)
56 {
57     nLastError = nError;
58 }
59 #endif
60 #endif
61 
StringToInt(const char * szString)62 static DWORD StringToInt(const char * szString)
63 {
64     DWORD dwValue = 0;
65 
66     while('0' <= szString[0] && szString[0] <= '9')
67     {
68         dwValue = (dwValue * 10) + (szString[0] - '9');
69         szString++;
70     }
71 
72     return dwValue;
73 }
74 
75 //-----------------------------------------------------------------------------
76 // Dummy init function
77 
BaseNone_Init(TFileStream *)78 static void BaseNone_Init(TFileStream *)
79 {
80     // Nothing here
81 }
82 
83 //-----------------------------------------------------------------------------
84 // Local functions - base file support
85 
BaseFile_Create(TFileStream * pStream)86 static bool BaseFile_Create(TFileStream * pStream)
87 {
88 #ifdef PLATFORM_WINDOWS
89     {
90         DWORD dwWriteShare = (pStream->dwFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0;
91 
92         pStream->Base.File.hFile = CreateFile(pStream->szFileName,
93                                               GENERIC_READ | GENERIC_WRITE,
94                                               dwWriteShare | FILE_SHARE_READ,
95                                               NULL,
96                                               CREATE_ALWAYS,
97                                               0,
98                                               NULL);
99         if(pStream->Base.File.hFile == INVALID_HANDLE_VALUE)
100             return false;
101     }
102 #endif
103 
104 #if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA)
105     {
106         intptr_t handle;
107 
108         handle = open(pStream->szFileName, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
109         if(handle == -1)
110         {
111             nLastError = errno;
112             pStream->Base.File.hFile = INVALID_HANDLE_VALUE; // BUGFIX (devilutionX)
113             return false;
114         }
115 
116         pStream->Base.File.hFile = (HANDLE)handle;
117     }
118 #endif
119 
120     // Reset the file size and position
121     pStream->Base.File.FileSize = 0;
122     pStream->Base.File.FilePos = 0;
123     return true;
124 }
125 
BaseFile_Open(TFileStream * pStream,const TCHAR * szFileName,DWORD dwStreamFlags)126 static bool BaseFile_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags)
127 {
128 #ifdef PLATFORM_WINDOWS
129     {
130         ULARGE_INTEGER FileSize;
131         DWORD dwWriteAccess = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? 0 : FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES;
132         DWORD dwWriteShare = (dwStreamFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0;
133 
134         // Open the file
135         pStream->Base.File.hFile = CreateFile(szFileName,
136                                               FILE_READ_DATA | FILE_READ_ATTRIBUTES | dwWriteAccess,
137                                               FILE_SHARE_READ | dwWriteShare,
138                                               NULL,
139                                               OPEN_EXISTING,
140                                               0,
141                                               NULL);
142         if(pStream->Base.File.hFile == INVALID_HANDLE_VALUE)
143             return false;
144 
145         // Query the file size
146         FileSize.LowPart = GetFileSize(pStream->Base.File.hFile, &FileSize.HighPart);
147         pStream->Base.File.FileSize = FileSize.QuadPart;
148 
149         // Query last write time
150         GetFileTime(pStream->Base.File.hFile, NULL, NULL, (LPFILETIME)&pStream->Base.File.FileTime);
151     }
152 #endif
153 
154 #if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA)
155     {
156         struct stat64 fileinfo;
157         int oflag = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? O_RDONLY : O_RDWR;
158         intptr_t handle;
159 
160         // Open the file
161         handle = open(szFileName, oflag | O_LARGEFILE);
162         if(handle == -1)
163         {
164             nLastError = errno;
165             pStream->Base.File.hFile = INVALID_HANDLE_VALUE; // BUGFIX (devilutionX)
166             return false;
167         }
168 
169         // Get the file size
170         if(fstat64(handle, &fileinfo) == -1)
171         {
172             nLastError = errno;
173             close(handle);
174             pStream->Base.File.hFile = INVALID_HANDLE_VALUE; // BUGFIX (devilutionX)
175             return false;
176         }
177 
178         // time_t is number of seconds since 1.1.1970, UTC.
179         // 1 second = 10000000 (decimal) in FILETIME
180         // Set the start to 1.1.1970 00:00:00
181         pStream->Base.File.FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime);
182         pStream->Base.File.FileSize = (ULONGLONG)fileinfo.st_size;
183         pStream->Base.File.hFile = (HANDLE)handle;
184     }
185 #endif
186 
187     // Reset the file position
188     pStream->Base.File.FilePos = 0;
189     return true;
190 }
191 
BaseFile_Read(TFileStream * pStream,ULONGLONG * pByteOffset,void * pvBuffer,DWORD dwBytesToRead)192 static bool BaseFile_Read(
193     TFileStream * pStream,                  // Pointer to an open stream
194     ULONGLONG * pByteOffset,                // Pointer to file byte offset. If NULL, it reads from the current position
195     void * pvBuffer,                        // Pointer to data to be read
196     DWORD dwBytesToRead)                    // Number of bytes to read from the file
197 {
198     ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos;
199     DWORD dwBytesRead = 0;                  // Must be set by platform-specific code
200 
201 #ifdef PLATFORM_WINDOWS
202     {
203         // Note: StormLib no longer supports Windows 9x.
204         // Thus, we can use the OVERLAPPED structure to specify
205         // file offset to read from file. This allows us to skip
206         // one system call to SetFilePointer
207 
208         // Update the byte offset
209         pStream->Base.File.FilePos = ByteOffset;
210 
211         // Read the data
212         if(dwBytesToRead != 0)
213         {
214             OVERLAPPED Overlapped;
215 
216             Overlapped.OffsetHigh = (DWORD)(ByteOffset >> 32);
217             Overlapped.Offset = (DWORD)ByteOffset;
218             Overlapped.hEvent = NULL;
219             if(!ReadFile(pStream->Base.File.hFile, pvBuffer, dwBytesToRead, &dwBytesRead, &Overlapped))
220                 return false;
221         }
222     }
223 #endif
224 
225 #if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA)
226     {
227         ssize_t bytes_read;
228 
229         // If the byte offset is different from the current file position,
230         // we have to update the file position   xxx
231         if(ByteOffset != pStream->Base.File.FilePos)
232         {
233             lseek64((intptr_t)pStream->Base.File.hFile, (off64_t)(ByteOffset), SEEK_SET);
234             pStream->Base.File.FilePos = ByteOffset;
235         }
236 
237         // Perform the read operation
238         if(dwBytesToRead != 0)
239         {
240             bytes_read = read((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToRead);
241             if(bytes_read == -1)
242             {
243                 nLastError = errno;
244                 return false;
245             }
246 
247             dwBytesRead = (DWORD)(size_t)bytes_read;
248         }
249     }
250 #endif
251 
252     // Increment the current file position by number of bytes read
253     // If the number of bytes read doesn't match to required amount, return false
254     pStream->Base.File.FilePos = ByteOffset + dwBytesRead;
255     if(dwBytesRead != dwBytesToRead)
256         SetLastError(ERROR_HANDLE_EOF);
257     return (dwBytesRead == dwBytesToRead);
258 }
259 
260 /**
261  * \a pStream Pointer to an open stream
262  * \a pByteOffset Pointer to file byte offset. If NULL, writes to current position
263  * \a pvBuffer Pointer to data to be written
264  * \a dwBytesToWrite Number of bytes to write to the file
265  */
266 
BaseFile_Write(TFileStream * pStream,ULONGLONG * pByteOffset,const void * pvBuffer,DWORD dwBytesToWrite)267 static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite)
268 {
269     ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos;
270     DWORD dwBytesWritten = 0;               // Must be set by platform-specific code
271 
272 #ifdef PLATFORM_WINDOWS
273     {
274         // Note: StormLib no longer supports Windows 9x.
275         // Thus, we can use the OVERLAPPED structure to specify
276         // file offset to read from file. This allows us to skip
277         // one system call to SetFilePointer
278 
279         // Update the byte offset
280         pStream->Base.File.FilePos = ByteOffset;
281 
282         // Read the data
283         if(dwBytesToWrite != 0)
284         {
285             OVERLAPPED Overlapped;
286 
287             Overlapped.OffsetHigh = (DWORD)(ByteOffset >> 32);
288             Overlapped.Offset = (DWORD)ByteOffset;
289             Overlapped.hEvent = NULL;
290             if(!WriteFile(pStream->Base.File.hFile, pvBuffer, dwBytesToWrite, &dwBytesWritten, &Overlapped))
291                 return false;
292         }
293     }
294 #endif
295 
296 #if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA)
297     {
298         ssize_t bytes_written;
299 
300         // If the byte offset is different from the current file position,
301         // we have to update the file position
302         if(ByteOffset != pStream->Base.File.FilePos)
303         {
304             lseek64((intptr_t)pStream->Base.File.hFile, (off64_t)(ByteOffset), SEEK_SET);
305             pStream->Base.File.FilePos = ByteOffset;
306         }
307 
308         // Perform the read operation
309         bytes_written = write((intptr_t)pStream->Base.File.hFile, pvBuffer, (size_t)dwBytesToWrite);
310         if(bytes_written == -1)
311         {
312             nLastError = errno;
313             return false;
314         }
315 
316         dwBytesWritten = (DWORD)(size_t)bytes_written;
317     }
318 #endif
319 
320     // Increment the current file position by number of bytes read
321     pStream->Base.File.FilePos = ByteOffset + dwBytesWritten;
322 
323     // Also modify the file size, if needed
324     if(pStream->Base.File.FilePos > pStream->Base.File.FileSize)
325         pStream->Base.File.FileSize = pStream->Base.File.FilePos;
326 
327     if(dwBytesWritten != dwBytesToWrite)
328         SetLastError(ERROR_DISK_FULL);
329     return (dwBytesWritten == dwBytesToWrite);
330 }
331 
332 /**
333  * \a pStream Pointer to an open stream
334  * \a NewFileSize New size of the file
335  */
BaseFile_Resize(TFileStream * pStream,ULONGLONG NewFileSize)336 static bool BaseFile_Resize(TFileStream * pStream, ULONGLONG NewFileSize)
337 {
338 #ifdef PLATFORM_WINDOWS
339     {
340         LONG FileSizeHi = (LONG)(NewFileSize >> 32);
341         LONG FileSizeLo;
342         DWORD dwNewPos;
343         bool bResult;
344 
345         // Set the position at the new file size
346         dwNewPos = SetFilePointer(pStream->Base.File.hFile, (LONG)NewFileSize, &FileSizeHi, FILE_BEGIN);
347         if(dwNewPos == INVALID_SET_FILE_POINTER && GetLastError() != ERROR_SUCCESS)
348             return false;
349 
350         // Set the current file pointer as the end of the file
351         bResult = (bool)SetEndOfFile(pStream->Base.File.hFile);
352         if(bResult)
353             pStream->Base.File.FileSize = NewFileSize;
354 
355         // Restore the file position
356         FileSizeHi = (LONG)(pStream->Base.File.FilePos >> 32);
357         FileSizeLo = (LONG)(pStream->Base.File.FilePos);
358         SetFilePointer(pStream->Base.File.hFile, FileSizeLo, &FileSizeHi, FILE_BEGIN);
359         return bResult;
360     }
361 #endif
362 
363 #if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA)
364     {
365         if(ftruncate64((intptr_t)pStream->Base.File.hFile, (off64_t)NewFileSize) == -1)
366         {
367             nLastError = errno;
368             return false;
369         }
370 
371         pStream->Base.File.FileSize = NewFileSize;
372         return true;
373     }
374 #endif
375 }
376 
377 // Gives the current file size
BaseFile_GetSize(TFileStream * pStream,ULONGLONG * pFileSize)378 static bool BaseFile_GetSize(TFileStream * pStream, ULONGLONG * pFileSize)
379 {
380     // Note: Used by all thre base providers.
381     // Requires the TBaseData union to have the same layout for all three base providers
382     *pFileSize = pStream->Base.File.FileSize;
383     return true;
384 }
385 
386 // Gives the current file position
BaseFile_GetPos(TFileStream * pStream,ULONGLONG * pByteOffset)387 static bool BaseFile_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset)
388 {
389     // Note: Used by all thre base providers.
390     // Requires the TBaseData union to have the same layout for all three base providers
391     *pByteOffset = pStream->Base.File.FilePos;
392     return true;
393 }
394 
395 // Renames the file pointed by pStream so that it contains data from pNewStream
BaseFile_Replace(TFileStream * pStream,TFileStream * pNewStream)396 static bool BaseFile_Replace(TFileStream * pStream, TFileStream * pNewStream)
397 {
398 #ifdef PLATFORM_WINDOWS
399     // Delete the original stream file. Don't check the result value,
400     // because if the file doesn't exist, it would fail
401     DeleteFile(pStream->szFileName);
402 
403     // Rename the new file to the old stream's file
404     return (bool)MoveFile(pNewStream->szFileName, pStream->szFileName);
405 #endif
406 
407 #if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA)
408     // "rename" on Linux also works if the target file exists
409     if(rename(pNewStream->szFileName, pStream->szFileName) == -1)
410     {
411         nLastError = errno;
412         return false;
413     }
414 
415     return true;
416 #endif
417 }
418 
BaseFile_Close(TFileStream * pStream)419 static void BaseFile_Close(TFileStream * pStream)
420 {
421     if(pStream->Base.File.hFile != INVALID_HANDLE_VALUE)
422     {
423 #ifdef PLATFORM_WINDOWS
424         CloseHandle(pStream->Base.File.hFile);
425 #endif
426 
427 #if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA)
428         close((intptr_t)pStream->Base.File.hFile);
429 #endif
430     }
431 
432     // Also invalidate the handle
433     pStream->Base.File.hFile = INVALID_HANDLE_VALUE;
434 }
435 
436 // Initializes base functions for the disk file
BaseFile_Init(TFileStream * pStream)437 static void BaseFile_Init(TFileStream * pStream)
438 {
439     pStream->BaseCreate  = BaseFile_Create;
440     pStream->BaseOpen    = BaseFile_Open;
441     pStream->BaseRead    = BaseFile_Read;
442     pStream->BaseWrite   = BaseFile_Write;
443     pStream->BaseResize  = BaseFile_Resize;
444     pStream->BaseGetSize = BaseFile_GetSize;
445     pStream->BaseGetPos  = BaseFile_GetPos;
446     pStream->BaseClose   = BaseFile_Close;
447 }
448 
449 //-----------------------------------------------------------------------------
450 // Local functions - base memory-mapped file support
451 
BaseMap_Open(TFileStream * pStream,const TCHAR * szFileName,DWORD dwStreamFlags)452 static bool BaseMap_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags)
453 {
454 #ifdef PLATFORM_WINDOWS
455 
456     ULARGE_INTEGER FileSize;
457     HANDLE hFile;
458     HANDLE hMap;
459     bool bResult = false;
460 
461     // Keep compiler happy
462     dwStreamFlags = dwStreamFlags;
463 
464     // Open the file for read access
465     hFile = CreateFile(szFileName, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
466     if(hFile != INVALID_HANDLE_VALUE)
467     {
468         // Retrieve file size. Don't allow mapping file of a zero size.
469         FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart);
470         if(FileSize.QuadPart != 0)
471         {
472             // Now create mapping object
473             hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
474             if(hMap != NULL)
475             {
476                 // Map the entire view into memory
477                 // Note that this operation will fail if the file can't fit
478                 // into usermode address space
479                 pStream->Base.Map.pbFile = (LPBYTE)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
480                 if(pStream->Base.Map.pbFile != NULL)
481                 {
482                     // Retrieve file time
483                     GetFileTime(hFile, NULL, NULL, (LPFILETIME)&pStream->Base.Map.FileTime);
484 
485                     // Retrieve file size and position
486                     pStream->Base.Map.FileSize = FileSize.QuadPart;
487                     pStream->Base.Map.FilePos = 0;
488                     bResult = true;
489                 }
490 
491                 // Close the map handle
492                 CloseHandle(hMap);
493             }
494         }
495 
496         // Close the file handle
497         CloseHandle(hFile);
498     }
499 
500     // If the file is not there and is not available for random access,
501     // report error
502     if(bResult == false)
503         return false;
504 #endif
505 
506 #if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA)
507     struct stat64 fileinfo;
508     intptr_t handle;
509     bool bResult = false;
510 
511     // Open the file
512     handle = open(szFileName, O_RDONLY);
513     if(handle != -1)
514     {
515         // Get the file size
516         if(fstat64(handle, &fileinfo) != -1)
517         {
518 #if !defined(PLATFORM_AMIGA)
519 #if defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA)
520             pStream->Base.Map.pbFile = (LPBYTE)malloc((size_t)fileinfo.st_size);
521 #else
522             pStream->Base.Map.pbFile = (LPBYTE)mmap(NULL, (size_t)fileinfo.st_size, PROT_READ, MAP_PRIVATE, handle, 0);
523 #endif
524             if(pStream->Base.Map.pbFile != NULL)
525             {
526                 // time_t is number of seconds since 1.1.1970, UTC.
527                 // 1 second = 10000000 (decimal) in FILETIME
528                 // Set the start to 1.1.1970 00:00:00
529                 pStream->Base.Map.FileTime = 0x019DB1DED53E8000ULL + (10000000 * fileinfo.st_mtime);
530                 pStream->Base.Map.FileSize = (ULONGLONG)fileinfo.st_size;
531                 pStream->Base.Map.FilePos = 0;
532                 bResult = true;
533             }
534 #endif
535         }
536         close(handle);
537     }
538 
539     // Did the mapping fail?
540     if(bResult == false)
541     {
542         nLastError = errno;
543         return false;
544     }
545 #endif
546 
547     return true;
548 }
549 
BaseMap_Read(TFileStream * pStream,ULONGLONG * pByteOffset,void * pvBuffer,DWORD dwBytesToRead)550 static bool BaseMap_Read(
551     TFileStream * pStream,                  // Pointer to an open stream
552     ULONGLONG * pByteOffset,                // Pointer to file byte offset. If NULL, it reads from the current position
553     void * pvBuffer,                        // Pointer to data to be read
554     DWORD dwBytesToRead)                    // Number of bytes to read from the file
555 {
556     ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Map.FilePos;
557 
558     // Do we have to read anything at all?
559     if(dwBytesToRead != 0)
560     {
561         // Don't allow reading past file size
562         if((ByteOffset + dwBytesToRead) > pStream->Base.Map.FileSize)
563             return false;
564 
565         // Copy the required data
566         memcpy(pvBuffer, pStream->Base.Map.pbFile + (size_t)ByteOffset, dwBytesToRead);
567     }
568 
569     // Move the current file position
570     pStream->Base.Map.FilePos += dwBytesToRead;
571     return true;
572 }
573 
BaseMap_Close(TFileStream * pStream)574 static void BaseMap_Close(TFileStream * pStream)
575 {
576 #ifdef PLATFORM_WINDOWS
577     if(pStream->Base.Map.pbFile != NULL)
578         UnmapViewOfFile(pStream->Base.Map.pbFile);
579 #endif
580 
581 #if (defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU)) && !defined(PLATFORM_AMIGA) && !defined(PLATFORM_SWITCH) && !defined(PLATFORM_CTR) && !defined(PLATFORM_VITA)
582     //Todo(Amiga): Fix a proper solution for this
583     if(pStream->Base.Map.pbFile != NULL)
584         munmap(pStream->Base.Map.pbFile, (size_t )pStream->Base.Map.FileSize);
585 #elif defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA)
586     if(pStream->Base.Map.pbFile != NULL)
587         free(pStream->Base.Map.pbFile);
588 #endif
589 
590     pStream->Base.Map.pbFile = NULL;
591 }
592 
593 // Initializes base functions for the mapped file
BaseMap_Init(TFileStream * pStream)594 static void BaseMap_Init(TFileStream * pStream)
595 {
596     // Supply the file stream functions
597     pStream->BaseOpen    = BaseMap_Open;
598     pStream->BaseRead    = BaseMap_Read;
599     pStream->BaseGetSize = BaseFile_GetSize;    // Reuse BaseFile function
600     pStream->BaseGetPos  = BaseFile_GetPos;     // Reuse BaseFile function
601     pStream->BaseClose   = BaseMap_Close;
602 
603     // Mapped files are read-only
604     pStream->dwFlags |= STREAM_FLAG_READ_ONLY;
605 }
606 
607 //-----------------------------------------------------------------------------
608 // Local functions - base HTTP file support
609 
BaseHttp_ExtractServerName(const TCHAR * szFileName,TCHAR * szServerName)610 static const TCHAR * BaseHttp_ExtractServerName(const TCHAR * szFileName, TCHAR * szServerName)
611 {
612     // Check for HTTP
613     if(!_tcsnicmp(szFileName, _T("http://"), 7))
614         szFileName += 7;
615 
616     // Cut off the server name
617     if(szServerName != NULL)
618     {
619         while(szFileName[0] != 0 && szFileName[0] != _T('/'))
620             *szServerName++ = *szFileName++;
621         *szServerName = 0;
622     }
623     else
624     {
625         while(szFileName[0] != 0 && szFileName[0] != _T('/'))
626             szFileName++;
627     }
628 
629     // Return the remainder
630     return szFileName;
631 }
632 
BaseHttp_Open(TFileStream * pStream,const TCHAR * szFileName,DWORD dwStreamFlags)633 static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags)
634 {
635 #ifdef PLATFORM_WINDOWS
636 
637     HINTERNET hRequest;
638     DWORD dwTemp = 0;
639 
640     // Keep compiler happy
641     dwStreamFlags = dwStreamFlags;
642 
643     // Don't connect to the internet
644     if(!InternetGetConnectedState(&dwTemp, 0))
645         return false;
646 
647     // Initiate the connection to the internet
648     pStream->Base.Http.hInternet = InternetOpen(_T("StormLib HTTP MPQ reader"),
649                                                 INTERNET_OPEN_TYPE_PRECONFIG,
650                                                 NULL,
651                                                 NULL,
652                                                 0);
653     if(pStream->Base.Http.hInternet != NULL)
654     {
655         TCHAR szServerName[MAX_PATH];
656         DWORD dwFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_CACHE_WRITE;
657 
658         // Initiate connection with the server
659         szFileName = BaseHttp_ExtractServerName(szFileName, szServerName);
660         pStream->Base.Http.hConnect = InternetConnect(pStream->Base.Http.hInternet,
661                                                       szServerName,
662                                                       INTERNET_DEFAULT_HTTP_PORT,
663                                                       NULL,
664                                                       NULL,
665                                                       INTERNET_SERVICE_HTTP,
666                                                       dwFlags,
667                                                       0);
668         if(pStream->Base.Http.hConnect != NULL)
669         {
670             // Open HTTP request to the file
671             hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0);
672             if(hRequest != NULL)
673             {
674                 if(HttpSendRequest(hRequest, NULL, 0, NULL, 0))
675                 {
676                     ULONGLONG FileTime = 0;
677                     DWORD dwFileSize = 0;
678                     DWORD dwDataSize;
679                     DWORD dwIndex = 0;
680 
681                     // Check if the MPQ has Last Modified field
682                     dwDataSize = sizeof(ULONGLONG);
683                     if(HttpQueryInfo(hRequest, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, &FileTime, &dwDataSize, &dwIndex))
684                         pStream->Base.Http.FileTime = FileTime;
685 
686                     // Verify if the server supports random access
687                     dwDataSize = sizeof(DWORD);
688                     if(HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwFileSize, &dwDataSize, &dwIndex))
689                     {
690                         if(dwFileSize != 0)
691                         {
692                             InternetCloseHandle(hRequest);
693                             pStream->Base.Http.FileSize = dwFileSize;
694                             pStream->Base.Http.FilePos = 0;
695                             return true;
696                         }
697                     }
698                 }
699 
700                 // Close the request
701                 InternetCloseHandle(hRequest);
702             }
703 
704             // Close the connection handle
705             InternetCloseHandle(pStream->Base.Http.hConnect);
706             pStream->Base.Http.hConnect = NULL;
707         }
708 
709         // Close the internet handle
710         InternetCloseHandle(pStream->Base.Http.hInternet);
711         pStream->Base.Http.hInternet = NULL;
712     }
713 
714     // If the file is not there or is not available for random access, report error
715     pStream->BaseClose(pStream);
716     return false;
717 
718 #else
719 
720     // Not supported
721     SetLastError(ERROR_NOT_SUPPORTED);
722     pStream = pStream;
723     return false;
724 
725 #endif
726 }
727 
BaseHttp_Read(TFileStream * pStream,ULONGLONG * pByteOffset,void * pvBuffer,DWORD dwBytesToRead)728 static bool BaseHttp_Read(
729     TFileStream * pStream,                  // Pointer to an open stream
730     ULONGLONG * pByteOffset,                // Pointer to file byte offset. If NULL, it reads from the current position
731     void * pvBuffer,                        // Pointer to data to be read
732     DWORD dwBytesToRead)                    // Number of bytes to read from the file
733 {
734 #ifdef PLATFORM_WINDOWS
735     ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Http.FilePos;
736     DWORD dwTotalBytesRead = 0;
737 
738     // Do we have to read anything at all?
739     if(dwBytesToRead != 0)
740     {
741         HINTERNET hRequest;
742         LPCTSTR szFileName;
743         LPBYTE pbBuffer = (LPBYTE)pvBuffer;
744         TCHAR szRangeRequest[0x80];
745         DWORD dwStartOffset = (DWORD)ByteOffset;
746         DWORD dwEndOffset = dwStartOffset + dwBytesToRead;
747 
748         // Open HTTP request to the file
749         szFileName = BaseHttp_ExtractServerName(pStream->szFileName, NULL);
750         hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0);
751         if(hRequest != NULL)
752         {
753             // Add range request to the HTTP headers
754             // http://www.clevercomponents.com/articles/article015/resuming.asp
755             _stprintf(szRangeRequest, _T("Range: bytes=%u-%u"), (unsigned int)dwStartOffset, (unsigned int)dwEndOffset);
756             HttpAddRequestHeaders(hRequest, szRangeRequest, 0xFFFFFFFF, HTTP_ADDREQ_FLAG_ADD_IF_NEW);
757 
758             // Send the request to the server
759             if(HttpSendRequest(hRequest, NULL, 0, NULL, 0))
760             {
761                 while(dwTotalBytesRead < dwBytesToRead)
762                 {
763                     DWORD dwBlockBytesToRead = dwBytesToRead - dwTotalBytesRead;
764                     DWORD dwBlockBytesRead = 0;
765 
766                     // Read the block from the file
767                     if(dwBlockBytesToRead > 0x200)
768                         dwBlockBytesToRead = 0x200;
769                     InternetReadFile(hRequest, pbBuffer, dwBlockBytesToRead, &dwBlockBytesRead);
770 
771                     // Check for end
772                     if(dwBlockBytesRead == 0)
773                         break;
774 
775                     // Move buffers
776                     dwTotalBytesRead += dwBlockBytesRead;
777                     pbBuffer += dwBlockBytesRead;
778                 }
779             }
780             InternetCloseHandle(hRequest);
781         }
782     }
783 
784     // Increment the current file position by number of bytes read
785     pStream->Base.Http.FilePos = ByteOffset + dwTotalBytesRead;
786 
787     // If the number of bytes read doesn't match the required amount, return false
788     if(dwTotalBytesRead != dwBytesToRead)
789         SetLastError(ERROR_HANDLE_EOF);
790     return (dwTotalBytesRead == dwBytesToRead);
791 
792 #else
793 
794     // Not supported
795     pStream = pStream;
796     pByteOffset = pByteOffset;
797     pvBuffer = pvBuffer;
798     dwBytesToRead = dwBytesToRead;
799     SetLastError(ERROR_NOT_SUPPORTED);
800     return false;
801 
802 #endif
803 }
804 
BaseHttp_Close(TFileStream * pStream)805 static void BaseHttp_Close(TFileStream * pStream)
806 {
807 #ifdef PLATFORM_WINDOWS
808     if(pStream->Base.Http.hConnect != NULL)
809         InternetCloseHandle(pStream->Base.Http.hConnect);
810     pStream->Base.Http.hConnect = NULL;
811 
812     if(pStream->Base.Http.hInternet != NULL)
813         InternetCloseHandle(pStream->Base.Http.hInternet);
814     pStream->Base.Http.hInternet = NULL;
815 #else
816     pStream = pStream;
817 #endif
818 }
819 
820 // Initializes base functions for the mapped file
BaseHttp_Init(TFileStream * pStream)821 static void BaseHttp_Init(TFileStream * pStream)
822 {
823     // Supply the stream functions
824     pStream->BaseOpen    = BaseHttp_Open;
825     pStream->BaseRead    = BaseHttp_Read;
826     pStream->BaseGetSize = BaseFile_GetSize;    // Reuse BaseFile function
827     pStream->BaseGetPos  = BaseFile_GetPos;     // Reuse BaseFile function
828     pStream->BaseClose   = BaseHttp_Close;
829 
830     // HTTP files are read-only
831     pStream->dwFlags |= STREAM_FLAG_READ_ONLY;
832 }
833 
834 //-----------------------------------------------------------------------------
835 // Local functions - base block-based support
836 
837 // Generic function that loads blocks from the file
838 // The function groups the block with the same availability,
839 // so the called BlockRead can finish the request in a single system call
BlockStream_Read(TBlockStream * pStream,ULONGLONG * pByteOffset,void * pvBuffer,DWORD dwBytesToRead)840 static bool BlockStream_Read(
841     TBlockStream * pStream,                 // Pointer to an open stream
842     ULONGLONG * pByteOffset,                // Pointer to file byte offset. If NULL, it reads from the current position
843     void * pvBuffer,                        // Pointer to data to be read
844     DWORD dwBytesToRead)                    // Number of bytes to read from the file
845 {
846     ULONGLONG BlockOffset0;
847     ULONGLONG BlockOffset;
848     ULONGLONG ByteOffset;
849     ULONGLONG EndOffset;
850     LPBYTE TransferBuffer;
851     LPBYTE BlockBuffer;
852     DWORD BlockBufferOffset;                // Offset of the desired data in the block buffer
853     DWORD BytesNeeded;                      // Number of bytes that really need to be read
854     DWORD BlockSize = pStream->BlockSize;
855     DWORD BlockCount;
856     bool bPrevBlockAvailable;
857     bool bCallbackCalled = false;
858     bool bBlockAvailable;
859     bool bResult = true;
860 
861     // The base block read function must be present
862     assert(pStream->BlockRead != NULL);
863 
864     // NOP reading of zero bytes
865     if(dwBytesToRead == 0)
866         return true;
867 
868     // Get the current position in the stream
869     ByteOffset = (pByteOffset != NULL) ? pByteOffset[0] : pStream->StreamPos;
870     EndOffset = ByteOffset + dwBytesToRead;
871     if(EndOffset > pStream->StreamSize)
872     {
873         SetLastError(ERROR_HANDLE_EOF);
874         return false;
875     }
876 
877     // Calculate the block parameters
878     BlockOffset0 = BlockOffset = ByteOffset & ~((ULONGLONG)BlockSize - 1);
879     BlockCount  = (DWORD)(((EndOffset - BlockOffset) + (BlockSize - 1)) / BlockSize);
880     BytesNeeded = (DWORD)(EndOffset - BlockOffset);
881 
882     // Remember where we have our data
883     assert((BlockSize & (BlockSize - 1)) == 0);
884     BlockBufferOffset = (DWORD)(ByteOffset & (BlockSize - 1));
885 
886     // Allocate buffer for reading blocks
887     TransferBuffer = BlockBuffer = STORM_ALLOC(BYTE, (BlockCount * BlockSize));
888     if(TransferBuffer == NULL)
889     {
890         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
891         return false;
892     }
893 
894     // If all blocks are available, just read all blocks at once
895     if(pStream->IsComplete == 0)
896     {
897         // Now parse the blocks and send the block read request
898         // to all blocks with the same availability
899         assert(pStream->BlockCheck != NULL);
900         bPrevBlockAvailable = pStream->BlockCheck(pStream, BlockOffset);
901 
902         // Loop as long as we have something to read
903         while(BlockOffset < EndOffset)
904         {
905             // Determine availability of the next block
906             bBlockAvailable = pStream->BlockCheck(pStream, BlockOffset);
907 
908             // If the availability has changed, read all blocks up to this one
909             if(bBlockAvailable != bPrevBlockAvailable)
910             {
911                 // Call the file stream callback, if the block is not available
912                 if(pStream->pMaster && pStream->pfnCallback && bPrevBlockAvailable == false)
913                 {
914                     pStream->pfnCallback(pStream->UserData, BlockOffset0, (DWORD)(BlockOffset - BlockOffset0));
915                     bCallbackCalled = true;
916                 }
917 
918                 // Load the continuous blocks with the same availability
919                 assert(BlockOffset > BlockOffset0);
920                 bResult = pStream->BlockRead(pStream, BlockOffset0, BlockOffset, BlockBuffer, BytesNeeded, bPrevBlockAvailable);
921                 if(!bResult)
922                     break;
923 
924                 // Move the block offset
925                 BlockBuffer += (DWORD)(BlockOffset - BlockOffset0);
926                 BytesNeeded -= (DWORD)(BlockOffset - BlockOffset0);
927                 bPrevBlockAvailable = bBlockAvailable;
928                 BlockOffset0 = BlockOffset;
929             }
930 
931             // Move to the block offset in the stream
932             BlockOffset += BlockSize;
933         }
934 
935         // If there is a block(s) remaining to be read, do it
936         if(BlockOffset > BlockOffset0)
937         {
938             // Call the file stream callback, if the block is not available
939             if(pStream->pMaster && pStream->pfnCallback && bPrevBlockAvailable == false)
940             {
941                 pStream->pfnCallback(pStream->UserData, BlockOffset0, (DWORD)(BlockOffset - BlockOffset0));
942                 bCallbackCalled = true;
943             }
944 
945             // Read the complete blocks from the file
946             if(BlockOffset > pStream->StreamSize)
947                 BlockOffset = pStream->StreamSize;
948             bResult = pStream->BlockRead(pStream, BlockOffset0, BlockOffset, BlockBuffer, BytesNeeded, bPrevBlockAvailable);
949         }
950     }
951     else
952     {
953         // Read the complete blocks from the file
954         if(EndOffset > pStream->StreamSize)
955             EndOffset = pStream->StreamSize;
956         bResult = pStream->BlockRead(pStream, BlockOffset, EndOffset, BlockBuffer, BytesNeeded, true);
957     }
958 
959     // Now copy the data to the user buffer
960     if(bResult)
961     {
962         memcpy(pvBuffer, TransferBuffer + BlockBufferOffset, dwBytesToRead);
963         pStream->StreamPos = ByteOffset + dwBytesToRead;
964     }
965     else
966     {
967         // If the block read failed, set the last error
968         SetLastError(ERROR_FILE_INCOMPLETE);
969     }
970 
971     // Call the callback to indicate we are done
972     if(bCallbackCalled)
973         pStream->pfnCallback(pStream->UserData, 0, 0);
974 
975     // Free the block buffer and return
976     STORM_FREE(TransferBuffer);
977     return bResult;
978 }
979 
BlockStream_GetSize(TFileStream * pStream,ULONGLONG * pFileSize)980 static bool BlockStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize)
981 {
982     *pFileSize = pStream->StreamSize;
983     return true;
984 }
985 
BlockStream_GetPos(TFileStream * pStream,ULONGLONG * pByteOffset)986 static bool BlockStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset)
987 {
988     *pByteOffset = pStream->StreamPos;
989     return true;
990 }
991 
BlockStream_Close(TBlockStream * pStream)992 static void BlockStream_Close(TBlockStream * pStream)
993 {
994     // Free the data map, if any
995     if(pStream->FileBitmap != NULL)
996         STORM_FREE(pStream->FileBitmap);
997     pStream->FileBitmap = NULL;
998 
999     // Call the base class for closing the stream
1000     pStream->BaseClose(pStream);
1001 }
1002 
1003 //-----------------------------------------------------------------------------
1004 // File stream allocation function
1005 
1006 static STREAM_INIT StreamBaseInit[4] =
1007 {
1008     BaseFile_Init,
1009     BaseMap_Init,
1010     BaseHttp_Init,
1011     BaseNone_Init
1012 };
1013 
1014 // This function allocates an empty structure for the file stream
1015 // The stream structure is created as flat block, variable length
1016 // The file name is placed after the end of the stream structure data
AllocateFileStream(const TCHAR * szFileName,size_t StreamSize,DWORD dwStreamFlags)1017 static TFileStream * AllocateFileStream(
1018     const TCHAR * szFileName,
1019     size_t StreamSize,
1020     DWORD dwStreamFlags)
1021 {
1022     TFileStream * pMaster = NULL;
1023     TFileStream * pStream;
1024     const TCHAR * szNextFile = szFileName;
1025     size_t FileNameSize;
1026 
1027     // Sanity check
1028     assert(StreamSize != 0);
1029 
1030     // The caller can specify chain of files in the following form:
1031     // C:\archive.MPQ*http://www.server.com/MPQs/archive-server.MPQ
1032     // In that case, we use the part after "*" as master file name
1033     while(szNextFile[0] != 0 && szNextFile[0] != _T('*'))
1034         szNextFile++;
1035     FileNameSize = (size_t)((szNextFile - szFileName) * sizeof(TCHAR));
1036 
1037     // If we have a next file, we need to open it as master stream
1038     // Note that we don't care if the master stream exists or not,
1039     // If it doesn't, later attempts to read missing file block will fail
1040     if(szNextFile[0] == _T('*'))
1041     {
1042         // Don't allow another master file in the string
1043         if(_tcschr(szNextFile + 1, _T('*')) != NULL)
1044         {
1045             SetLastError(ERROR_INVALID_PARAMETER);
1046             return NULL;
1047         }
1048 
1049         // Open the master file
1050         pMaster = FileStream_OpenFile(szNextFile + 1, STREAM_FLAG_READ_ONLY);
1051     }
1052 
1053     // Allocate the stream structure for the given stream type
1054     pStream = (TFileStream *)STORM_ALLOC(BYTE, StreamSize + FileNameSize + sizeof(TCHAR));
1055     if(pStream != NULL)
1056     {
1057         // Zero the entire structure
1058         memset(pStream, 0, StreamSize);
1059         pStream->pMaster = pMaster;
1060         pStream->dwFlags = dwStreamFlags;
1061 
1062         // Initialize the file name
1063         pStream->szFileName = (TCHAR *)((BYTE *)pStream + StreamSize);
1064         memcpy(pStream->szFileName, szFileName, FileNameSize);
1065         pStream->szFileName[FileNameSize / sizeof(TCHAR)] = 0;
1066 
1067         // Initialize the stream functions
1068         StreamBaseInit[dwStreamFlags & 0x03](pStream);
1069     }
1070 
1071     return pStream;
1072 }
1073 
1074 //-----------------------------------------------------------------------------
1075 // Local functions - flat stream support
1076 
FlatStream_CheckFile(TBlockStream * pStream)1077 static DWORD FlatStream_CheckFile(TBlockStream * pStream)
1078 {
1079     LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap;
1080     DWORD WholeByteCount = (pStream->BlockCount / 8);
1081     DWORD ExtraBitsCount = (pStream->BlockCount & 7);
1082     BYTE ExpectedValue;
1083 
1084     // Verify the whole bytes - their value must be 0xFF
1085     for(DWORD i = 0; i < WholeByteCount; i++)
1086     {
1087         if(FileBitmap[i] != 0xFF)
1088             return 0;
1089     }
1090 
1091     // If there are extra bits, calculate the mask
1092     if(ExtraBitsCount != 0)
1093     {
1094         ExpectedValue = (BYTE)((1 << ExtraBitsCount) - 1);
1095         if(FileBitmap[WholeByteCount] != ExpectedValue)
1096             return 0;
1097     }
1098 
1099     // Yes, the file is complete
1100     return 1;
1101 }
1102 
FlatStream_LoadBitmap(TBlockStream * pStream)1103 static bool FlatStream_LoadBitmap(TBlockStream * pStream)
1104 {
1105     FILE_BITMAP_FOOTER Footer;
1106     ULONGLONG ByteOffset;
1107     LPBYTE FileBitmap;
1108     DWORD BlockCount;
1109     DWORD BitmapSize;
1110 
1111     // Do not load the bitmap if we should not have to
1112     if(!(pStream->dwFlags & STREAM_FLAG_USE_BITMAP))
1113         return false;
1114 
1115     // Only if the size is greater than size of bitmap footer
1116     if(pStream->Base.File.FileSize > sizeof(FILE_BITMAP_FOOTER))
1117     {
1118         // Load the bitmap footer
1119         ByteOffset = pStream->Base.File.FileSize - sizeof(FILE_BITMAP_FOOTER);
1120         if(pStream->BaseRead(pStream, &ByteOffset, &Footer, sizeof(FILE_BITMAP_FOOTER)))
1121         {
1122             // Make sure that the array is properly BSWAP-ed
1123             BSWAP_ARRAY32_UNSIGNED((LPDWORD)(&Footer), sizeof(FILE_BITMAP_FOOTER));
1124 
1125             // Verify if there is actually a footer
1126             if(Footer.Signature == ID_FILE_BITMAP_FOOTER && Footer.Version == 0x03)
1127             {
1128                 // Get the offset of the bitmap, number of blocks and size of the bitmap
1129                 ByteOffset = MAKE_OFFSET64(Footer.MapOffsetHi, Footer.MapOffsetLo);
1130                 BlockCount = (DWORD)(((ByteOffset - 1) / Footer.BlockSize) + 1);
1131                 BitmapSize = ((BlockCount + 7) / 8);
1132 
1133                 // Check if the sizes match
1134                 if(ByteOffset + BitmapSize + sizeof(FILE_BITMAP_FOOTER) == pStream->Base.File.FileSize)
1135                 {
1136                     // Allocate space for the bitmap
1137                     FileBitmap = STORM_ALLOC(BYTE, BitmapSize);
1138                     if(FileBitmap != NULL)
1139                     {
1140                         // Load the bitmap bits
1141                         if(!pStream->BaseRead(pStream, &ByteOffset, FileBitmap, BitmapSize))
1142                         {
1143                             STORM_FREE(FileBitmap);
1144                             return false;
1145                         }
1146 
1147                         // Update the stream size
1148                         pStream->BuildNumber = Footer.BuildNumber;
1149                         pStream->StreamSize = ByteOffset;
1150 
1151                         // Fill the bitmap information
1152                         pStream->FileBitmap = FileBitmap;
1153                         pStream->BitmapSize = BitmapSize;
1154                         pStream->BlockSize  = Footer.BlockSize;
1155                         pStream->BlockCount = BlockCount;
1156                         pStream->IsComplete = FlatStream_CheckFile(pStream);
1157                         return true;
1158                     }
1159                 }
1160             }
1161         }
1162     }
1163 
1164     return false;
1165 }
1166 
FlatStream_UpdateBitmap(TBlockStream * pStream,ULONGLONG StartOffset,ULONGLONG EndOffset)1167 static void FlatStream_UpdateBitmap(
1168     TBlockStream * pStream,                // Pointer to an open stream
1169     ULONGLONG StartOffset,
1170     ULONGLONG EndOffset)
1171 {
1172     LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap;
1173     DWORD BlockIndex;
1174     DWORD BlockSize = pStream->BlockSize;
1175     DWORD ByteIndex;
1176     BYTE BitMask;
1177 
1178     // Sanity checks
1179     assert((StartOffset & (BlockSize - 1)) == 0);
1180     assert(FileBitmap != NULL);
1181 
1182     // Calculate the index of the block
1183     BlockIndex = (DWORD)(StartOffset / BlockSize);
1184     ByteIndex = (BlockIndex / 0x08);
1185     BitMask = (BYTE)(1 << (BlockIndex & 0x07));
1186 
1187     // Set all bits for the specified range
1188     while(StartOffset < EndOffset)
1189     {
1190         // Set the bit
1191         FileBitmap[ByteIndex] |= BitMask;
1192 
1193         // Move all
1194         StartOffset += BlockSize;
1195         ByteIndex += (BitMask >> 0x07);
1196         BitMask = (BitMask >> 0x07) | (BitMask << 0x01);
1197     }
1198 
1199     // Increment the bitmap update count
1200     pStream->IsModified = 1;
1201 }
1202 
FlatStream_BlockCheck(TBlockStream * pStream,ULONGLONG BlockOffset)1203 static bool FlatStream_BlockCheck(
1204     TBlockStream * pStream,                // Pointer to an open stream
1205     ULONGLONG BlockOffset)
1206 {
1207     LPBYTE FileBitmap = (LPBYTE)pStream->FileBitmap;
1208     DWORD BlockIndex;
1209     BYTE BitMask;
1210 
1211     // Sanity checks
1212     assert((BlockOffset & (pStream->BlockSize - 1)) == 0);
1213     assert(FileBitmap != NULL);
1214 
1215     // Calculate the index of the block
1216     BlockIndex = (DWORD)(BlockOffset / pStream->BlockSize);
1217     BitMask = (BYTE)(1 << (BlockIndex & 0x07));
1218 
1219     // Check if the bit is present
1220     return (FileBitmap[BlockIndex / 0x08] & BitMask) ? true : false;
1221 }
1222 
FlatStream_BlockRead(TBlockStream * pStream,ULONGLONG StartOffset,ULONGLONG EndOffset,LPBYTE BlockBuffer,DWORD BytesNeeded,bool bAvailable)1223 static bool FlatStream_BlockRead(
1224     TBlockStream * pStream,                // Pointer to an open stream
1225     ULONGLONG StartOffset,
1226     ULONGLONG EndOffset,
1227     LPBYTE BlockBuffer,
1228     DWORD BytesNeeded,
1229     bool bAvailable)
1230 {
1231     DWORD BytesToRead = (DWORD)(EndOffset - StartOffset);
1232 
1233     // The starting offset must be aligned to size of the block
1234     assert(pStream->FileBitmap != NULL);
1235     assert((StartOffset & (pStream->BlockSize - 1)) == 0);
1236     assert(StartOffset < EndOffset);
1237 
1238     // If the blocks are not available, we need to load them from the master
1239     // and then save to the mirror
1240     if(bAvailable == false)
1241     {
1242         // If we have no master, we cannot satisfy read request
1243         if(pStream->pMaster == NULL)
1244             return false;
1245 
1246         // Load the blocks from the master stream
1247         // Note that we always have to read complete blocks
1248         // so they get properly stored to the mirror stream
1249         if(!FileStream_Read(pStream->pMaster, &StartOffset, BlockBuffer, BytesToRead))
1250             return false;
1251 
1252         // Store the loaded blocks to the mirror file.
1253         // Note that this operation is not required to succeed
1254         if(pStream->BaseWrite(pStream, &StartOffset, BlockBuffer, BytesToRead))
1255             FlatStream_UpdateBitmap(pStream, StartOffset, EndOffset);
1256 
1257         return true;
1258     }
1259     else
1260     {
1261         if(BytesToRead > BytesNeeded)
1262             BytesToRead = BytesNeeded;
1263         return pStream->BaseRead(pStream, &StartOffset, BlockBuffer, BytesToRead);
1264     }
1265 }
1266 
FlatStream_Close(TBlockStream * pStream)1267 static void FlatStream_Close(TBlockStream * pStream)
1268 {
1269     FILE_BITMAP_FOOTER Footer;
1270 
1271     if(pStream->FileBitmap && pStream->IsModified)
1272     {
1273         // Write the file bitmap
1274         pStream->BaseWrite(pStream, &pStream->StreamSize, pStream->FileBitmap, pStream->BitmapSize);
1275 
1276         // Prepare and write the file footer
1277         Footer.Signature   = ID_FILE_BITMAP_FOOTER;
1278         Footer.Version     = 3;
1279         Footer.BuildNumber = pStream->BuildNumber;
1280         Footer.MapOffsetLo = (DWORD)(pStream->StreamSize & 0xFFFFFFFF);
1281         Footer.MapOffsetHi = (DWORD)(pStream->StreamSize >> 0x20);
1282         Footer.BlockSize   = pStream->BlockSize;
1283         BSWAP_ARRAY32_UNSIGNED(&Footer, sizeof(FILE_BITMAP_FOOTER));
1284         pStream->BaseWrite(pStream, NULL, &Footer, sizeof(FILE_BITMAP_FOOTER));
1285     }
1286 
1287     // Close the base class
1288     BlockStream_Close(pStream);
1289 }
1290 
FlatStream_CreateMirror(TBlockStream * pStream)1291 static bool FlatStream_CreateMirror(TBlockStream * pStream)
1292 {
1293     ULONGLONG MasterSize = 0;
1294     ULONGLONG MirrorSize = 0;
1295     LPBYTE FileBitmap = NULL;
1296     DWORD dwBitmapSize;
1297     DWORD dwBlockCount;
1298     bool bNeedCreateMirrorStream = true;
1299     bool bNeedResizeMirrorStream = true;
1300 
1301     // Do we have master function and base creation function?
1302     if(pStream->pMaster == NULL || pStream->BaseCreate == NULL)
1303         return false;
1304 
1305     // Retrieve the master file size, block count and bitmap size
1306     FileStream_GetSize(pStream->pMaster, &MasterSize);
1307     dwBlockCount = (DWORD)((MasterSize + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE);
1308     dwBitmapSize = (DWORD)((dwBlockCount + 7) / 8);
1309 
1310     // Setup stream size and position
1311     pStream->BuildNumber = DEFAULT_BUILD_NUMBER;        // BUGBUG: Really???
1312     pStream->StreamSize = MasterSize;
1313     pStream->StreamPos = 0;
1314 
1315     // Open the base stream for write access
1316     if(pStream->BaseOpen(pStream, pStream->szFileName, 0))
1317     {
1318         // If the file open succeeded, check if the file size matches required size
1319         pStream->BaseGetSize(pStream, &MirrorSize);
1320         if(MirrorSize == MasterSize + dwBitmapSize + sizeof(FILE_BITMAP_FOOTER))
1321         {
1322             // Attempt to load an existing file bitmap
1323             if(FlatStream_LoadBitmap(pStream))
1324                 return true;
1325 
1326             // We need to create new file bitmap
1327             bNeedResizeMirrorStream = false;
1328         }
1329 
1330         // We need to create mirror stream
1331         bNeedCreateMirrorStream = false;
1332     }
1333 
1334     // Create a new stream, if needed
1335     if(bNeedCreateMirrorStream)
1336     {
1337         if(!pStream->BaseCreate(pStream))
1338             return false;
1339     }
1340 
1341     // If we need to, then resize the mirror stream
1342     if(bNeedResizeMirrorStream)
1343     {
1344         if(!pStream->BaseResize(pStream, MasterSize + dwBitmapSize + sizeof(FILE_BITMAP_FOOTER)))
1345             return false;
1346     }
1347 
1348     // Allocate the bitmap array
1349     FileBitmap = STORM_ALLOC(BYTE, dwBitmapSize);
1350     if(FileBitmap == NULL)
1351         return false;
1352 
1353     // Initialize the bitmap
1354     memset(FileBitmap, 0, dwBitmapSize);
1355     pStream->FileBitmap = FileBitmap;
1356     pStream->BitmapSize = dwBitmapSize;
1357     pStream->BlockSize  = DEFAULT_BLOCK_SIZE;
1358     pStream->BlockCount = dwBlockCount;
1359     pStream->IsComplete = 0;
1360     pStream->IsModified = 1;
1361 
1362     // Note: Don't write the stream bitmap right away.
1363     // Doing so would cause sparse file resize on NTFS,
1364     // which would take long time on larger files.
1365     return true;
1366 }
1367 
FlatStream_Open(const TCHAR * szFileName,DWORD dwStreamFlags)1368 static TFileStream * FlatStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)
1369 {
1370     TBlockStream * pStream;
1371     ULONGLONG ByteOffset = 0;
1372 
1373     // Create new empty stream
1374     pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags);
1375     if(pStream == NULL)
1376         return NULL;
1377 
1378     // Do we have a master stream?
1379     if(pStream->pMaster != NULL)
1380     {
1381         if(!FlatStream_CreateMirror(pStream))
1382         {
1383             FileStream_Close(pStream);
1384             SetLastError(ERROR_FILE_NOT_FOUND);
1385             return NULL;
1386         }
1387     }
1388     else
1389     {
1390         // Attempt to open the base stream
1391         if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags))
1392         {
1393             FileStream_Close(pStream);
1394             return NULL;
1395         }
1396 
1397         // Load the bitmap, if required to
1398         if(dwStreamFlags & STREAM_FLAG_USE_BITMAP)
1399             FlatStream_LoadBitmap(pStream);
1400     }
1401 
1402     // If we have a stream bitmap, set the reading functions
1403     // which check presence of each file block
1404     if(pStream->FileBitmap != NULL)
1405     {
1406         // Set the stream position to zero. Stream size is already set
1407         assert(pStream->StreamSize != 0);
1408         pStream->StreamPos = 0;
1409         pStream->dwFlags |= STREAM_FLAG_READ_ONLY;
1410 
1411         // Supply the stream functions
1412         pStream->StreamRead    = (STREAM_READ)BlockStream_Read;
1413         pStream->StreamGetSize = BlockStream_GetSize;
1414         pStream->StreamGetPos  = BlockStream_GetPos;
1415         pStream->StreamClose   = (STREAM_CLOSE)FlatStream_Close;
1416 
1417         // Supply the block functions
1418         pStream->BlockCheck    = (BLOCK_CHECK)FlatStream_BlockCheck;
1419         pStream->BlockRead     = (BLOCK_READ)FlatStream_BlockRead;
1420     }
1421     else
1422     {
1423         // Reset the base position to zero
1424         pStream->BaseRead(pStream, &ByteOffset, NULL, 0);
1425 
1426         // Setup stream size and position
1427         pStream->StreamSize = pStream->Base.File.FileSize;
1428         pStream->StreamPos = 0;
1429 
1430         // Set the base functions
1431         pStream->StreamRead    = pStream->BaseRead;
1432         pStream->StreamWrite   = pStream->BaseWrite;
1433         pStream->StreamResize  = pStream->BaseResize;
1434         pStream->StreamGetSize = pStream->BaseGetSize;
1435         pStream->StreamGetPos  = pStream->BaseGetPos;
1436         pStream->StreamClose   = pStream->BaseClose;
1437     }
1438 
1439     return pStream;
1440 }
1441 
1442 //-----------------------------------------------------------------------------
1443 // Local functions - partial stream support
1444 
IsPartHeader(PPART_FILE_HEADER pPartHdr)1445 static bool IsPartHeader(PPART_FILE_HEADER pPartHdr)
1446 {
1447     // Version number must be 2
1448     if(pPartHdr->PartialVersion == 2)
1449     {
1450         // GameBuildNumber must be an ASCII number
1451         if(isdigit(pPartHdr->GameBuildNumber[0]) && isdigit(pPartHdr->GameBuildNumber[1]) && isdigit(pPartHdr->GameBuildNumber[2]))
1452         {
1453             // Block size must be power of 2
1454             if((pPartHdr->BlockSize & (pPartHdr->BlockSize - 1)) == 0)
1455                 return true;
1456         }
1457     }
1458 
1459     return false;
1460 }
1461 
PartStream_CheckFile(TBlockStream * pStream)1462 static DWORD PartStream_CheckFile(TBlockStream * pStream)
1463 {
1464     PPART_FILE_MAP_ENTRY FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap;
1465     DWORD dwBlockCount;
1466 
1467     // Get the number of blocks
1468     dwBlockCount = (DWORD)((pStream->StreamSize + pStream->BlockSize - 1) / pStream->BlockSize);
1469 
1470     // Check all blocks
1471     for(DWORD i = 0; i < dwBlockCount; i++, FileBitmap++)
1472     {
1473         // Few sanity checks
1474         assert(FileBitmap->LargeValueHi == 0);
1475         assert(FileBitmap->LargeValueLo == 0);
1476         assert(FileBitmap->Flags == 0 || FileBitmap->Flags == 3);
1477 
1478         // Check if this block is present
1479         if(FileBitmap->Flags != 3)
1480             return 0;
1481     }
1482 
1483     // Yes, the file is complete
1484     return 1;
1485 }
1486 
PartStream_LoadBitmap(TBlockStream * pStream)1487 static bool PartStream_LoadBitmap(TBlockStream * pStream)
1488 {
1489     PPART_FILE_MAP_ENTRY FileBitmap;
1490     PART_FILE_HEADER PartHdr;
1491     ULONGLONG ByteOffset = 0;
1492     ULONGLONG StreamSize = 0;
1493     DWORD BlockCount;
1494     DWORD BitmapSize;
1495 
1496     // Only if the size is greater than size of the bitmap header
1497     if(pStream->Base.File.FileSize > sizeof(PART_FILE_HEADER))
1498     {
1499         // Attempt to read PART file header
1500         if(pStream->BaseRead(pStream, &ByteOffset, &PartHdr, sizeof(PART_FILE_HEADER)))
1501         {
1502             // We need to swap PART file header on big-endian platforms
1503             BSWAP_ARRAY32_UNSIGNED(&PartHdr, sizeof(PART_FILE_HEADER));
1504 
1505             // Verify the PART file header
1506             if(IsPartHeader(&PartHdr))
1507             {
1508                 // Get the number of blocks and size of one block
1509                 StreamSize = MAKE_OFFSET64(PartHdr.FileSizeHi, PartHdr.FileSizeLo);
1510                 ByteOffset = sizeof(PART_FILE_HEADER);
1511                 BlockCount = (DWORD)((StreamSize + PartHdr.BlockSize - 1) / PartHdr.BlockSize);
1512                 BitmapSize = BlockCount * sizeof(PART_FILE_MAP_ENTRY);
1513 
1514                 // Check if sizes match
1515                 if((ByteOffset + BitmapSize) < pStream->Base.File.FileSize)
1516                 {
1517                     // Allocate space for the array of PART_FILE_MAP_ENTRY
1518                     FileBitmap = STORM_ALLOC(PART_FILE_MAP_ENTRY, BlockCount);
1519                     if(FileBitmap != NULL)
1520                     {
1521                         // Load the block map
1522                         if(!pStream->BaseRead(pStream, &ByteOffset, FileBitmap, BitmapSize))
1523                         {
1524                             STORM_FREE(FileBitmap);
1525                             return false;
1526                         }
1527 
1528                         // Make sure that the byte order is correct
1529                         BSWAP_ARRAY32_UNSIGNED(FileBitmap, BitmapSize);
1530 
1531                         // Update the stream size
1532                         pStream->BuildNumber = StringToInt(PartHdr.GameBuildNumber);
1533                         pStream->StreamSize = StreamSize;
1534 
1535                         // Fill the bitmap information
1536                         pStream->FileBitmap = FileBitmap;
1537                         pStream->BitmapSize = BitmapSize;
1538                         pStream->BlockSize  = PartHdr.BlockSize;
1539                         pStream->BlockCount = BlockCount;
1540                         pStream->IsComplete = PartStream_CheckFile(pStream);
1541                         return true;
1542                     }
1543                 }
1544             }
1545         }
1546     }
1547 
1548     return false;
1549 }
1550 
PartStream_UpdateBitmap(TBlockStream * pStream,ULONGLONG StartOffset,ULONGLONG EndOffset,ULONGLONG RealOffset)1551 static void PartStream_UpdateBitmap(
1552     TBlockStream * pStream,                // Pointer to an open stream
1553     ULONGLONG StartOffset,
1554     ULONGLONG EndOffset,
1555     ULONGLONG RealOffset)
1556 {
1557     PPART_FILE_MAP_ENTRY FileBitmap;
1558     DWORD BlockSize = pStream->BlockSize;
1559 
1560     // Sanity checks
1561     assert((StartOffset & (BlockSize - 1)) == 0);
1562     assert(pStream->FileBitmap != NULL);
1563 
1564     // Calculate the first entry in the block map
1565     FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + (StartOffset / BlockSize);
1566 
1567     // Set all bits for the specified range
1568     while(StartOffset < EndOffset)
1569     {
1570         // Set the bit
1571         FileBitmap->BlockOffsHi = (DWORD)(RealOffset >> 0x20);
1572         FileBitmap->BlockOffsLo = (DWORD)(RealOffset & 0xFFFFFFFF);
1573         FileBitmap->Flags = 3;
1574 
1575         // Move all
1576         StartOffset += BlockSize;
1577         RealOffset += BlockSize;
1578         FileBitmap++;
1579     }
1580 
1581     // Increment the bitmap update count
1582     pStream->IsModified = 1;
1583 }
1584 
PartStream_BlockCheck(TBlockStream * pStream,ULONGLONG BlockOffset)1585 static bool PartStream_BlockCheck(
1586     TBlockStream * pStream,                // Pointer to an open stream
1587     ULONGLONG BlockOffset)
1588 {
1589     PPART_FILE_MAP_ENTRY FileBitmap;
1590 
1591     // Sanity checks
1592     assert((BlockOffset & (pStream->BlockSize - 1)) == 0);
1593     assert(pStream->FileBitmap != NULL);
1594 
1595     // Calculate the block map entry
1596     FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + (BlockOffset / pStream->BlockSize);
1597 
1598     // Check if the flags are present
1599     return (FileBitmap->Flags & 0x03) ? true : false;
1600 }
1601 
PartStream_BlockRead(TBlockStream * pStream,ULONGLONG StartOffset,ULONGLONG EndOffset,LPBYTE BlockBuffer,DWORD BytesNeeded,bool bAvailable)1602 static bool PartStream_BlockRead(
1603     TBlockStream * pStream,
1604     ULONGLONG StartOffset,
1605     ULONGLONG EndOffset,
1606     LPBYTE BlockBuffer,
1607     DWORD BytesNeeded,
1608     bool bAvailable)
1609 {
1610     PPART_FILE_MAP_ENTRY FileBitmap;
1611     ULONGLONG ByteOffset;
1612     DWORD BytesToRead;
1613     DWORD BlockIndex = (DWORD)(StartOffset / pStream->BlockSize);
1614 
1615     // The starting offset must be aligned to size of the block
1616     assert(pStream->FileBitmap != NULL);
1617     assert((StartOffset & (pStream->BlockSize - 1)) == 0);
1618     assert(StartOffset < EndOffset);
1619 
1620     // If the blocks are not available, we need to load them from the master
1621     // and then save to the mirror
1622     if(bAvailable == false)
1623     {
1624         // If we have no master, we cannot satisfy read request
1625         if(pStream->pMaster == NULL)
1626             return false;
1627 
1628         // Load the blocks from the master stream
1629         // Note that we always have to read complete blocks
1630         // so they get properly stored to the mirror stream
1631         BytesToRead = (DWORD)(EndOffset - StartOffset);
1632         if(!FileStream_Read(pStream->pMaster, &StartOffset, BlockBuffer, BytesToRead))
1633             return false;
1634 
1635         // The loaded blocks are going to be stored to the end of the file
1636         // Note that this operation is not required to succeed
1637         if(pStream->BaseGetSize(pStream, &ByteOffset))
1638         {
1639             // Store the loaded blocks to the mirror file.
1640             if(pStream->BaseWrite(pStream, &ByteOffset, BlockBuffer, BytesToRead))
1641             {
1642                 PartStream_UpdateBitmap(pStream, StartOffset, EndOffset, ByteOffset);
1643             }
1644         }
1645     }
1646     else
1647     {
1648         // Get the file map entry
1649         FileBitmap = (PPART_FILE_MAP_ENTRY)pStream->FileBitmap + BlockIndex;
1650 
1651         // Read all blocks
1652         while(StartOffset < EndOffset)
1653         {
1654             // Get the number of bytes to be read
1655             BytesToRead = (DWORD)(EndOffset - StartOffset);
1656             if(BytesToRead > pStream->BlockSize)
1657                 BytesToRead = pStream->BlockSize;
1658             if(BytesToRead > BytesNeeded)
1659                 BytesToRead = BytesNeeded;
1660 
1661             // Read the block
1662             ByteOffset = MAKE_OFFSET64(FileBitmap->BlockOffsHi, FileBitmap->BlockOffsLo);
1663             if(!pStream->BaseRead(pStream, &ByteOffset, BlockBuffer, BytesToRead))
1664                 return false;
1665 
1666             // Move the pointers
1667             StartOffset += pStream->BlockSize;
1668             BlockBuffer += pStream->BlockSize;
1669             BytesNeeded -= pStream->BlockSize;
1670             FileBitmap++;
1671         }
1672     }
1673 
1674     return true;
1675 }
1676 
PartStream_Close(TBlockStream * pStream)1677 static void PartStream_Close(TBlockStream * pStream)
1678 {
1679     PART_FILE_HEADER PartHeader;
1680     ULONGLONG ByteOffset = 0;
1681 
1682     if(pStream->FileBitmap && pStream->IsModified)
1683     {
1684         // Prepare the part file header
1685         memset(&PartHeader, 0, sizeof(PART_FILE_HEADER));
1686         PartHeader.PartialVersion = 2;
1687         PartHeader.FileSizeHi     = (DWORD)(pStream->StreamSize >> 0x20);
1688         PartHeader.FileSizeLo     = (DWORD)(pStream->StreamSize & 0xFFFFFFFF);
1689         PartHeader.BlockSize      = pStream->BlockSize;
1690 
1691         // Make sure that the header is properly BSWAPed
1692         BSWAP_ARRAY32_UNSIGNED(&PartHeader, sizeof(PART_FILE_HEADER));
1693         sprintf(PartHeader.GameBuildNumber, "%u", (unsigned int)pStream->BuildNumber);
1694 
1695         // Write the part header
1696         pStream->BaseWrite(pStream, &ByteOffset, &PartHeader, sizeof(PART_FILE_HEADER));
1697 
1698         // Write the block bitmap
1699         BSWAP_ARRAY32_UNSIGNED(pStream->FileBitmap, pStream->BitmapSize);
1700         pStream->BaseWrite(pStream, NULL, pStream->FileBitmap, pStream->BitmapSize);
1701     }
1702 
1703     // Close the base class
1704     BlockStream_Close(pStream);
1705 }
1706 
PartStream_CreateMirror(TBlockStream * pStream)1707 static bool PartStream_CreateMirror(TBlockStream * pStream)
1708 {
1709     ULONGLONG RemainingSize;
1710     ULONGLONG MasterSize = 0;
1711     ULONGLONG MirrorSize = 0;
1712     LPBYTE FileBitmap = NULL;
1713     DWORD dwBitmapSize;
1714     DWORD dwBlockCount;
1715     bool bNeedCreateMirrorStream = true;
1716     bool bNeedResizeMirrorStream = true;
1717 
1718     // Do we have master function and base creation function?
1719     if(pStream->pMaster == NULL || pStream->BaseCreate == NULL)
1720         return false;
1721 
1722     // Retrieve the master file size, block count and bitmap size
1723     FileStream_GetSize(pStream->pMaster, &MasterSize);
1724     dwBlockCount = (DWORD)((MasterSize + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE);
1725     dwBitmapSize = (DWORD)(dwBlockCount * sizeof(PART_FILE_MAP_ENTRY));
1726 
1727     // Setup stream size and position
1728     pStream->BuildNumber = DEFAULT_BUILD_NUMBER;        // BUGBUG: Really???
1729     pStream->StreamSize = MasterSize;
1730     pStream->StreamPos = 0;
1731 
1732     // Open the base stream for write access
1733     if(pStream->BaseOpen(pStream, pStream->szFileName, 0))
1734     {
1735         // If the file open succeeded, check if the file size matches required size
1736         pStream->BaseGetSize(pStream, &MirrorSize);
1737         if(MirrorSize >= sizeof(PART_FILE_HEADER) + dwBitmapSize)
1738         {
1739             // Check if the remaining size is aligned to block
1740             RemainingSize = MirrorSize - sizeof(PART_FILE_HEADER) - dwBitmapSize;
1741             if((RemainingSize & (DEFAULT_BLOCK_SIZE - 1)) == 0 || RemainingSize == MasterSize)
1742             {
1743                 // Attempt to load an existing file bitmap
1744                 if(PartStream_LoadBitmap(pStream))
1745                     return true;
1746             }
1747         }
1748 
1749         // We need to create mirror stream
1750         bNeedCreateMirrorStream = false;
1751     }
1752 
1753     // Create a new stream, if needed
1754     if(bNeedCreateMirrorStream)
1755     {
1756         if(!pStream->BaseCreate(pStream))
1757             return false;
1758     }
1759 
1760     // If we need to, then resize the mirror stream
1761     if(bNeedResizeMirrorStream)
1762     {
1763         if(!pStream->BaseResize(pStream, sizeof(PART_FILE_HEADER) + dwBitmapSize))
1764             return false;
1765     }
1766 
1767     // Allocate the bitmap array
1768     FileBitmap = STORM_ALLOC(BYTE, dwBitmapSize);
1769     if(FileBitmap == NULL)
1770         return false;
1771 
1772     // Initialize the bitmap
1773     memset(FileBitmap, 0, dwBitmapSize);
1774     pStream->FileBitmap = FileBitmap;
1775     pStream->BitmapSize = dwBitmapSize;
1776     pStream->BlockSize  = DEFAULT_BLOCK_SIZE;
1777     pStream->BlockCount = dwBlockCount;
1778     pStream->IsComplete = 0;
1779     pStream->IsModified = 1;
1780 
1781     // Note: Don't write the stream bitmap right away.
1782     // Doing so would cause sparse file resize on NTFS,
1783     // which would take long time on larger files.
1784     return true;
1785 }
1786 
1787 
PartStream_Open(const TCHAR * szFileName,DWORD dwStreamFlags)1788 static TFileStream * PartStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)
1789 {
1790     TBlockStream * pStream;
1791 
1792     // Create new empty stream
1793     pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags);
1794     if(pStream == NULL)
1795         return NULL;
1796 
1797     // Do we have a master stream?
1798     if(pStream->pMaster != NULL)
1799     {
1800         if(!PartStream_CreateMirror(pStream))
1801         {
1802             FileStream_Close(pStream);
1803             SetLastError(ERROR_FILE_NOT_FOUND);
1804             return NULL;
1805         }
1806     }
1807     else
1808     {
1809         // Attempt to open the base stream
1810         if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags))
1811         {
1812             FileStream_Close(pStream);
1813             return NULL;
1814         }
1815 
1816         // Load the part stream block map
1817         if(!PartStream_LoadBitmap(pStream))
1818         {
1819             FileStream_Close(pStream);
1820             SetLastError(ERROR_BAD_FORMAT);
1821             return NULL;
1822         }
1823     }
1824 
1825     // Set the stream position to zero. Stream size is already set
1826     assert(pStream->StreamSize != 0);
1827     pStream->StreamPos = 0;
1828     pStream->dwFlags |= STREAM_FLAG_READ_ONLY;
1829 
1830     // Set new function pointers
1831     pStream->StreamRead    = (STREAM_READ)BlockStream_Read;
1832     pStream->StreamGetPos  = BlockStream_GetPos;
1833     pStream->StreamGetSize = BlockStream_GetSize;
1834     pStream->StreamClose   = (STREAM_CLOSE)PartStream_Close;
1835 
1836     // Supply the block functions
1837     pStream->BlockCheck    = (BLOCK_CHECK)PartStream_BlockCheck;
1838     pStream->BlockRead     = (BLOCK_READ)PartStream_BlockRead;
1839     return pStream;
1840 }
1841 
1842 //-----------------------------------------------------------------------------
1843 // Local functions - MPQE stream support
1844 
1845 static const char * szKeyTemplate = "expand 32-byte k000000000000000000000000000000000000000000000000";
1846 
1847 static const char * AuthCodeArray[] =
1848 {
1849     // Starcraft II (Heart of the Swarm)
1850     // Authentication code URL: http://dist.blizzard.com/mediakey/hots-authenticationcode-bgdl.txt
1851     //                                                                                          -0C-    -1C--08-    -18--04-    -14--00-    -10-
1852     "S48B6CDTN5XEQAKQDJNDLJBJ73FDFM3U",         // SC2 Heart of the Swarm-all : "expand 32-byte kQAKQ0000FM3UN5XE000073FD6CDT0000LJBJS48B0000DJND"
1853 
1854     // Diablo III: Agent.exe (1.0.0.954)
1855     // Address of decryption routine: 00502b00
1856     // Pointer to decryptor object: ECX
1857     // Pointer to key: ECX+0x5C
1858     // Authentication code URL: http://dist.blizzard.com/mediakey/d3-authenticationcode-enGB.txt
1859     //                                                                                           -0C-    -1C--08-    -18--04-    -14--00-    -10-
1860     "UCMXF6EJY352EFH4XFRXCFH2XC9MQRZK",         // Diablo III Installer (deDE): "expand 32-byte kEFH40000QRZKY3520000XC9MF6EJ0000CFH2UCMX0000XFRX"
1861     "MMKVHY48RP7WXP4GHYBQ7SL9J9UNPHBP",         // Diablo III Installer (enGB): "expand 32-byte kXP4G0000PHBPRP7W0000J9UNHY4800007SL9MMKV0000HYBQ"
1862     "8MXLWHQ7VGGLTZ9MQZQSFDCLJYET3CPP",         // Diablo III Installer (enSG): "expand 32-byte kTZ9M00003CPPVGGL0000JYETWHQ70000FDCL8MXL0000QZQS"
1863     "EJ2R5TM6XFE2GUNG5QDGHKQ9UAKPWZSZ",         // Diablo III Installer (enUS): "expand 32-byte kGUNG0000WZSZXFE20000UAKP5TM60000HKQ9EJ2R00005QDG"
1864     "PBGFBE42Z6LNK65UGJQ3WZVMCLP4HQQT",         // Diablo III Installer (esES): "expand 32-byte kK65U0000HQQTZ6LN0000CLP4BE420000WZVMPBGF0000GJQ3"
1865     "X7SEJJS9TSGCW5P28EBSC47AJPEY8VU2",         // Diablo III Installer (esMX): "expand 32-byte kW5P200008VU2TSGC0000JPEYJJS90000C47AX7SE00008EBS"
1866     "5KVBQA8VYE6XRY3DLGC5ZDE4XS4P7YA2",         // Diablo III Installer (frFR): "expand 32-byte kRY3D00007YA2YE6X0000XS4PQA8V0000ZDE45KVB0000LGC5"
1867     "478JD2K56EVNVVY4XX8TDWYT5B8KB254",         // Diablo III Installer (itIT): "expand 32-byte kVVY40000B2546EVN00005B8KD2K50000DWYT478J0000XX8T"
1868     "8TS4VNFQRZTN6YWHE9CHVDH9NVWD474A",         // Diablo III Installer (koKR): "expand 32-byte k6YWH0000474ARZTN0000NVWDVNFQ0000VDH98TS40000E9CH"
1869     "LJ52Z32DF4LZ4ZJJXVKK3AZQA6GABLJB",         // Diablo III Installer (plPL): "expand 32-byte k4ZJJ0000BLJBF4LZ0000A6GAZ32D00003AZQLJ520000XVKK"
1870     "K6BDHY2ECUE2545YKNLBJPVYWHE7XYAG",         // Diablo III Installer (ptBR): "expand 32-byte k545Y0000XYAGCUE20000WHE7HY2E0000JPVYK6BD0000KNLB"
1871     "NDVW8GWLAYCRPGRNY8RT7ZZUQU63VLPR",         // Diablo III Installer (ruRU): "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
1872     "6VWCQTN8V3ZZMRUCZXV8A8CGUX2TAA8H",         // Diablo III Installer (zhTW): "expand 32-byte kMRUC0000AA8HV3ZZ0000UX2TQTN80000A8CG6VWC0000ZXV8"
1873 //  "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",         // Diablo III Installer (zhCN): "expand 32-byte kXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
1874 
1875     // Starcraft II (Wings of Liberty): Installer.exe (4.1.1.4219)
1876     // Address of decryption routine: 0053A3D0
1877     // Pointer to decryptor object: ECX
1878     // Pointer to key: ECX+0x5C
1879     // Authentication code URL: http://dist.blizzard.com/mediakey/sc2-authenticationcode-enUS.txt
1880     //                                                                                          -0C-    -1C--08-    -18--04-    -14--00-    -10-
1881     "Y45MD3CAK4KXSSXHYD9VY64Z8EKJ4XFX",         // SC2 Wings of Liberty (deDE): "expand 32-byte kSSXH00004XFXK4KX00008EKJD3CA0000Y64ZY45M0000YD9V"
1882     "G8MN8UDG6NA2ANGY6A3DNY82HRGF29ZH",         // SC2 Wings of Liberty (enGB): "expand 32-byte kANGY000029ZH6NA20000HRGF8UDG0000NY82G8MN00006A3D"
1883     "W9RRHLB2FDU9WW5B3ECEBLRSFWZSF7HW",         // SC2 Wings of Liberty (enSG): "expand 32-byte kWW5B0000F7HWFDU90000FWZSHLB20000BLRSW9RR00003ECE"
1884     "3DH5RE5NVM5GTFD85LXGWT6FK859ETR5",         // SC2 Wings of Liberty (enUS): "expand 32-byte kTFD80000ETR5VM5G0000K859RE5N0000WT6F3DH500005LXG"
1885     "8WLKUAXE94PFQU4Y249PAZ24N4R4XKTQ",         // SC2 Wings of Liberty (esES): "expand 32-byte kQU4Y0000XKTQ94PF0000N4R4UAXE0000AZ248WLK0000249P"
1886     "A34DXX3VHGGXSQBRFE5UFFDXMF9G4G54",         // SC2 Wings of Liberty (esMX): "expand 32-byte kSQBR00004G54HGGX0000MF9GXX3V0000FFDXA34D0000FE5U"
1887     "ZG7J9K938HJEFWPQUA768MA2PFER6EAJ",         // SC2 Wings of Liberty (frFR): "expand 32-byte kFWPQ00006EAJ8HJE0000PFER9K9300008MA2ZG7J0000UA76"
1888     "NE7CUNNNTVAPXV7E3G2BSVBWGVMW8BL2",         // SC2 Wings of Liberty (itIT): "expand 32-byte kXV7E00008BL2TVAP0000GVMWUNNN0000SVBWNE7C00003G2B"
1889     "3V9E2FTMBM9QQWK7U6MAMWAZWQDB838F",         // SC2 Wings of Liberty (koKR): "expand 32-byte kQWK70000838FBM9Q0000WQDB2FTM0000MWAZ3V9E0000U6MA"
1890     "2NSFB8MELULJ83U6YHA3UP6K4MQD48L6",         // SC2 Wings of Liberty (plPL): "expand 32-byte k83U6000048L6LULJ00004MQDB8ME0000UP6K2NSF0000YHA3"
1891     "QA2TZ9EWZ4CUU8BMB5WXCTY65F9CSW4E",         // SC2 Wings of Liberty (ptBR): "expand 32-byte kU8BM0000SW4EZ4CU00005F9CZ9EW0000CTY6QA2T0000B5WX"
1892     "VHB378W64BAT9SH7D68VV9NLQDK9YEGT",         // SC2 Wings of Liberty (ruRU): "expand 32-byte k9SH70000YEGT4BAT0000QDK978W60000V9NLVHB30000D68V"
1893     "U3NFQJV4M6GC7KBN9XQJ3BRDN3PLD9NE",         // SC2 Wings of Liberty (zhTW): "expand 32-byte k7KBN0000D9NEM6GC0000N3PLQJV400003BRDU3NF00009XQJ"
1894 
1895     NULL
1896 };
1897 
Rol32(DWORD dwValue,DWORD dwRolCount)1898 static DWORD Rol32(DWORD dwValue, DWORD dwRolCount)
1899 {
1900     DWORD dwShiftRight = 32 - dwRolCount;
1901 
1902     return (dwValue << dwRolCount) | (dwValue >> dwShiftRight);
1903 }
1904 
CreateKeyFromAuthCode(LPBYTE pbKeyBuffer,const char * szAuthCode)1905 static void CreateKeyFromAuthCode(
1906     LPBYTE pbKeyBuffer,
1907     const char * szAuthCode)
1908 {
1909     LPDWORD KeyPosition = (LPDWORD)(pbKeyBuffer + 0x10);
1910     LPDWORD AuthCode32 = (LPDWORD)szAuthCode;
1911 
1912     memcpy(pbKeyBuffer, szKeyTemplate, MPQE_CHUNK_SIZE);
1913     KeyPosition[0x00] = AuthCode32[0x03];
1914     KeyPosition[0x02] = AuthCode32[0x07];
1915     KeyPosition[0x03] = AuthCode32[0x02];
1916     KeyPosition[0x05] = AuthCode32[0x06];
1917     KeyPosition[0x06] = AuthCode32[0x01];
1918     KeyPosition[0x08] = AuthCode32[0x05];
1919     KeyPosition[0x09] = AuthCode32[0x00];
1920     KeyPosition[0x0B] = AuthCode32[0x04];
1921     BSWAP_ARRAY32_UNSIGNED(pbKeyBuffer, MPQE_CHUNK_SIZE);
1922 }
1923 
DecryptFileChunk(DWORD * MpqData,LPBYTE pbKey,ULONGLONG ByteOffset,DWORD dwLength)1924 static void DecryptFileChunk(
1925     DWORD * MpqData,
1926     LPBYTE pbKey,
1927     ULONGLONG ByteOffset,
1928     DWORD dwLength)
1929 {
1930     ULONGLONG ChunkOffset;
1931     DWORD KeyShuffled[0x10];
1932     DWORD KeyMirror[0x10];
1933     DWORD RoundCount = 0x14;
1934 
1935     // Prepare the key
1936     ChunkOffset = ByteOffset / MPQE_CHUNK_SIZE;
1937     memcpy(KeyMirror, pbKey, MPQE_CHUNK_SIZE);
1938     BSWAP_ARRAY32_UNSIGNED(KeyMirror, MPQE_CHUNK_SIZE);
1939     KeyMirror[0x05] = (DWORD)(ChunkOffset >> 32);
1940     KeyMirror[0x08] = (DWORD)(ChunkOffset);
1941 
1942     while(dwLength >= MPQE_CHUNK_SIZE)
1943     {
1944         // Shuffle the key - part 1
1945         KeyShuffled[0x0E] = KeyMirror[0x00];
1946         KeyShuffled[0x0C] = KeyMirror[0x01];
1947         KeyShuffled[0x05] = KeyMirror[0x02];
1948         KeyShuffled[0x0F] = KeyMirror[0x03];
1949         KeyShuffled[0x0A] = KeyMirror[0x04];
1950         KeyShuffled[0x07] = KeyMirror[0x05];
1951         KeyShuffled[0x0B] = KeyMirror[0x06];
1952         KeyShuffled[0x09] = KeyMirror[0x07];
1953         KeyShuffled[0x03] = KeyMirror[0x08];
1954         KeyShuffled[0x06] = KeyMirror[0x09];
1955         KeyShuffled[0x08] = KeyMirror[0x0A];
1956         KeyShuffled[0x0D] = KeyMirror[0x0B];
1957         KeyShuffled[0x02] = KeyMirror[0x0C];
1958         KeyShuffled[0x04] = KeyMirror[0x0D];
1959         KeyShuffled[0x01] = KeyMirror[0x0E];
1960         KeyShuffled[0x00] = KeyMirror[0x0F];
1961 
1962         // Shuffle the key - part 2
1963         for(DWORD i = 0; i < RoundCount; i += 2)
1964         {
1965             KeyShuffled[0x0A] = KeyShuffled[0x0A] ^ Rol32((KeyShuffled[0x0E] + KeyShuffled[0x02]), 0x07);
1966             KeyShuffled[0x03] = KeyShuffled[0x03] ^ Rol32((KeyShuffled[0x0A] + KeyShuffled[0x0E]), 0x09);
1967             KeyShuffled[0x02] = KeyShuffled[0x02] ^ Rol32((KeyShuffled[0x03] + KeyShuffled[0x0A]), 0x0D);
1968             KeyShuffled[0x0E] = KeyShuffled[0x0E] ^ Rol32((KeyShuffled[0x02] + KeyShuffled[0x03]), 0x12);
1969 
1970             KeyShuffled[0x07] = KeyShuffled[0x07] ^ Rol32((KeyShuffled[0x0C] + KeyShuffled[0x04]), 0x07);
1971             KeyShuffled[0x06] = KeyShuffled[0x06] ^ Rol32((KeyShuffled[0x07] + KeyShuffled[0x0C]), 0x09);
1972             KeyShuffled[0x04] = KeyShuffled[0x04] ^ Rol32((KeyShuffled[0x06] + KeyShuffled[0x07]), 0x0D);
1973             KeyShuffled[0x0C] = KeyShuffled[0x0C] ^ Rol32((KeyShuffled[0x04] + KeyShuffled[0x06]), 0x12);
1974 
1975             KeyShuffled[0x0B] = KeyShuffled[0x0B] ^ Rol32((KeyShuffled[0x05] + KeyShuffled[0x01]), 0x07);
1976             KeyShuffled[0x08] = KeyShuffled[0x08] ^ Rol32((KeyShuffled[0x0B] + KeyShuffled[0x05]), 0x09);
1977             KeyShuffled[0x01] = KeyShuffled[0x01] ^ Rol32((KeyShuffled[0x08] + KeyShuffled[0x0B]), 0x0D);
1978             KeyShuffled[0x05] = KeyShuffled[0x05] ^ Rol32((KeyShuffled[0x01] + KeyShuffled[0x08]), 0x12);
1979 
1980             KeyShuffled[0x09] = KeyShuffled[0x09] ^ Rol32((KeyShuffled[0x0F] + KeyShuffled[0x00]), 0x07);
1981             KeyShuffled[0x0D] = KeyShuffled[0x0D] ^ Rol32((KeyShuffled[0x09] + KeyShuffled[0x0F]), 0x09);
1982             KeyShuffled[0x00] = KeyShuffled[0x00] ^ Rol32((KeyShuffled[0x0D] + KeyShuffled[0x09]), 0x0D);
1983             KeyShuffled[0x0F] = KeyShuffled[0x0F] ^ Rol32((KeyShuffled[0x00] + KeyShuffled[0x0D]), 0x12);
1984 
1985             KeyShuffled[0x04] = KeyShuffled[0x04] ^ Rol32((KeyShuffled[0x0E] + KeyShuffled[0x09]), 0x07);
1986             KeyShuffled[0x08] = KeyShuffled[0x08] ^ Rol32((KeyShuffled[0x04] + KeyShuffled[0x0E]), 0x09);
1987             KeyShuffled[0x09] = KeyShuffled[0x09] ^ Rol32((KeyShuffled[0x08] + KeyShuffled[0x04]), 0x0D);
1988             KeyShuffled[0x0E] = KeyShuffled[0x0E] ^ Rol32((KeyShuffled[0x09] + KeyShuffled[0x08]), 0x12);
1989 
1990             KeyShuffled[0x01] = KeyShuffled[0x01] ^ Rol32((KeyShuffled[0x0C] + KeyShuffled[0x0A]), 0x07);
1991             KeyShuffled[0x0D] = KeyShuffled[0x0D] ^ Rol32((KeyShuffled[0x01] + KeyShuffled[0x0C]), 0x09);
1992             KeyShuffled[0x0A] = KeyShuffled[0x0A] ^ Rol32((KeyShuffled[0x0D] + KeyShuffled[0x01]), 0x0D);
1993             KeyShuffled[0x0C] = KeyShuffled[0x0C] ^ Rol32((KeyShuffled[0x0A] + KeyShuffled[0x0D]), 0x12);
1994 
1995             KeyShuffled[0x00] = KeyShuffled[0x00] ^ Rol32((KeyShuffled[0x05] + KeyShuffled[0x07]), 0x07);
1996             KeyShuffled[0x03] = KeyShuffled[0x03] ^ Rol32((KeyShuffled[0x00] + KeyShuffled[0x05]), 0x09);
1997             KeyShuffled[0x07] = KeyShuffled[0x07] ^ Rol32((KeyShuffled[0x03] + KeyShuffled[0x00]), 0x0D);
1998             KeyShuffled[0x05] = KeyShuffled[0x05] ^ Rol32((KeyShuffled[0x07] + KeyShuffled[0x03]), 0x12);
1999 
2000             KeyShuffled[0x02] = KeyShuffled[0x02] ^ Rol32((KeyShuffled[0x0F] + KeyShuffled[0x0B]), 0x07);
2001             KeyShuffled[0x06] = KeyShuffled[0x06] ^ Rol32((KeyShuffled[0x02] + KeyShuffled[0x0F]), 0x09);
2002             KeyShuffled[0x0B] = KeyShuffled[0x0B] ^ Rol32((KeyShuffled[0x06] + KeyShuffled[0x02]), 0x0D);
2003             KeyShuffled[0x0F] = KeyShuffled[0x0F] ^ Rol32((KeyShuffled[0x0B] + KeyShuffled[0x06]), 0x12);
2004         }
2005 
2006         // Decrypt one data chunk
2007         BSWAP_ARRAY32_UNSIGNED(MpqData, MPQE_CHUNK_SIZE);
2008         MpqData[0x00] = MpqData[0x00] ^ (KeyShuffled[0x0E] + KeyMirror[0x00]);
2009         MpqData[0x01] = MpqData[0x01] ^ (KeyShuffled[0x04] + KeyMirror[0x0D]);
2010         MpqData[0x02] = MpqData[0x02] ^ (KeyShuffled[0x08] + KeyMirror[0x0A]);
2011         MpqData[0x03] = MpqData[0x03] ^ (KeyShuffled[0x09] + KeyMirror[0x07]);
2012         MpqData[0x04] = MpqData[0x04] ^ (KeyShuffled[0x0A] + KeyMirror[0x04]);
2013         MpqData[0x05] = MpqData[0x05] ^ (KeyShuffled[0x0C] + KeyMirror[0x01]);
2014         MpqData[0x06] = MpqData[0x06] ^ (KeyShuffled[0x01] + KeyMirror[0x0E]);
2015         MpqData[0x07] = MpqData[0x07] ^ (KeyShuffled[0x0D] + KeyMirror[0x0B]);
2016         MpqData[0x08] = MpqData[0x08] ^ (KeyShuffled[0x03] + KeyMirror[0x08]);
2017         MpqData[0x09] = MpqData[0x09] ^ (KeyShuffled[0x07] + KeyMirror[0x05]);
2018         MpqData[0x0A] = MpqData[0x0A] ^ (KeyShuffled[0x05] + KeyMirror[0x02]);
2019         MpqData[0x0B] = MpqData[0x0B] ^ (KeyShuffled[0x00] + KeyMirror[0x0F]);
2020         MpqData[0x0C] = MpqData[0x0C] ^ (KeyShuffled[0x02] + KeyMirror[0x0C]);
2021         MpqData[0x0D] = MpqData[0x0D] ^ (KeyShuffled[0x06] + KeyMirror[0x09]);
2022         MpqData[0x0E] = MpqData[0x0E] ^ (KeyShuffled[0x0B] + KeyMirror[0x06]);
2023         MpqData[0x0F] = MpqData[0x0F] ^ (KeyShuffled[0x0F] + KeyMirror[0x03]);
2024         BSWAP_ARRAY32_UNSIGNED(MpqData, MPQE_CHUNK_SIZE);
2025 
2026         // Update byte offset in the key
2027         KeyMirror[0x08]++;
2028         if(KeyMirror[0x08] == 0)
2029             KeyMirror[0x05]++;
2030 
2031         // Move pointers and decrease number of bytes to decrypt
2032         MpqData  += (MPQE_CHUNK_SIZE / sizeof(DWORD));
2033         dwLength -= MPQE_CHUNK_SIZE;
2034     }
2035 }
2036 
MpqeStream_DetectFileKey(TEncryptedStream * pStream)2037 static bool MpqeStream_DetectFileKey(TEncryptedStream * pStream)
2038 {
2039     ULONGLONG ByteOffset = 0;
2040     BYTE EncryptedHeader[MPQE_CHUNK_SIZE];
2041     BYTE FileHeader[MPQE_CHUNK_SIZE];
2042 
2043     // Read the first file chunk
2044     if(pStream->BaseRead(pStream, &ByteOffset, EncryptedHeader, sizeof(EncryptedHeader)))
2045     {
2046         // We just try all known keys one by one
2047         for(int i = 0; AuthCodeArray[i] != NULL; i++)
2048         {
2049             // Prepare they decryption key from game serial number
2050             CreateKeyFromAuthCode(pStream->Key, AuthCodeArray[i]);
2051 
2052             // Try to decrypt with the given key
2053             memcpy(FileHeader, EncryptedHeader, MPQE_CHUNK_SIZE);
2054             DecryptFileChunk((LPDWORD)FileHeader, pStream->Key, ByteOffset, MPQE_CHUNK_SIZE);
2055 
2056             // We check the decrypted data
2057             // All known encrypted MPQs have header at the begin of the file,
2058             // so we check for MPQ signature there.
2059             if(FileHeader[0] == 'M' && FileHeader[1] == 'P' && FileHeader[2] == 'Q')
2060             {
2061                 // Update the stream size
2062                 pStream->StreamSize = pStream->Base.File.FileSize;
2063 
2064                 // Fill the block information
2065                 pStream->BlockSize  = MPQE_CHUNK_SIZE;
2066                 pStream->BlockCount = (DWORD)(pStream->Base.File.FileSize + MPQE_CHUNK_SIZE - 1) / MPQE_CHUNK_SIZE;
2067                 pStream->IsComplete = 1;
2068                 return true;
2069             }
2070         }
2071     }
2072 
2073     // Key not found, sorry
2074     return false;
2075 }
2076 
MpqeStream_BlockRead(TEncryptedStream * pStream,ULONGLONG StartOffset,ULONGLONG EndOffset,LPBYTE BlockBuffer,DWORD BytesNeeded,bool bAvailable)2077 static bool MpqeStream_BlockRead(
2078     TEncryptedStream * pStream,
2079     ULONGLONG StartOffset,
2080     ULONGLONG EndOffset,
2081     LPBYTE BlockBuffer,
2082     DWORD BytesNeeded,
2083     bool bAvailable)
2084 {
2085     DWORD dwBytesToRead;
2086 
2087     assert((StartOffset & (pStream->BlockSize - 1)) == 0);
2088     assert(StartOffset < EndOffset);
2089     assert(bAvailable != false);
2090     BytesNeeded = BytesNeeded;
2091     bAvailable = bAvailable;
2092 
2093     // Read the file from the stream as-is
2094     // Limit the reading to number of blocks really needed
2095     dwBytesToRead = (DWORD)(EndOffset - StartOffset);
2096     if(!pStream->BaseRead(pStream, &StartOffset, BlockBuffer, dwBytesToRead))
2097         return false;
2098 
2099     // Decrypt the data
2100     dwBytesToRead = (dwBytesToRead + MPQE_CHUNK_SIZE - 1) & ~(MPQE_CHUNK_SIZE - 1);
2101     DecryptFileChunk((LPDWORD)BlockBuffer, pStream->Key, StartOffset, dwBytesToRead);
2102     return true;
2103 }
2104 
MpqeStream_Open(const TCHAR * szFileName,DWORD dwStreamFlags)2105 static TFileStream * MpqeStream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)
2106 {
2107     TEncryptedStream * pStream;
2108 
2109     // Create new empty stream
2110     pStream = (TEncryptedStream *)AllocateFileStream(szFileName, sizeof(TEncryptedStream), dwStreamFlags);
2111     if(pStream == NULL)
2112         return NULL;
2113 
2114     // Attempt to open the base stream
2115     assert(pStream->BaseOpen != NULL);
2116     if(!pStream->BaseOpen(pStream, pStream->szFileName, dwStreamFlags))
2117         return NULL;
2118 
2119     // Determine the encryption key for the MPQ
2120     if(MpqeStream_DetectFileKey(pStream))
2121     {
2122         // Set the stream position and size
2123         assert(pStream->StreamSize != 0);
2124         pStream->StreamPos = 0;
2125         pStream->dwFlags |= STREAM_FLAG_READ_ONLY;
2126 
2127         // Set new function pointers
2128         pStream->StreamRead    = (STREAM_READ)BlockStream_Read;
2129         pStream->StreamGetPos  = BlockStream_GetPos;
2130         pStream->StreamGetSize = BlockStream_GetSize;
2131         pStream->StreamClose   = pStream->BaseClose;
2132 
2133         // Supply the block functions
2134         pStream->BlockRead     = (BLOCK_READ)MpqeStream_BlockRead;
2135         return pStream;
2136     }
2137 
2138     // Cleanup the stream and return
2139     FileStream_Close(pStream);
2140     SetLastError(ERROR_UNKNOWN_FILE_KEY);
2141     return NULL;
2142 }
2143 
2144 //-----------------------------------------------------------------------------
2145 // Local functions - Block4 stream support
2146 
2147 #define BLOCK4_BLOCK_SIZE   0x4000          // Size of one block
2148 #define BLOCK4_HASH_SIZE    0x20            // Size of MD5 hash that is after each block
2149 #define BLOCK4_MAX_BLOCKS   0x00002000      // Maximum amount of blocks per file
2150 #define BLOCK4_MAX_FSIZE    0x08040000      // Max size of one file
2151 
Block4Stream_BlockRead(TBlockStream * pStream,ULONGLONG StartOffset,ULONGLONG EndOffset,LPBYTE BlockBuffer,DWORD BytesNeeded,bool bAvailable)2152 static bool Block4Stream_BlockRead(
2153     TBlockStream * pStream,                // Pointer to an open stream
2154     ULONGLONG StartOffset,
2155     ULONGLONG EndOffset,
2156     LPBYTE BlockBuffer,
2157     DWORD BytesNeeded,
2158     bool bAvailable)
2159 {
2160     TBaseProviderData * BaseArray = (TBaseProviderData *)pStream->FileBitmap;
2161     ULONGLONG ByteOffset;
2162     DWORD BytesToRead;
2163     DWORD StreamIndex;
2164     DWORD BlockIndex;
2165     bool bResult;
2166 
2167     // The starting offset must be aligned to size of the block
2168     assert(pStream->FileBitmap != NULL);
2169     assert((StartOffset & (pStream->BlockSize - 1)) == 0);
2170     assert(StartOffset < EndOffset);
2171     assert(bAvailable == true);
2172 
2173     // Keep compiler happy
2174     bAvailable = bAvailable;
2175     EndOffset = EndOffset;
2176 
2177     while(BytesNeeded != 0)
2178     {
2179         // Calculate the block index and the file index
2180         StreamIndex = (DWORD)((StartOffset / pStream->BlockSize) / BLOCK4_MAX_BLOCKS);
2181         BlockIndex  = (DWORD)((StartOffset / pStream->BlockSize) % BLOCK4_MAX_BLOCKS);
2182         if(StreamIndex > pStream->BitmapSize)
2183             return false;
2184 
2185         // Calculate the block offset
2186         ByteOffset = ((ULONGLONG)BlockIndex * (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE));
2187         BytesToRead = STORMLIB_MIN(BytesNeeded, BLOCK4_BLOCK_SIZE);
2188 
2189         // Read from the base stream
2190         pStream->Base = BaseArray[StreamIndex];
2191         bResult = pStream->BaseRead(pStream, &ByteOffset, BlockBuffer, BytesToRead);
2192         BaseArray[StreamIndex] = pStream->Base;
2193 
2194         // Did the result succeed?
2195         if(bResult == false)
2196             return false;
2197 
2198         // Move pointers
2199         StartOffset += BytesToRead;
2200         BlockBuffer += BytesToRead;
2201         BytesNeeded -= BytesToRead;
2202     }
2203 
2204     return true;
2205 }
2206 
2207 
Block4Stream_Close(TBlockStream * pStream)2208 static void Block4Stream_Close(TBlockStream * pStream)
2209 {
2210     TBaseProviderData * BaseArray = (TBaseProviderData *)pStream->FileBitmap;
2211 
2212     // If we have a non-zero count of base streams,
2213     // we have to close them all
2214     if(BaseArray != NULL)
2215     {
2216         // Close all base streams
2217         for(DWORD i = 0; i < pStream->BitmapSize; i++)
2218         {
2219             memcpy(&pStream->Base, BaseArray + i, sizeof(TBaseProviderData));
2220             pStream->BaseClose(pStream);
2221         }
2222     }
2223 
2224     // Free the data map, if any
2225     if(pStream->FileBitmap != NULL)
2226         STORM_FREE(pStream->FileBitmap);
2227     pStream->FileBitmap = NULL;
2228 
2229     // Do not call the BaseClose function,
2230     // we closed all handles already
2231     return;
2232 }
2233 
Block4Stream_Open(const TCHAR * szFileName,DWORD dwStreamFlags)2234 static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamFlags)
2235 {
2236     TBaseProviderData * NewBaseArray = NULL;
2237     ULONGLONG RemainderBlock;
2238     ULONGLONG BlockCount;
2239     ULONGLONG FileSize;
2240     TBlockStream * pStream;
2241     TCHAR * szNameBuff;
2242     size_t nNameLength;
2243     DWORD dwBaseFiles = 0;
2244     DWORD dwBaseFlags;
2245 
2246     // Create new empty stream
2247     pStream = (TBlockStream *)AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags);
2248     if(pStream == NULL)
2249         return NULL;
2250 
2251     // Sanity check
2252     assert(pStream->BaseOpen != NULL);
2253 
2254     // Get the length of the file name without numeric suffix
2255     nNameLength = _tcslen(pStream->szFileName);
2256     if(pStream->szFileName[nNameLength - 2] == '.' && pStream->szFileName[nNameLength - 1] == '0')
2257         nNameLength -= 2;
2258     pStream->szFileName[nNameLength] = 0;
2259 
2260     // Supply the stream functions
2261     pStream->StreamRead    = (STREAM_READ)BlockStream_Read;
2262     pStream->StreamGetSize = BlockStream_GetSize;
2263     pStream->StreamGetPos  = BlockStream_GetPos;
2264     pStream->StreamClose   = (STREAM_CLOSE)Block4Stream_Close;
2265     pStream->BlockRead     = (BLOCK_READ)Block4Stream_BlockRead;
2266 
2267     // Allocate work space for numeric names
2268     szNameBuff = STORM_ALLOC(TCHAR, nNameLength + 4);
2269     if(szNameBuff != NULL)
2270     {
2271         // Set the base flags
2272         dwBaseFlags = (dwStreamFlags & STREAM_PROVIDERS_MASK) | STREAM_FLAG_READ_ONLY;
2273 
2274         // Go all suffixes from 0 to 30
2275         for(int nSuffix = 0; nSuffix < 30; nSuffix++)
2276         {
2277             // Open the n-th file
2278             _stprintf(szNameBuff, _T("%s.%u"), pStream->szFileName, nSuffix);
2279             if(!pStream->BaseOpen(pStream, szNameBuff, dwBaseFlags))
2280                 break;
2281 
2282             // If the open succeeded, we re-allocate the base provider array
2283             NewBaseArray = STORM_ALLOC(TBaseProviderData, dwBaseFiles + 1);
2284             if(NewBaseArray == NULL)
2285             {
2286                 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
2287                 return NULL;
2288             }
2289 
2290             // Copy the old base data array to the new base data array
2291             if(pStream->FileBitmap != NULL)
2292             {
2293                 memcpy(NewBaseArray, pStream->FileBitmap, sizeof(TBaseProviderData) * dwBaseFiles);
2294                 STORM_FREE(pStream->FileBitmap);
2295             }
2296 
2297             // Also copy the opened base array
2298             memcpy(NewBaseArray + dwBaseFiles, &pStream->Base, sizeof(TBaseProviderData));
2299             pStream->FileBitmap = NewBaseArray;
2300             dwBaseFiles++;
2301 
2302             // Get the size of the base stream
2303             pStream->BaseGetSize(pStream, &FileSize);
2304             assert(FileSize <= BLOCK4_MAX_FSIZE);
2305             RemainderBlock = FileSize % (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE);
2306             BlockCount = FileSize / (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE);
2307 
2308             // Increment the stream size and number of blocks
2309             pStream->StreamSize += (BlockCount * BLOCK4_BLOCK_SIZE);
2310             pStream->BlockCount += (DWORD)BlockCount;
2311 
2312             // Is this the last file?
2313             if(FileSize < BLOCK4_MAX_FSIZE)
2314             {
2315                 if(RemainderBlock)
2316                 {
2317                     pStream->StreamSize += (RemainderBlock - BLOCK4_HASH_SIZE);
2318                     pStream->BlockCount++;
2319                 }
2320                 break;
2321             }
2322         }
2323 
2324         // Fill the remainining block stream variables
2325         pStream->BitmapSize = dwBaseFiles;
2326         pStream->BlockSize  = BLOCK4_BLOCK_SIZE;
2327         pStream->IsComplete = 1;
2328         pStream->IsModified = 0;
2329 
2330         // Fill the remaining stream variables
2331         pStream->StreamPos = 0;
2332         pStream->dwFlags |= STREAM_FLAG_READ_ONLY;
2333 
2334         STORM_FREE(szNameBuff);
2335     }
2336 
2337     // If we opened something, return success
2338     if(dwBaseFiles == 0)
2339     {
2340         FileStream_Close(pStream);
2341         SetLastError(ERROR_FILE_NOT_FOUND);
2342         pStream = NULL;
2343     }
2344 
2345     return pStream;
2346 }
2347 
2348 //-----------------------------------------------------------------------------
2349 // Public functions
2350 
2351 /**
2352  * This function creates a new file for read-write access
2353  *
2354  * - If the current platform supports file sharing,
2355  *   the file must be created for read sharing (i.e. another application
2356  *   can open the file for read, but not for write)
2357  * - If the file does not exist, the function must create new one
2358  * - If the file exists, the function must rewrite it and set to zero size
2359  * - The parameters of the function must be validate by the caller
2360  * - The function must initialize all stream function pointers in TFileStream
2361  * - If the function fails from any reason, it must close all handles
2362  *   and free all memory that has been allocated in the process of stream creation,
2363  *   including the TFileStream structure itself
2364  *
2365  * \a szFileName Name of the file to create
2366  */
2367 
FileStream_CreateFile(const TCHAR * szFileName,DWORD dwStreamFlags)2368 TFileStream * FileStream_CreateFile(
2369     const TCHAR * szFileName,
2370     DWORD dwStreamFlags)
2371 {
2372     TFileStream * pStream;
2373 
2374     // We only support creation of flat, local file
2375     if((dwStreamFlags & (STREAM_PROVIDERS_MASK)) != (STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE))
2376     {
2377         SetLastError(ERROR_NOT_SUPPORTED);
2378         return NULL;
2379     }
2380 
2381     // Allocate file stream structure for flat stream
2382     pStream = AllocateFileStream(szFileName, sizeof(TBlockStream), dwStreamFlags);
2383     if(pStream != NULL)
2384     {
2385         // Attempt to create the disk file
2386         if(BaseFile_Create(pStream))
2387         {
2388             // Fill the stream provider functions
2389             pStream->StreamRead    = pStream->BaseRead;
2390             pStream->StreamWrite   = pStream->BaseWrite;
2391             pStream->StreamResize  = pStream->BaseResize;
2392             pStream->StreamGetSize = pStream->BaseGetSize;
2393             pStream->StreamGetPos  = pStream->BaseGetPos;
2394             pStream->StreamClose   = pStream->BaseClose;
2395             return pStream;
2396         }
2397 
2398         // File create failed, delete the stream
2399         STORM_FREE(pStream);
2400         pStream = NULL;
2401     }
2402 
2403     // Return the stream
2404     return pStream;
2405 }
2406 
2407 /**
2408  * This function opens an existing file for read or read-write access
2409  * - If the current platform supports file sharing,
2410  *   the file must be open for read sharing (i.e. another application
2411  *   can open the file for read, but not for write)
2412  * - If the file does not exist, the function must return NULL
2413  * - If the file exists but cannot be open, then function must return NULL
2414  * - The parameters of the function must be validate by the caller
2415  * - The function must initialize all stream function pointers in TFileStream
2416  * - If the function fails from any reason, it must close all handles
2417  *   and free all memory that has been allocated in the process of stream creation,
2418  *   including the TFileStream structure itself
2419  *
2420  * \a szFileName Name of the file to open
2421  * \a dwStreamFlags specifies the provider and base storage type
2422  */
2423 
FileStream_OpenFile(const TCHAR * szFileName,DWORD dwStreamFlags)2424 TFileStream * FileStream_OpenFile(
2425     const TCHAR * szFileName,
2426     DWORD dwStreamFlags)
2427 {
2428     DWORD dwProvider = dwStreamFlags & STREAM_PROVIDERS_MASK;
2429     size_t nPrefixLength = FileStream_Prefix(szFileName, &dwProvider);
2430 
2431     // Re-assemble the stream flags
2432     dwStreamFlags = (dwStreamFlags & STREAM_OPTIONS_MASK) | dwProvider;
2433     szFileName += nPrefixLength;
2434 
2435     // Perform provider-specific open
2436     switch(dwStreamFlags & STREAM_PROVIDER_MASK)
2437     {
2438         case STREAM_PROVIDER_FLAT:
2439             return FlatStream_Open(szFileName, dwStreamFlags);
2440 
2441         case STREAM_PROVIDER_PARTIAL:
2442             return PartStream_Open(szFileName, dwStreamFlags);
2443 
2444         case STREAM_PROVIDER_MPQE:
2445             return MpqeStream_Open(szFileName, dwStreamFlags);
2446 
2447         case STREAM_PROVIDER_BLOCK4:
2448             return Block4Stream_Open(szFileName, dwStreamFlags);
2449 
2450         default:
2451             SetLastError(ERROR_INVALID_PARAMETER);
2452             return NULL;
2453     }
2454 }
2455 
2456 /**
2457  * Returns the file name of the stream
2458  *
2459  * \a pStream Pointer to an open stream
2460  */
FileStream_GetFileName(TFileStream * pStream)2461 const TCHAR * FileStream_GetFileName(TFileStream * pStream)
2462 {
2463     assert(pStream != NULL);
2464     return pStream->szFileName;
2465 }
2466 
2467 /**
2468  * Returns the length of the provider prefix. Returns zero if no prefix
2469  *
2470  * \a szFileName Pointer to a stream name (file, mapped file, URL)
2471  * \a pdwStreamProvider Pointer to a DWORD variable that receives stream provider (STREAM_PROVIDER_XXX)
2472  */
2473 
FileStream_Prefix(const TCHAR * szFileName,DWORD * pdwProvider)2474 size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider)
2475 {
2476     size_t nPrefixLength1 = 0;
2477     size_t nPrefixLength2 = 0;
2478     DWORD dwProvider = 0;
2479 
2480     if(szFileName != NULL)
2481     {
2482         //
2483         // Determine the stream provider
2484         //
2485 
2486         if(!_tcsnicmp(szFileName, _T("flat-"), 5))
2487         {
2488             dwProvider |= STREAM_PROVIDER_FLAT;
2489             nPrefixLength1 = 5;
2490         }
2491 
2492         else if(!_tcsnicmp(szFileName, _T("part-"), 5))
2493         {
2494             dwProvider |= STREAM_PROVIDER_PARTIAL;
2495             nPrefixLength1 = 5;
2496         }
2497 
2498         else if(!_tcsnicmp(szFileName, _T("mpqe-"), 5))
2499         {
2500             dwProvider |= STREAM_PROVIDER_MPQE;
2501             nPrefixLength1 = 5;
2502         }
2503 
2504         else if(!_tcsnicmp(szFileName, _T("blk4-"), 5))
2505         {
2506             dwProvider |= STREAM_PROVIDER_BLOCK4;
2507             nPrefixLength1 = 5;
2508         }
2509 
2510         //
2511         // Determine the base provider
2512         //
2513 
2514         if(!_tcsnicmp(szFileName+nPrefixLength1, _T("file:"), 5))
2515         {
2516             dwProvider |= BASE_PROVIDER_FILE;
2517             nPrefixLength2 = 5;
2518         }
2519 
2520         else if(!_tcsnicmp(szFileName+nPrefixLength1, _T("map:"), 4))
2521         {
2522             dwProvider |= BASE_PROVIDER_MAP;
2523             nPrefixLength2 = 4;
2524         }
2525 
2526         else if(!_tcsnicmp(szFileName+nPrefixLength1, _T("http:"), 5))
2527         {
2528             dwProvider |= BASE_PROVIDER_HTTP;
2529             nPrefixLength2 = 5;
2530         }
2531 
2532         // Only accept stream provider if we recognized the base provider
2533         if(nPrefixLength2 != 0)
2534         {
2535             // It is also allowed to put "//" after the base provider, e.g. "file://", "http://"
2536             if(szFileName[nPrefixLength1+nPrefixLength2] == '/' && szFileName[nPrefixLength1+nPrefixLength2+1] == '/')
2537                 nPrefixLength2 += 2;
2538 
2539             if(pdwProvider != NULL)
2540                 *pdwProvider = dwProvider;
2541             return nPrefixLength1 + nPrefixLength2;
2542         }
2543     }
2544 
2545     return 0;
2546 }
2547 
2548 /**
2549  * Sets a download callback. Whenever the stream needs to download one or more blocks
2550  * from the server, the callback is called
2551  *
2552  * \a pStream Pointer to an open stream
2553  * \a pfnCallback Pointer to callback function
2554  * \a pvUserData Arbitrary user pointer passed to the download callback
2555  */
2556 
FileStream_SetCallback(TFileStream * pStream,SFILE_DOWNLOAD_CALLBACK pfnCallback,void * pvUserData)2557 bool FileStream_SetCallback(TFileStream * pStream, SFILE_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData)
2558 {
2559     TBlockStream * pBlockStream = (TBlockStream *)pStream;
2560 
2561     if(pStream->BlockRead == NULL)
2562     {
2563         SetLastError(ERROR_NOT_SUPPORTED);
2564         return false;
2565     }
2566 
2567     pBlockStream->pfnCallback = pfnCallback;
2568     pBlockStream->UserData = pvUserData;
2569     return true;
2570 }
2571 
2572 /**
2573  * This function gives the block map. The 'pvBitmap' pointer must point to a buffer
2574  * of at least sizeof(STREAM_BLOCK_MAP) size. It can also have size of the complete
2575  * block map (i.e. sizeof(STREAM_BLOCK_MAP) + BitmapSize). In that case, the function
2576  * also copies the bit-based block map.
2577  *
2578  * \a pStream Pointer to an open stream
2579  * \a pvBitmap Pointer to buffer where the block map will be stored
2580  * \a cbBitmap Length of the buffer, of the block map
2581  * \a cbLengthNeeded Length of the bitmap, in bytes
2582  */
2583 
FileStream_GetBitmap(TFileStream * pStream,void * pvBitmap,DWORD cbBitmap,DWORD * pcbLengthNeeded)2584 bool FileStream_GetBitmap(TFileStream * pStream, void * pvBitmap, DWORD cbBitmap, DWORD * pcbLengthNeeded)
2585 {
2586     TStreamBitmap * pBitmap = (TStreamBitmap *)pvBitmap;
2587     TBlockStream * pBlockStream = (TBlockStream *)pStream;
2588     ULONGLONG BlockOffset;
2589     LPBYTE Bitmap = (LPBYTE)(pBitmap + 1);
2590     DWORD BitmapSize;
2591     DWORD BlockCount;
2592     DWORD BlockSize;
2593     bool bResult = false;
2594 
2595     // Retrieve the size of one block
2596     if(pStream->BlockCheck != NULL)
2597     {
2598         BlockCount = pBlockStream->BlockCount;
2599         BlockSize = pBlockStream->BlockSize;
2600     }
2601     else
2602     {
2603         BlockCount = (DWORD)((pStream->StreamSize + DEFAULT_BLOCK_SIZE - 1) / DEFAULT_BLOCK_SIZE);
2604         BlockSize = DEFAULT_BLOCK_SIZE;
2605     }
2606 
2607     // Fill-in the variables
2608     BitmapSize = (BlockCount + 7) / 8;
2609 
2610     // Give the number of blocks
2611     if(pcbLengthNeeded != NULL)
2612         pcbLengthNeeded[0] = sizeof(TStreamBitmap) + BitmapSize;
2613 
2614     // If the length of the buffer is not enough
2615     if(pvBitmap != NULL && cbBitmap != 0)
2616     {
2617         // Give the STREAM_BLOCK_MAP structure
2618         if(cbBitmap >= sizeof(TStreamBitmap))
2619         {
2620             pBitmap->StreamSize = pStream->StreamSize;
2621             pBitmap->BitmapSize = BitmapSize;
2622             pBitmap->BlockCount = BlockCount;
2623             pBitmap->BlockSize  = BlockSize;
2624             pBitmap->IsComplete = (pStream->BlockCheck != NULL) ? pBlockStream->IsComplete : 1;
2625             bResult = true;
2626         }
2627 
2628         // Give the block bitmap, if enough space
2629         if(cbBitmap >= sizeof(TStreamBitmap) + BitmapSize)
2630         {
2631             // Version with bitmap present
2632             if(pStream->BlockCheck != NULL)
2633             {
2634                 DWORD ByteIndex = 0;
2635                 BYTE BitMask = 0x01;
2636 
2637                 // Initialize the map with zeros
2638                 memset(Bitmap, 0, BitmapSize);
2639 
2640                 // Fill the map
2641                 for(BlockOffset = 0; BlockOffset < pStream->StreamSize; BlockOffset += BlockSize)
2642                 {
2643                     // Set the bit if the block is present
2644                     if(pBlockStream->BlockCheck(pStream, BlockOffset))
2645                         Bitmap[ByteIndex] |= BitMask;
2646 
2647                     // Move bit position
2648                     ByteIndex += (BitMask >> 0x07);
2649                     BitMask = (BitMask >> 0x07) | (BitMask << 0x01);
2650                 }
2651             }
2652             else
2653             {
2654                 memset(Bitmap, 0xFF, BitmapSize);
2655             }
2656         }
2657     }
2658 
2659     // Set last error value and return
2660     if(bResult == false)
2661         SetLastError(ERROR_INSUFFICIENT_BUFFER);
2662     return bResult;
2663 }
2664 
2665 /**
2666  * Reads data from the stream
2667  *
2668  * - Returns true if the read operation succeeded and all bytes have been read
2669  * - Returns false if either read failed or not all bytes have been read
2670  * - If the pByteOffset is NULL, the function must read the data from the current file position
2671  * - The function can be called with dwBytesToRead = 0. In that case, pvBuffer is ignored
2672  *   and the function just adjusts file pointer.
2673  *
2674  * \a pStream Pointer to an open stream
2675  * \a pByteOffset Pointer to file byte offset. If NULL, it reads from the current position
2676  * \a pvBuffer Pointer to data to be read
2677  * \a dwBytesToRead Number of bytes to read from the file
2678  *
2679  * \returns
2680  * - If the function reads the required amount of bytes, it returns true.
2681  * - If the function reads less than required bytes, it returns false and GetLastError() returns ERROR_HANDLE_EOF
2682  * - If the function fails, it reads false and GetLastError() returns an error code different from ERROR_HANDLE_EOF
2683  */
FileStream_Read(TFileStream * pStream,ULONGLONG * pByteOffset,void * pvBuffer,DWORD dwBytesToRead)2684 bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead)
2685 {
2686     assert(pStream->StreamRead != NULL);
2687     return pStream->StreamRead(pStream, pByteOffset, pvBuffer, dwBytesToRead);
2688 }
2689 
2690 /**
2691  * This function writes data to the stream
2692  *
2693  * - Returns true if the write operation succeeded and all bytes have been written
2694  * - Returns false if either write failed or not all bytes have been written
2695  * - If the pByteOffset is NULL, the function must write the data to the current file position
2696  *
2697  * \a pStream Pointer to an open stream
2698  * \a pByteOffset Pointer to file byte offset. If NULL, it reads from the current position
2699  * \a pvBuffer Pointer to data to be written
2700  * \a dwBytesToWrite Number of bytes to write to the file
2701  */
FileStream_Write(TFileStream * pStream,ULONGLONG * pByteOffset,const void * pvBuffer,DWORD dwBytesToWrite)2702 bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite)
2703 {
2704     if(pStream->dwFlags & STREAM_FLAG_READ_ONLY)
2705     {
2706         SetLastError(ERROR_ACCESS_DENIED);
2707         return false;
2708     }
2709 
2710     assert(pStream->StreamWrite != NULL);
2711     return pStream->StreamWrite(pStream, pByteOffset, pvBuffer, dwBytesToWrite);
2712 }
2713 
2714 /**
2715  * Returns the size of a file
2716  *
2717  * \a pStream Pointer to an open stream
2718  * \a FileSize Pointer where to store the file size
2719  */
FileStream_GetSize(TFileStream * pStream,ULONGLONG * pFileSize)2720 bool FileStream_GetSize(TFileStream * pStream, ULONGLONG * pFileSize)
2721 {
2722     assert(pStream->StreamGetSize != NULL);
2723     return pStream->StreamGetSize(pStream, pFileSize);
2724 }
2725 
2726 /**
2727  * Sets the size of a file
2728  *
2729  * \a pStream Pointer to an open stream
2730  * \a NewFileSize File size to set
2731  */
FileStream_SetSize(TFileStream * pStream,ULONGLONG NewFileSize)2732 bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize)
2733 {
2734     if(pStream->dwFlags & STREAM_FLAG_READ_ONLY)
2735     {
2736         SetLastError(ERROR_ACCESS_DENIED);
2737         return false;
2738     }
2739 
2740     assert(pStream->StreamResize != NULL);
2741     return pStream->StreamResize(pStream, NewFileSize);
2742 }
2743 
2744 /**
2745  * This function returns the current file position
2746  * \a pStream
2747  * \a pByteOffset
2748  */
FileStream_GetPos(TFileStream * pStream,ULONGLONG * pByteOffset)2749 bool FileStream_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset)
2750 {
2751     assert(pStream->StreamGetPos != NULL);
2752     return pStream->StreamGetPos(pStream, pByteOffset);
2753 }
2754 
2755 /**
2756  * Returns the last write time of a file
2757  *
2758  * \a pStream Pointer to an open stream
2759  * \a pFileType Pointer where to store the file last write time
2760  */
FileStream_GetTime(TFileStream * pStream,ULONGLONG * pFileTime)2761 bool FileStream_GetTime(TFileStream * pStream, ULONGLONG * pFileTime)
2762 {
2763     // Just use the saved filetime value
2764     *pFileTime = pStream->Base.File.FileTime;
2765     return true;
2766 }
2767 
2768 /**
2769  * Returns the stream flags
2770  *
2771  * \a pStream Pointer to an open stream
2772  * \a pdwStreamFlags Pointer where to store the stream flags
2773  */
FileStream_GetFlags(TFileStream * pStream,LPDWORD pdwStreamFlags)2774 bool FileStream_GetFlags(TFileStream * pStream, LPDWORD pdwStreamFlags)
2775 {
2776     *pdwStreamFlags = pStream->dwFlags;
2777     return true;
2778 }
2779 
2780 /**
2781  * Switches a stream with another. Used for final phase of archive compacting.
2782  * Performs these steps:
2783  *
2784  * 1) Closes the handle to the existing MPQ
2785  * 2) Renames the temporary MPQ to the original MPQ, overwrites existing one
2786  * 3) Opens the MPQ stores the handle and stream position to the new stream structure
2787  *
2788  * \a pStream Pointer to an open stream
2789  * \a pNewStream Temporary ("working") stream (created during archive compacting)
2790  */
FileStream_Replace(TFileStream * pStream,TFileStream * pNewStream)2791 bool FileStream_Replace(TFileStream * pStream, TFileStream * pNewStream)
2792 {
2793     // Only supported on flat files
2794     if((pStream->dwFlags & STREAM_PROVIDERS_MASK) != (STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE))
2795     {
2796         SetLastError(ERROR_NOT_SUPPORTED);
2797         return false;
2798     }
2799 
2800     // Not supported on read-only streams
2801     if(pStream->dwFlags & STREAM_FLAG_READ_ONLY)
2802     {
2803         SetLastError(ERROR_ACCESS_DENIED);
2804         return false;
2805     }
2806 
2807     // Close both stream's base providers
2808     pNewStream->BaseClose(pNewStream);
2809     pStream->BaseClose(pStream);
2810 
2811     // Now we have to delete the (now closed) old file and rename the new file
2812     if(!BaseFile_Replace(pStream, pNewStream))
2813         return false;
2814 
2815     // Now open the base file again
2816     if(!BaseFile_Open(pStream, pStream->szFileName, pStream->dwFlags))
2817         return false;
2818 
2819     // Cleanup the new stream
2820     FileStream_Close(pNewStream);
2821     return true;
2822 }
2823 
2824 /**
2825  * This function closes an archive file and frees any data buffers
2826  * that have been allocated for stream management. The function must also
2827  * support partially allocated structure, i.e. one or more buffers
2828  * can be NULL, if there was an allocation failure during the process
2829  *
2830  * \a pStream Pointer to an open stream
2831  */
FileStream_Close(TFileStream * pStream)2832 void FileStream_Close(TFileStream * pStream)
2833 {
2834     // Check if the stream structure is allocated at all
2835     if(pStream != NULL)
2836     {
2837         // Free the master stream, if any
2838         if(pStream->pMaster != NULL)
2839             FileStream_Close(pStream->pMaster);
2840         pStream->pMaster = NULL;
2841 
2842         // Close the stream provider ...
2843         if(pStream->StreamClose != NULL)
2844             pStream->StreamClose(pStream);
2845 
2846         // ... or close base stream, if any
2847         else if(pStream->BaseClose != NULL)
2848             pStream->BaseClose(pStream);
2849 
2850         // Free the stream itself
2851         STORM_FREE(pStream);
2852     }
2853 }
2854