aboutsummaryrefslogtreecommitdiff
path: root/dep
diff options
context:
space:
mode:
Diffstat (limited to 'dep')
-rw-r--r--dep/CascLib/CMakeLists.txt4
-rw-r--r--dep/CascLib/src/CascCommon.h9
-rw-r--r--dep/CascLib/src/CascDumpData.cpp5
-rw-r--r--dep/CascLib/src/CascFiles.cpp303
-rw-r--r--dep/CascLib/src/CascIndexFiles.cpp6
-rw-r--r--dep/CascLib/src/CascLib.h4
-rw-r--r--dep/CascLib/src/CascOpenStorage.cpp28
-rw-r--r--dep/CascLib/src/CascPort.h20
-rw-r--r--dep/CascLib/src/CascReadFile.cpp2
-rw-r--r--dep/CascLib/src/CascRootFile_WoW.cpp2
-rw-r--r--dep/CascLib/src/DllMain.rc8
-rw-r--r--dep/CascLib/src/common/Common.cpp20
-rw-r--r--dep/CascLib/src/common/Common.h17
-rw-r--r--dep/CascLib/src/common/Csv.cpp32
-rw-r--r--dep/CascLib/src/common/Csv.h22
-rw-r--r--dep/CascLib/src/common/Directory.cpp8
-rw-r--r--dep/CascLib/src/common/FileStream.cpp367
-rw-r--r--dep/CascLib/src/common/FileStream.h19
-rw-r--r--dep/CascLib/src/common/Mime.cpp674
-rw-r--r--dep/CascLib/src/common/Mime.h130
-rw-r--r--dep/CascLib/src/common/Path.h4
-rw-r--r--dep/CascLib/src/common/Sockets.cpp441
-rw-r--r--dep/CascLib/src/common/Sockets.h115
-rw-r--r--dep/PackageList.txt2
24 files changed, 1849 insertions, 393 deletions
diff --git a/dep/CascLib/CMakeLists.txt b/dep/CascLib/CMakeLists.txt
index cce693a6146..273b7155468 100644
--- a/dep/CascLib/CMakeLists.txt
+++ b/dep/CascLib/CMakeLists.txt
@@ -12,8 +12,10 @@ set(HEADER_FILES
src/common/IndexMap.h
src/common/ListFile.h
src/common/Map.h
+ src/common/Mime.h
src/common/Path.h
src/common/RootHandler.h
+ src/common/Sockets.h
src/jenkins/lookup.h
)
@@ -24,7 +26,9 @@ set(SRC_FILES
src/common/FileStream.cpp
src/common/FileTree.cpp
src/common/ListFile.cpp
+ src/common/Mime.cpp
src/common/RootHandler.cpp
+ src/common/Sockets.cpp
src/jenkins/lookup3.c
src/CascDecompress.cpp
src/CascDecrypt.cpp
diff --git a/dep/CascLib/src/CascCommon.h b/dep/CascLib/src/CascCommon.h
index aa0aff876ce..cddd0a8ca0e 100644
--- a/dep/CascLib/src/CascCommon.h
+++ b/dep/CascLib/src/CascCommon.h
@@ -30,8 +30,10 @@
#include "common/Directory.h"
#include "common/ListFile.h"
#include "common/Csv.h"
+#include "common/Mime.h"
#include "common/Path.h"
#include "common/RootHandler.h"
+#include "common/Sockets.h"
// Headers from Alexander Peslyak's MD5 implementation
#include "md5/md5.h"
@@ -58,8 +60,7 @@
#define CASC_MAGIC_FIND 0x444E494643534143 // 'CASCFIND'
// For CASC_CDN_DOWNLOAD::Flags
-#define CASC_CDN_FLAG_PORT1119 0x0001 // Use port 1119
-#define CASC_CDN_FORCE_DOWNLOAD 0x0002 // Force downloading the file even if in the cache
+#define CASC_CDN_FORCE_DOWNLOAD 0x0001 // Force downloading the file even if in the cache
//-----------------------------------------------------------------------------
// In-memory structures
@@ -101,7 +102,7 @@ typedef struct _CASC_INDEX
typedef struct _CASC_INDEX_HEADER
{
USHORT IndexVersion; // 5 for index v 1.0, 7 for index version 2.0
- BYTE BucketIndex; // Should be the same as the first byte of the hex filename.
+ BYTE BucketIndex; // Should be the same as the first byte of the hex filename.
BYTE StorageOffsetLength; // Length, in bytes, of the StorageOffset field in the EKey entry
BYTE EncodedSizeLength; // Length, in bytes, of the EncodedSize in the EKey entry
BYTE EKeyLength; // Length, in bytes, of the (trimmed) EKey in the EKey entry
@@ -367,7 +368,7 @@ struct TCascFile
DWORD bVerifyIntegrity:1; // If true, then the data are validated more strictly when read
DWORD bDownloadFileIf:1; // If true, then the data will be downloaded from the online storage if missing
DWORD bCloseFileStream:1; // If true, file stream needs to be closed during CascCloseFile
- DWORD bOvercomeEncrypted:1; // If true, then CascReadFile will fill the part that is encrypted (and key was not found) with zeros
+ DWORD bOvercomeEncrypted:1; // If true, then CascReadFile will fill the part that is encrypted (and key was not found) with zeros
DWORD bFreeCKeyEntries:1; // If true, dectructor will free the array of CKey entries
ULONGLONG FileCacheStart; // Starting offset of the file cached area
diff --git a/dep/CascLib/src/CascDumpData.cpp b/dep/CascLib/src/CascDumpData.cpp
index e29ad66bf63..9437ba52db2 100644
--- a/dep/CascLib/src/CascDumpData.cpp
+++ b/dep/CascLib/src/CascDumpData.cpp
@@ -530,4 +530,9 @@ void CascDumpStorage(HANDLE hStorage, const char * szDumpFile)
}
}
+#else // _DEBUG
+
+// so linker won't mind this .cpp file is empty in non-DEBUG builds
+void unused_symbol() { }
+
#endif // _DEBUG
diff --git a/dep/CascLib/src/CascFiles.cpp b/dep/CascLib/src/CascFiles.cpp
index 596a07d3362..695d7d4af6a 100644
--- a/dep/CascLib/src/CascFiles.cpp
+++ b/dep/CascLib/src/CascFiles.cpp
@@ -13,6 +13,10 @@
#include "CascLib.h"
#include "CascCommon.h"
+#ifdef _MSC_VER
+#pragma comment(lib, "ws2_32.lib") // Internet functions for HTTP stream
+#endif
+
//-----------------------------------------------------------------------------
// Local defines
@@ -56,6 +60,8 @@ static LPCTSTR DataDirs[] =
NULL,
};
+static LPCTSTR bnet_region = _T("us");
+
//-----------------------------------------------------------------------------
// Local functions
@@ -93,12 +99,12 @@ static const char * CaptureDecimalInteger(const char * szDataPtr, const char * s
while (szDataPtr < szDataEnd && szDataPtr[0] != ' ')
{
// Must only contain decimal digits ('0' - '9')
- if (!IsCharDigit(szDataPtr[0]))
+ if(!IsCharDigit(szDataPtr[0]))
break;
// Get the next value and verify overflow
AddValue = szDataPtr[0] - '0';
- if ((TotalValue + AddValue) < TotalValue)
+ if((TotalValue + AddValue) < TotalValue)
return NULL;
TotalValue = (TotalValue * 10) + AddValue;
@@ -143,13 +149,13 @@ static const char * CaptureSingleHash(const char * szDataPtr, const char * szDat
// Count all hash characters
for (size_t i = 0; i < HashStringLength; i++)
{
- if (szDataPtr >= szDataEnd || isxdigit(szDataPtr[0]) == 0)
+ if(szDataPtr >= szDataEnd || isxdigit(szDataPtr[0]) == 0)
return NULL;
szDataPtr++;
}
// There must be a separator or end-of-string
- if (szDataPtr > szDataEnd || IsWhiteSpace(szDataPtr) == false)
+ if(szDataPtr > szDataEnd || IsWhiteSpace(szDataPtr) == false)
return NULL;
// Give the values
@@ -167,7 +173,7 @@ static const char * CaptureHashCount(const char * szDataPtr, const char * szData
{
// Check one hash
szDataPtr = CaptureSingleHash(szDataPtr, szDataEnd, HashValue, MD5_HASH_SIZE);
- if (szDataPtr == NULL)
+ if(szDataPtr == NULL)
return NULL;
// Skip all whitespaces
@@ -227,11 +233,11 @@ static bool CheckConfigFileVariable(
// Capture the variable from the line
szLinePtr = CaptureSingleString(szLinePtr, szLineEnd, szVariableName, sizeof(szVariableName));
- if (szLinePtr == NULL)
+ if(szLinePtr == NULL)
return false;
// Verify whether this is the variable
- if (!CascCheckWildCard(szVariableName, szVarName))
+ if(!CascCheckWildCard(szVariableName, szVarName))
return false;
// Skip the spaces and '='
@@ -239,7 +245,7 @@ static bool CheckConfigFileVariable(
szLinePtr++;
// Call the parsing function only if there is some data
- if (szLinePtr >= szLineEnd)
+ if(szLinePtr >= szLineEnd)
return false;
return (PfnParseProc(hs, szVariableName, szLinePtr, szLineEnd, pvParseParam) == ERROR_SUCCESS);
@@ -256,7 +262,7 @@ static DWORD LoadHashArray(
// Allocate the blob buffer
pBlob->cbData = (DWORD)(HashCount * MD5_HASH_SIZE);
pBlob->pbData = CASC_ALLOC<BYTE>(pBlob->cbData);
- if (pBlob->pbData != NULL)
+ if(pBlob->pbData != NULL)
{
LPBYTE pbBuffer = pBlob->pbData;
@@ -264,7 +270,7 @@ static DWORD LoadHashArray(
{
// Capture the hash value
szLinePtr = CaptureSingleHash(szLinePtr, szLineEnd, pbBuffer, MD5_HASH_SIZE);
- if (szLinePtr == NULL)
+ if(szLinePtr == NULL)
return ERROR_BAD_FORMAT;
// Move buffer
@@ -283,7 +289,7 @@ static DWORD LoadMultipleHashes(PQUERY_KEY pBlob, const char * szLineBegin, cons
DWORD dwErrCode = ERROR_SUCCESS;
// Retrieve the hash count
- if (CaptureHashCount(szLineBegin, szLineEnd, &HashCount) == NULL)
+ if(CaptureHashCount(szLineBegin, szLineEnd, &HashCount) == NULL)
return ERROR_BAD_FORMAT;
// Do nothing if there is no data
@@ -398,10 +404,10 @@ static DWORD LoadVfsRootEntry(TCascStorage * hs, const char * szVariableName, co
DWORD VfsRootIndex = CASC_INVALID_INDEX;
// Skip the "vfs-" part
- if (!strncmp(szVariableName, "vfs-", 4))
+ if(!strncmp(szVariableName, "vfs-", 4))
{
// Then, there must be a decimal number as index
- if ((szVarPtr = CaptureDecimalInteger(szVarPtr + 4, szVarEnd, &VfsRootIndex)) != NULL)
+ if((szVarPtr = CaptureDecimalInteger(szVarPtr + 4, szVarEnd, &VfsRootIndex)) != NULL)
{
// We expect the array to be initialized
assert(pArray->IsInitialized());
@@ -484,9 +490,9 @@ static DWORD LoadBuildNumber(TCascStorage * hs, const char * /* szVariableName *
static int LoadQueryKey(const CASC_CSV_COLUMN & Column, QUERY_KEY & Key)
{
// Check the input data
- if (Column.szValue == NULL)
+ if(Column.szValue == NULL)
return ERROR_BUFFER_OVERFLOW;
- if (Column.nLength != MD5_STRING_SIZE)
+ if(Column.nLength != MD5_STRING_SIZE)
return ERROR_BAD_FORMAT;
return LoadHashArray(&Key, Column.szValue, Column.szValue + Column.nLength, 1);
@@ -538,6 +544,7 @@ static DWORD GetDefaultLocaleMask(TCascStorage * hs, const CASC_CSV_COLUMN & Col
static DWORD ParseFile_CDNS(TCascStorage * hs, CASC_CSV & Csv)
{
const char * szWantedRegion = hs->szRegion;
+ const char * szRegion;
size_t nLineCount;
// Fix the region
@@ -548,19 +555,23 @@ static DWORD ParseFile_CDNS(TCascStorage * hs, CASC_CSV & Csv)
nLineCount = Csv.GetLineCount();
// Find the active config
- for (size_t i = 0; i < nLineCount; i++)
+ for(size_t i = 0; i < nLineCount; i++)
{
- // Is it the version we are looking for?
- if(!strcmp(Csv[i]["Name!STRING:0"].szValue, szWantedRegion))
+ // Retrieve the region
+ if((szRegion = Csv[i]["Name!STRING:0"].szValue) != NULL)
{
- // Save the list of CDN servers
- hs->szCdnServers = CascNewStrA2T(Csv[i]["Hosts!STRING:0"].szValue);
+ // Is it the version we are looking for?
+ if(!strcmp(Csv[i]["Name!STRING:0"].szValue, szWantedRegion))
+ {
+ // Save the list of CDN servers
+ hs->szCdnServers = CascNewStrA2T(Csv[i]["Hosts!STRING:0"].szValue);
- // Save the CDN subpath
- hs->szCdnPath = CascNewStrA2T(Csv[i]["Path!STRING:0"].szValue);
+ // Save the CDN subpath
+ hs->szCdnPath = CascNewStrA2T(Csv[i]["Path!STRING:0"].szValue);
- // Check and return result
- return (hs->szCdnServers && hs->szCdnPath) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
+ // Check and return result
+ return (hs->szCdnServers && hs->szCdnPath) ? ERROR_SUCCESS : ERROR_BAD_FORMAT;
+ }
}
}
@@ -657,12 +668,12 @@ static DWORD ParseFile_BuildInfo(TCascStorage * hs, CASC_CSV & Csv)
{
// Extract the CDN build key
dwErrCode = LoadQueryKey(Csv[nSelected]["Build Key!HEX:16"], hs->CdnBuildKey);
- if (dwErrCode != ERROR_SUCCESS)
+ if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Extract the CDN config key
dwErrCode = LoadQueryKey(Csv[nSelected]["CDN Key!HEX:16"], hs->CdnConfigKey);
- if (dwErrCode != ERROR_SUCCESS)
+ if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Get the CDN path
@@ -694,20 +705,20 @@ static DWORD ParseFile_VersionsDb(TCascStorage * hs, CASC_CSV & Csv)
for (size_t i = 0; i < nLineCount; i++)
{
// Either take the version required or take the first one
- if (hs->szRegion == NULL || !strcmp(Csv[i]["Region!STRING:0"].szValue, hs->szRegion))
+ if(hs->szRegion == NULL || !strcmp(Csv[i]["Region!STRING:0"].szValue, hs->szRegion))
{
// Extract the CDN build key
dwErrCode = LoadQueryKey(Csv[i]["BuildConfig!HEX:16"], hs->CdnBuildKey);
- if (dwErrCode != ERROR_SUCCESS)
+ if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Extract the CDN config key
dwErrCode = LoadQueryKey(Csv[i]["CDNConfig!HEX:16"], hs->CdnConfigKey);
- if (dwErrCode != ERROR_SUCCESS)
+ if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
const CASC_CSV_COLUMN & VerColumn = Csv[i]["VersionsName!String:0"];
- if (VerColumn.szValue && VerColumn.nLength)
+ if(VerColumn.szValue && VerColumn.nLength)
{
LoadBuildNumber(hs, NULL, VerColumn.szValue, VerColumn.szValue + VerColumn.nLength, NULL);
}
@@ -739,7 +750,7 @@ static DWORD ParseFile_BuildDb(TCascStorage * hs, CASC_CSV & Csv)
// Extract the CDN config key
dwErrCode = LoadQueryKey(Csv[CSV_ZERO][1], hs->CdnConfigKey);
- if (dwErrCode != ERROR_SUCCESS)
+ if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Verify all variables
@@ -798,7 +809,7 @@ static DWORD ParseFile_CdnBuild(TCascStorage * hs, void * pvListFile)
// Initialize the empty VFS array
dwErrCode = hs->VfsRootList.Create<CASC_CKEY_ENTRY>(0x10);
- if (dwErrCode != ERROR_SUCCESS)
+ if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
// Parse all variables
@@ -860,39 +871,45 @@ static DWORD ParseFile_CdnBuild(TCascStorage * hs, void * pvListFile)
static DWORD CheckDataDirectory(TCascStorage * hs, LPTSTR szDirectory)
{
- LPTSTR szDataPath;
+ TCHAR szDataPath[MAX_PATH];
DWORD dwErrCode = ERROR_FILE_NOT_FOUND;
// Try all known subdirectories
for(size_t i = 0; DataDirs[i] != NULL; i++)
{
// Create the eventual data path
- szDataPath = CombinePath(szDirectory, DataDirs[i]);
- if(szDataPath != NULL)
- {
- // Does that directory exist?
- if(DirectoryExists(szDataPath))
- {
- hs->szRootPath = CascNewStr(szDirectory);
- hs->szDataPath = szDataPath;
- return ERROR_SUCCESS;
- }
+ CombinePath(szDataPath, _countof(szDataPath), szDirectory, DataDirs[i], NULL);
- // Free the data path
- CASC_FREE(szDataPath);
+ // Does that directory exist?
+ if(DirectoryExists(szDataPath))
+ {
+ hs->szRootPath = CascNewStr(szDirectory);
+ hs->szDataPath = CascNewStr(szDataPath);
+ return ERROR_SUCCESS;
}
}
return dwErrCode;
}
+static DWORD LoadCsvFile(TCascStorage * hs, LPBYTE pbFileData, size_t cbFileData, PARSECSVFILE PfnParseProc, bool bHasHeader)
+{
+ CASC_CSV Csv(0x40, bHasHeader);
+ DWORD dwErrCode;
+
+ // Load the external file to memory
+ if((dwErrCode = Csv.Load(pbFileData, cbFileData)) == ERROR_SUCCESS)
+ dwErrCode = PfnParseProc(hs, Csv);
+ return dwErrCode;
+}
+
static DWORD LoadCsvFile(TCascStorage * hs, LPCTSTR szFileName, PARSECSVFILE PfnParseProc, bool bHasHeader)
{
CASC_CSV Csv(0x40, bHasHeader);
DWORD dwErrCode;
// Load the external file to memory
- if ((dwErrCode = Csv.Load(szFileName)) == ERROR_SUCCESS)
+ if((dwErrCode = Csv.Load(szFileName)) == ERROR_SUCCESS)
dwErrCode = PfnParseProc(hs, Csv);
return dwErrCode;
}
@@ -940,7 +957,7 @@ static void CreateRemoteAndLocalPath(TCascStorage * hs, CASC_CDN_DOWNLOAD & Cdns
{
// The file is given by EKey. It's either a loose file, or it's stored in an archive.
// We check that using the EKey map
- if ((pEKeyEntry = (PCASC_EKEY_ENTRY)hs->IndexMap.FindObject(CdnsInfo.pbEKey)) != NULL)
+ if((pEKeyEntry = (PCASC_EKEY_ENTRY)hs->IndexMap.FindObject(CdnsInfo.pbEKey)) != NULL)
{
// Change the path type to "data"
RemotePath.AppendString(_T("data"), true);
@@ -1073,15 +1090,15 @@ static DWORD DownloadFile(
// Open the remote stream
pRemStream = FileStream_OpenFile(szRemoteName, BASE_PROVIDER_HTTP | STREAM_PROVIDER_FLAT | dwPortFlags);
- if (pRemStream != NULL)
+ if(pRemStream != NULL)
{
// Will we download the entire file or just a part of it?
- if (PtrByteOffset == NULL)
+ if(PtrByteOffset == NULL)
{
ULONGLONG FileSize = 0;
// Retrieve the file size, but not longer than 1 GB
- if (FileStream_GetSize(pRemStream, &FileSize) && 0 < FileSize && FileSize < 0x40000000)
+ if(FileStream_GetSize(pRemStream, &FileSize) && 0 < FileSize && FileSize < 0x40000000)
{
// Cut the file size down to 32 bits
cbReadSize = (DWORD)FileSize;
@@ -1089,15 +1106,15 @@ static DWORD DownloadFile(
}
// Shall we read something?
- if ((cbReadSize != 0) && (pbFileData = CASC_ALLOC<BYTE>(cbReadSize)) != NULL)
+ if((cbReadSize != 0) && (pbFileData = CASC_ALLOC<BYTE>(cbReadSize)) != NULL)
{
// Read all required data from the remote file
- if (FileStream_Read(pRemStream, PtrByteOffset, pbFileData, cbReadSize))
+ if(FileStream_Read(pRemStream, PtrByteOffset, pbFileData, cbReadSize))
{
pLocStream = FileStream_CreateFile(szLocalName, BASE_PROVIDER_FILE | STREAM_PROVIDER_FLAT);
- if (pLocStream != NULL)
+ if(pLocStream != NULL)
{
- if (FileStream_Write(pLocStream, NULL, pbFileData, cbReadSize))
+ if(FileStream_Write(pLocStream, NULL, pbFileData, cbReadSize))
dwErrCode = ERROR_SUCCESS;
FileStream_Close(pLocStream);
@@ -1119,11 +1136,63 @@ static DWORD DownloadFile(
return dwErrCode;
}
+static DWORD RibbitDownloadFile(LPCTSTR szProduct, LPCTSTR szFileName, QUERY_KEY & FileData)
+{
+ TFileStream * pStream;
+ ULONGLONG FileSize = 0;
+ TCHAR szRemoteUrl[256];
+ DWORD dwErrCode = ERROR_CAN_NOT_COMPLETE;
+
+ // Construct the full URL (https://wowdev.wiki/Ribbit)
+ // Old (HTTP) download: wget http://us.patch.battle.net:1119/wow_classic/cdns
+ CascStrPrintf(szRemoteUrl, _countof(szRemoteUrl), _T("ribbit://%s.version.battle.net/v1/products/%s/%s"), bnet_region, szProduct, szFileName);
+
+ // Open the file stream
+ if((pStream = FileStream_OpenFile(szRemoteUrl, 0)) != NULL)
+ {
+ if(FileStream_GetSize(pStream, &FileSize) && FileSize <= 0x04000000)
+ {
+ // Fill-in the file pointer and size
+ FileData.pbData = CASC_ALLOC<BYTE>((size_t)FileSize);
+ if(FileData.pbData != NULL)
+ {
+ if(FileStream_Read(pStream, NULL, FileData.pbData, (DWORD)FileSize))
+ {
+ FileData.cbData = (size_t)FileSize;
+ dwErrCode = ERROR_SUCCESS;
+ }
+ else
+ {
+ dwErrCode = GetCascError();
+ CASC_FREE(FileData.pbData);
+ FileData.pbData = NULL;
+ }
+ }
+ else
+ {
+ dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+ else
+ {
+ dwErrCode = GetCascError();
+ }
+
+ // Close the remote stream
+ FileStream_Close(pStream);
+ }
+ else
+ {
+ dwErrCode = GetCascError();
+ }
+
+ return dwErrCode;
+}
+
static DWORD DownloadFileFromCDN2(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInfo)
{
CASC_PATH<TCHAR> RemotePath(URL_SEP_CHAR);
CASC_PATH<TCHAR> LocalPath(PATH_SEP_CHAR);
- DWORD dwPortFlags = (CdnsInfo.Flags & CASC_CDN_FLAG_PORT1119) ? STREAM_FLAG_USE_PORT_1119 : 0;
DWORD dwErrCode;
// Assemble both the remote and local path
@@ -1139,7 +1208,7 @@ static DWORD DownloadFileFromCDN2(TCascStorage * hs, CASC_CDN_DOWNLOAD & CdnsInf
return dwErrCode;
// Attempt to download the file
- dwErrCode = DownloadFile(RemotePath, LocalPath, NULL, 0, dwPortFlags);
+ dwErrCode = DownloadFile(RemotePath, LocalPath, NULL, 0, 0);
if(dwErrCode != ERROR_SUCCESS)
return dwErrCode;
}
@@ -1213,9 +1282,9 @@ static DWORD FetchAndLoadConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey, PARS
// Load and verify the external listfile
pvListFile = ListFile_OpenExternal(szLocalPath);
- if (pvListFile != NULL)
+ if(pvListFile != NULL)
{
- if (ListFile_VerifyMD5(pvListFile, pFileKey->pbData))
+ if(ListFile_VerifyMD5(pvListFile, pFileKey->pbData))
{
dwErrCode = PfnParseProc(hs, pvListFile);
}
@@ -1283,34 +1352,30 @@ DWORD GetFileSpanInfo(PCASC_CKEY_ENTRY pCKeyEntry, PULONGLONG PtrContentSize, PU
DWORD CheckGameDirectory(TCascStorage * hs, LPTSTR szDirectory)
{
TFileStream * pStream;
- LPTSTR szBuildFile;
+ TCHAR szBuildFile[MAX_PATH];
DWORD dwErrCode = ERROR_FILE_NOT_FOUND;
// Try to find any of the root files used in the history
for (size_t i = 0; BuildTypes[i].szFileName != NULL; i++)
{
// Create the full name of the .agent.db file
- szBuildFile = CombinePath(szDirectory, BuildTypes[i].szFileName);
- if (szBuildFile != NULL)
+ CombinePath(szBuildFile, _countof(szBuildFile), szDirectory, BuildTypes[i].szFileName, NULL);
+
+ // Attempt to open the file
+ pStream = FileStream_OpenFile(szBuildFile, STREAM_FLAG_READ_ONLY);
+ if(pStream != NULL)
{
- // Attempt to open the file
- pStream = FileStream_OpenFile(szBuildFile, STREAM_FLAG_READ_ONLY);
- if (pStream != NULL)
- {
- // Free the stream
- FileStream_Close(pStream);
+ // Free the stream
+ FileStream_Close(pStream);
- // Check for the data directory
- dwErrCode = CheckDataDirectory(hs, szDirectory);
- if (dwErrCode == ERROR_SUCCESS)
- {
- hs->szBuildFile = szBuildFile;
- hs->BuildFileType = BuildTypes[i].BuildFileType;
- return ERROR_SUCCESS;
- }
+ // Check for the data directory
+ dwErrCode = CheckDataDirectory(hs, szDirectory);
+ if(dwErrCode == ERROR_SUCCESS)
+ {
+ hs->szBuildFile = CascNewStr(szBuildFile);
+ hs->BuildFileType = BuildTypes[i].BuildFileType;
+ return ERROR_SUCCESS;
}
-
- CASC_FREE(szBuildFile);
}
}
@@ -1323,35 +1388,6 @@ DWORD LoadBuildInfo(TCascStorage * hs)
DWORD dwErrCode;
bool bHasHeader = true;
- // If the storage is online storage, we need to download "versions"
- if(hs->dwFeatures & CASC_FEATURE_ONLINE)
- {
- CASC_CDN_DOWNLOAD CdnsInfo = {0};
- TCHAR szLocalFile[MAX_PATH];
-
- // Inform the user about loading the build.info/build.db/versions
- if(InvokeProgressCallback(hs, "Downloading the \"versions\" file", NULL, 0, 0))
- return ERROR_CANCELLED;
-
- // Prepare the download structure for "us.patch.battle.net/%CODENAME%/versions" file
- CdnsInfo.szCdnsHost = _T("us.patch.battle.net");
- CdnsInfo.szCdnsPath = hs->szCodeName;
- CdnsInfo.szPathType = _T("");
- CdnsInfo.szFileName = _T("versions");
- CdnsInfo.szLocalPath = szLocalFile;
- CdnsInfo.ccLocalPath = _countof(szLocalFile);
- CdnsInfo.Flags = CASC_CDN_FLAG_PORT1119 | CASC_CDN_FORCE_DOWNLOAD;
-
- // Attempt to download the "versions" file
- dwErrCode = DownloadFileFromCDN(hs, CdnsInfo);
- if(dwErrCode != ERROR_SUCCESS)
- return dwErrCode;
-
- // Retrieve the name of the "versions" file
- if((hs->szBuildFile = CascNewStr(szLocalFile)) == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
- }
-
// We support ".build.info", ".build.db" or "versions"
switch (hs->BuildFileType)
{
@@ -1372,14 +1408,35 @@ DWORD LoadBuildInfo(TCascStorage * hs)
return ERROR_NOT_SUPPORTED;
}
- assert(hs->szBuildFile != NULL);
- return LoadCsvFile(hs, hs->szBuildFile, PfnParseProc, bHasHeader);
+ // If the storage is online storage, we need to download "versions"
+ if(hs->dwFeatures & CASC_FEATURE_ONLINE)
+ {
+ QUERY_KEY FileData;
+
+ // Inform the user about loading the build.info/build.db/versions
+ if(InvokeProgressCallback(hs, "Downloading the \"versions\" file", NULL, 0, 0))
+ return ERROR_CANCELLED;
+
+ // Download the file using Ribbit protocol
+ dwErrCode = RibbitDownloadFile(hs->szCodeName, _T("versions"), FileData);
+ if(dwErrCode == ERROR_SUCCESS)
+ {
+ // Parse the downloaded file
+ dwErrCode = LoadCsvFile(hs, FileData.pbData, FileData.cbData, ParseFile_VersionsDb, true);
+ }
+ }
+ else
+ {
+ assert(hs->szBuildFile != NULL);
+ dwErrCode = LoadCsvFile(hs, hs->szBuildFile, PfnParseProc, bHasHeader);
+ }
+
+ return dwErrCode;
}
DWORD LoadCdnsFile(TCascStorage * hs)
{
- CASC_CDN_DOWNLOAD CdnsInfo = {0};
- TCHAR szLocalPath[MAX_PATH];
+ QUERY_KEY FileData;
DWORD dwErrCode = ERROR_SUCCESS;
// Sanity checks
@@ -1389,19 +1446,12 @@ DWORD LoadCdnsFile(TCascStorage * hs)
if(InvokeProgressCallback(hs, "Downloading the \"cdns\" file", NULL, 0, 0))
return ERROR_CANCELLED;
- // Prepare the download structure
- CdnsInfo.szCdnsHost = _T("us.patch.battle.net");
- CdnsInfo.szCdnsPath = hs->szCodeName;
- CdnsInfo.szPathType = _T("");
- CdnsInfo.szFileName = _T("cdns");
- CdnsInfo.szLocalPath = szLocalPath;
- CdnsInfo.ccLocalPath = _countof(szLocalPath);
- CdnsInfo.Flags = CASC_CDN_FLAG_PORT1119 | CASC_CDN_FORCE_DOWNLOAD;
-
- // Download file and parse it
- if((dwErrCode = DownloadFileFromCDN(hs, CdnsInfo)) == ERROR_SUCCESS)
+ // Download the file using Ribbit protocol
+ dwErrCode = RibbitDownloadFile(hs->szCodeName, _T("cdns"), FileData);
+ if(dwErrCode == ERROR_SUCCESS)
{
- dwErrCode = LoadCsvFile(hs, szLocalPath, ParseFile_CDNS, true);
+ // Parse the downloaded file
+ dwErrCode = LoadCsvFile(hs, FileData.pbData, FileData.cbData, ParseFile_CDNS, true);
}
return dwErrCode;
@@ -1524,7 +1574,7 @@ LPBYTE LoadFileToMemory(LPCTSTR szFileName, DWORD * pcbFileData)
FileStream_GetSize(pStream, &FileSize);
cbFileData = (DWORD)FileSize;
- // Do not load zero files or too larget files
+ // Do not load zero files or too large files
if(0 < FileSize && FileSize <= 0x2000000)
{
// Allocate file data buffer. Make it 1 byte longer
@@ -1532,7 +1582,12 @@ LPBYTE LoadFileToMemory(LPCTSTR szFileName, DWORD * pcbFileData)
pbFileData = CASC_ALLOC<BYTE>(cbFileData + 1);
if(pbFileData != NULL)
{
- if(!FileStream_Read(pStream, NULL, pbFileData, cbFileData))
+ if(FileStream_Read(pStream, NULL, pbFileData, cbFileData))
+ {
+ // Terminate the data with zero so various string-based functions can process it
+ pbFileData[cbFileData] = 0;
+ }
+ else
{
CASC_FREE(pbFileData);
cbFileData = 0;
diff --git a/dep/CascLib/src/CascIndexFiles.cpp b/dep/CascLib/src/CascIndexFiles.cpp
index bc50f9f25bb..98416c2b360 100644
--- a/dep/CascLib/src/CascIndexFiles.cpp
+++ b/dep/CascLib/src/CascIndexFiles.cpp
@@ -118,6 +118,7 @@ static bool IndexDirectory_OnFileFound(
static LPTSTR CreateIndexFileName(TCascStorage * hs, DWORD IndexValue, DWORD IndexVersion)
{
+ TCHAR szFullName[MAX_PATH];
TCHAR szPlainName[0x40];
// Sanity checks
@@ -127,7 +128,10 @@ static LPTSTR CreateIndexFileName(TCascStorage * hs, DWORD IndexValue, DWORD Ind
// Create the full path
CascStrPrintf(szPlainName, _countof(szPlainName), hs->szIndexFormat, IndexValue, IndexVersion);
- return CombinePath(hs->szIndexPath, szPlainName);
+ CombinePath(szFullName, _countof(szFullName), hs->szIndexPath, szPlainName, NULL);
+
+ // Return allocated path
+ return CascNewStr(szFullName);
}
static void SaveFileOffsetBitsAndEKeyLength(TCascStorage * hs, BYTE FileOffsetBits, BYTE EKeyLength)
diff --git a/dep/CascLib/src/CascLib.h b/dep/CascLib/src/CascLib.h
index 75f8729e75f..815ca7e3376 100644
--- a/dep/CascLib/src/CascLib.h
+++ b/dep/CascLib/src/CascLib.h
@@ -74,8 +74,8 @@ extern "C" {
//-----------------------------------------------------------------------------
// Defines
-#define CASCLIB_VERSION 0x0200 // CascLib version - integral (1.50)
-#define CASCLIB_VERSION_STRING "2.0" // CascLib version - string
+#define CASCLIB_VERSION 0x0210 // CascLib version - integral (2.1)
+#define CASCLIB_VERSION_STRING "2.1" // CascLib version - string
// Values for CascOpenFile
#define CASC_OPEN_BY_NAME 0x00000000 // Open the file by name. This is the default value
diff --git a/dep/CascLib/src/CascOpenStorage.cpp b/dep/CascLib/src/CascOpenStorage.cpp
index 00b3797eb68..584b79a38a1 100644
--- a/dep/CascLib/src/CascOpenStorage.cpp
+++ b/dep/CascLib/src/CascOpenStorage.cpp
@@ -135,6 +135,11 @@ TCascStorage * TCascStorage::Release()
// Need this to be atomic to make multi-threaded file opens work
if(CascInterlockedDecrement(&dwRefCount) == 0)
{
+ // Release all references in the socket cache
+ if(dwFeatures & CASC_FEATURE_ONLINE)
+ sockets_set_caching(false);
+
+ // Delete the object and return NULL
delete this;
return NULL;
}
@@ -162,16 +167,16 @@ void * ProbeOutputBuffer(void * pvBuffer, size_t cbLength, size_t cbMinLength, s
static LPTSTR CheckForIndexDirectory(TCascStorage * hs, LPCTSTR szSubDir)
{
- LPTSTR szIndexPath;
+ TCHAR szIndexPath[MAX_PATH];
// Combine the index path
- szIndexPath = CombinePath(hs->szDataPath, szSubDir);
- if (!DirectoryExists(szIndexPath))
- {
- CASC_FREE(szIndexPath);
- }
+ CombinePath(szIndexPath, _countof(szIndexPath), hs->szDataPath, szSubDir, NULL);
- return szIndexPath;
+ // Check whether the path exists
+ if(!DirectoryExists(szIndexPath))
+ return NULL;
+
+ return CascNewStr(szIndexPath);
}
// Inserts an entry from the text build file
@@ -1168,9 +1173,14 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
if(ExtractVersionedArgument(pArgs, FIELD_OFFSET(CASC_OPEN_STORAGE_ARGS, szBuildKey), &szBuildKey) && szBuildKey != NULL)
hs->szBuildKey = CascNewStrT2A(szBuildKey);
- // For online storages, we need to load CDN servers
- if ((dwErrCode == ERROR_SUCCESS) && (hs->dwFeatures & CASC_FEATURE_ONLINE))
+ // Special handling to online storages
+ if(hs->dwFeatures & CASC_FEATURE_ONLINE)
{
+ // Enable caching of the sockets. This will add references
+ // to all existing and all future sockets
+ sockets_set_caching(true);
+
+ // For online storages, we need to load CDN servers
dwErrCode = LoadCdnsFile(hs);
}
diff --git a/dep/CascLib/src/CascPort.h b/dep/CascLib/src/CascPort.h
index 030d12719f4..da11fccd677 100644
--- a/dep/CascLib/src/CascPort.h
+++ b/dep/CascLib/src/CascPort.h
@@ -13,8 +13,8 @@
#define __CASCPORT_H__
#ifndef __cplusplus
- #define bool char
- #define true 1
+ #define bool char
+ #define true 1
#define false 0
#endif
@@ -43,9 +43,9 @@
#include <direct.h>
#include <malloc.h>
#include <windows.h>
- #include <wininet.h>
+ #include <ws2tcpip.h>
#include <strsafe.h>
- #include <sys/types.h>
+
#define PLATFORM_LITTLE_ENDIAN
#pragma intrinsic(memset, memcmp, memcpy) // Make these functions intrinsic (inline)
@@ -56,6 +56,7 @@
#define PLATFORM_WINDOWS
#define PLATFORM_DEFINED // The platform is known now
+
#endif
#ifndef FIELD_OFFSET
@@ -70,6 +71,7 @@
// Macintosh
#include <sys/types.h>
#include <sys/stat.h>
+ #include <sys/socket.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <dirent.h>
@@ -85,6 +87,7 @@
#include <cassert>
#include <errno.h>
#include <pthread.h>
+ #include <netdb.h>
// Support for PowerPC on Max OS X
#if (__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1)
@@ -104,6 +107,8 @@
#define PATH_SEP_CHAR '/'
#define PATH_SEP_STRING "/"
+ typedef int SOCKET;
+
#define PLATFORM_MAC
#define PLATFORM_DEFINED // The platform is known now
@@ -115,6 +120,7 @@
#if !defined(PLATFORM_DEFINED)
#include <sys/types.h>
#include <sys/stat.h>
+ #include <sys/socket.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <dirent.h>
@@ -130,11 +136,14 @@
#include <assert.h>
#include <errno.h>
#include <pthread.h>
+ #include <netdb.h>
#define URL_SEP_CHAR '/'
#define PATH_SEP_CHAR '/'
#define PATH_SEP_STRING "/"
+ typedef int SOCKET;
+
#define PLATFORM_LITTLE_ENDIAN
#define PLATFORM_LINUX
#define PLATFORM_DEFINED
@@ -202,6 +211,8 @@
#define _tcsicmp strcasecmp
#define _tcsnicmp strncasecmp
+ #define closesocket close
+
#endif // !PLATFORM_WINDOWS
// 64-bit calls are supplied by "normal" calls on Mac
@@ -235,6 +246,7 @@
#define ERROR_FILE_ENCRYPTED 1005 // Returned by encrypted stream when can't find file key
#define ERROR_FILE_TOO_LARGE 1006 // No such error code under Linux
#define ERROR_ARITHMETIC_OVERFLOW 1007 // The string value is too large to fit in the given type
+ #define ERROR_NETWORK_NOT_AVAILABLE 1008 // Cannot connect to the internet
#endif
#ifndef ERROR_FILE_INCOMPLETE
diff --git a/dep/CascLib/src/CascReadFile.cpp b/dep/CascLib/src/CascReadFile.cpp
index 6078c32fb7f..e71387b0466 100644
--- a/dep/CascLib/src/CascReadFile.cpp
+++ b/dep/CascLib/src/CascReadFile.cpp
@@ -48,7 +48,7 @@ static DWORD OpenDataStream(TCascFile * hf, PCASC_FILE_SPAN pFileSpan, PCASC_CKE
{
// Prepare the name of the data file
CascStrPrintf(szPlainName, _countof(szPlainName), _T("data.%03u"), dwArchiveIndex);
- CombinePath(szDataFile, _countof(szDataFile), PATH_SEP_CHAR, hs->szIndexPath, szPlainName, NULL);
+ CombinePath(szDataFile, _countof(szDataFile), hs->szIndexPath, szPlainName, NULL);
// Open the data stream with read+write sharing to prevent Battle.net agent
// detecting a corruption and redownloading the entire package
diff --git a/dep/CascLib/src/CascRootFile_WoW.cpp b/dep/CascLib/src/CascRootFile_WoW.cpp
index dd085b872e7..e5d0d0426ed 100644
--- a/dep/CascLib/src/CascRootFile_WoW.cpp
+++ b/dep/CascLib/src/CascRootFile_WoW.cpp
@@ -163,7 +163,7 @@ struct TRootHandler_WoW : public TFileTreeRoot
pbRootPtr = pbRootPtr + (sizeof(CONTENT_KEY) * RootGroup.Header.NumberOfFiles);
// Also include array of file hashes
- if(FileCounter > FileCounterHashless)
+ if(!(RootGroup.Header.ContentFlags & CASC_CFLAG_NO_NAME_HASH))
{
if((pbRootPtr + (sizeof(ULONGLONG) * RootGroup.Header.NumberOfFiles)) > pbRootEnd)
return NULL;
diff --git a/dep/CascLib/src/DllMain.rc b/dep/CascLib/src/DllMain.rc
index 4241c2687de..befa7c43508 100644
--- a/dep/CascLib/src/DllMain.rc
+++ b/dep/CascLib/src/DllMain.rc
@@ -27,8 +27,8 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,50,0,204
- PRODUCTVERSION 1,50,0,204
+ FILEVERSION 1,50,0,205
+ PRODUCTVERSION 1,50,0,205
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
@@ -45,12 +45,12 @@ BEGIN
BEGIN
VALUE "Comments", "http://www.zezula.net/casc.html"
VALUE "FileDescription", "CascLib library for reading Blizzard CASC storages"
- VALUE "FileVersion", "1, 50, 0, 204\0"
+ VALUE "FileVersion", "1, 50, 0, 205\0"
VALUE "InternalName", "CascLib"
VALUE "LegalCopyright", "Copyright (c) 2014 - 2019 Ladislav Zezula"
VALUE "OriginalFilename", "CascLib.dll"
VALUE "ProductName", "CascLib"
- VALUE "ProductVersion", "1, 50, 0, 204\0"
+ VALUE "ProductVersion", "1, 50, 0, 205\0"
END
END
BLOCK "VarFileInfo"
diff --git a/dep/CascLib/src/common/Common.cpp b/dep/CascLib/src/common/Common.cpp
index 7bec2b14a6a..953a4f0b129 100644
--- a/dep/CascLib/src/common/Common.cpp
+++ b/dep/CascLib/src/common/Common.cpp
@@ -454,9 +454,9 @@ bool CutLastPathPart(LPTSTR szWorkPath)
return true;
}
-size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, char chSeparator, va_list argList)
+size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, va_list argList)
{
- CASC_PATH<TCHAR> Path(chSeparator);
+ CASC_PATH<TCHAR> Path(PATH_SEP_CHAR);
LPCTSTR szFragment;
bool bWithSeparator = false;
@@ -470,28 +470,18 @@ size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, char chSeparator, va_list
return Path.Copy(szBuffer, nMaxChars);
}
-size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, char chSeparator, ...)
+size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, ...)
{
va_list argList;
size_t nLength;
- va_start(argList, chSeparator);
- nLength = CombinePath(szBuffer, nMaxChars, chSeparator, argList);
+ va_start(argList, nMaxChars);
+ nLength = CombinePath(szBuffer, nMaxChars, argList);
va_end(argList);
return nLength;
}
-LPTSTR CombinePath(LPCTSTR szDirectory, LPCTSTR szSubDir)
-{
- CASC_PATH<TCHAR> Path(PATH_SEP_CHAR);
-
- // Merge the path
- Path.AppendString(szDirectory, false);
- Path.AppendString(szSubDir, true);
- return Path.New();
-}
-
size_t NormalizeFileName(const unsigned char * NormTable, char * szNormName, const char * szFileName, size_t cchMaxChars)
{
char * szNormNameEnd = szNormName + cchMaxChars;
diff --git a/dep/CascLib/src/common/Common.h b/dep/CascLib/src/common/Common.h
index 46b23adb6ae..63996e5503c 100644
--- a/dep/CascLib/src/common/Common.h
+++ b/dep/CascLib/src/common/Common.h
@@ -328,9 +328,8 @@ wchar_t * CascNewStr(const wchar_t * szString, size_t nCharsToReserve = 0);
LPSTR CascNewStrT2A(LPCTSTR szString, size_t nCharsToReserve = 0);
LPTSTR CascNewStrA2T(LPCSTR szString, size_t nCharsToReserve = 0);
-size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, char chSeparator, va_list argList);
-size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, char chSeparator, ...);
-LPTSTR CombinePath(LPCTSTR szPath, LPCTSTR szSubDir);
+size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, va_list argList);
+size_t CombinePath(LPTSTR szBuffer, size_t nMaxChars, ...);
LPTSTR GetLastPathPart(LPTSTR szWorkPath);
bool CutLastPathPart(LPTSTR szWorkPath);
@@ -438,7 +437,7 @@ xchar * StringFromBinary(LPBYTE pbBinary, size_t cbBinary, xchar * szBuffer)
}
//-----------------------------------------------------------------------------
-// Structure query key
+// Structures for data blobs
struct QUERY_KEY
{
@@ -454,6 +453,16 @@ struct QUERY_KEY
cbData = 0;
}
+ DWORD SetData(const void * pv, size_t cb)
+ {
+ if((pbData = CASC_ALLOC<BYTE>(cb)) == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ memcpy(pbData, pv, cb);
+ cbData = cb;
+ return ERROR_SUCCESS;
+ }
+
LPBYTE pbData;
size_t cbData;
};
diff --git a/dep/CascLib/src/common/Csv.cpp b/dep/CascLib/src/common/Csv.cpp
index 40a50ec723b..4afaae10a0a 100644
--- a/dep/CascLib/src/common/Csv.cpp
+++ b/dep/CascLib/src/common/Csv.cpp
@@ -21,7 +21,7 @@ static const CASC_CSV_LINE NullLine;
//-----------------------------------------------------------------------------
// Local functions
-static char * NextLine(char * szLine)
+static char * NextLine_Default(void * /* pvUserData */, char * szLine)
{
// Find the end of the line
while(szLine[0] != 0 && szLine[0] != 0x0A && szLine[0] != 0x0D)
@@ -35,7 +35,7 @@ static char * NextLine(char * szLine)
return (szLine[0] != 0) ? szLine : NULL;
}
-static char * NextColumn(char * szColumn)
+static char * NextColumn_Default(void * /* pvUserData */, char * szColumn)
{
// Find the end of the column
while(szColumn[0] != 0 && szColumn[0] != '|')
@@ -86,6 +86,7 @@ CASC_CSV_LINE::~CASC_CSV_LINE()
bool CASC_CSV_LINE::SetLine(CASC_CSV * pParent, char * szCsvLine)
{
+ CASC_CSV_NEXTPROC PfnNextColumn = pParent->GetNextColumnProc();
char * szCsvColumn;
size_t nHdrColumns = 0;
size_t nColumns = 0;
@@ -99,7 +100,7 @@ bool CASC_CSV_LINE::SetLine(CASC_CSV * pParent, char * szCsvLine)
{
// Get current line and the next one
szCsvColumn = szCsvLine;
- szCsvLine = NextColumn(szCsvLine);
+ szCsvLine = PfnNextColumn(pParent->GetUserData(), szCsvLine);
// Save the current line
Columns[nColumns].szValue = szCsvColumn;
@@ -154,12 +155,17 @@ CASC_CSV::CASC_CSV(size_t nLinesMax, bool bHasHeader)
{
// Initialize the class variables
memset(HashTable, 0xFF, sizeof(HashTable));
+ m_pvUserData = NULL;
m_szCsvFile = NULL;
m_szCsvPtr = NULL;
m_nCsvFile = 0;
m_nLines = 0;
m_bHasHeader = bHasHeader;
+ // Initialize the "NextLine" function that will serve for finding next line in the text
+ PfnNextLine = NextLine_Default;
+ PfnNextColumn = NextColumn_Default;
+
// Nonzero number of lines means a finite CSV handler. The CSV will be loaded at once.
// Zero means that the user needs to call LoadNextLine() and then the line data
if(nLinesMax != 0)
@@ -185,6 +191,21 @@ CASC_CSV::~CASC_CSV()
m_szCsvFile = NULL;
}
+DWORD CASC_CSV::SetNextLineProc(CASC_CSV_NEXTPROC PfnNextLineProc, CASC_CSV_NEXTPROC PfnNextColProc, void * pvUserData)
+{
+ // Set the procedure for next line parsing
+ if(PfnNextLineProc != NULL)
+ PfnNextLine = PfnNextLineProc;
+
+ // Set procedure for next columne parsing
+ if(PfnNextColProc != NULL)
+ PfnNextColumn = PfnNextColProc;
+
+ // Save the user data
+ m_pvUserData = pvUserData;
+ return ERROR_SUCCESS;
+}
+
DWORD CASC_CSV::Load(LPCTSTR szFileName)
{
DWORD cbFileData = 0;
@@ -193,8 +214,7 @@ DWORD CASC_CSV::Load(LPCTSTR szFileName)
m_szCsvFile = (char *)LoadFileToMemory(szFileName, &cbFileData);
if (m_szCsvFile != NULL)
{
- // There is one extra byte reserved by LoadFileToMemory, so we can put zero there
- m_szCsvFile[cbFileData] = 0;
+ // Assign the data to the CSV object
m_szCsvPtr = m_szCsvFile;
m_nCsvFile = cbFileData;
@@ -282,7 +302,7 @@ bool CASC_CSV::LoadNextLine(CASC_CSV_LINE & Line)
char * szCsvLine = m_szCsvPtr;
// Get the next line
- m_szCsvPtr = NextLine(m_szCsvPtr);
+ m_szCsvPtr = PfnNextLine(m_pvUserData, m_szCsvPtr);
// Initialize the line
if (Line.SetLine(this, szCsvLine))
diff --git a/dep/CascLib/src/common/Csv.h b/dep/CascLib/src/common/Csv.h
index 2fcee847703..8df99c63456 100644
--- a/dep/CascLib/src/common/Csv.h
+++ b/dep/CascLib/src/common/Csv.h
@@ -20,6 +20,13 @@
#define CSV_HASH_TABLE_SIZE 0x80
//-----------------------------------------------------------------------------
+// Interface for finding of next text element (line, column)
+
+// The function must find the next (line|column), put zero there and return begin
+// of the next (line|column). In case there is no next (line|column), the function returns NULL
+typedef char * (*CASC_CSV_NEXTPROC)(void * pvUserData, char * szLine);
+
+//-----------------------------------------------------------------------------
// Class for CSV line
class CASC_CSV;
@@ -69,6 +76,12 @@ class CASC_CSV
CASC_CSV(size_t nLinesMax, bool bHasHeader);
~CASC_CSV();
+ DWORD SetNextLineProc(CASC_CSV_NEXTPROC PfnNextLineProc, CASC_CSV_NEXTPROC PfnNextColProc = NULL, void * pvUserData = NULL);
+ CASC_CSV_NEXTPROC GetNextColumnProc()
+ {
+ return PfnNextColumn;
+ }
+
DWORD Load(LPBYTE pbData, size_t cbData);
DWORD Load(LPCTSTR szFileName);
bool LoadNextLine();
@@ -79,6 +92,11 @@ class CASC_CSV
size_t GetHeaderColumns() const;
size_t GetColumnIndex(const char * szColumnName) const;
+ void * GetUserData() const
+ {
+ return m_pvUserData;
+ }
+
size_t GetLineCount() const
{
return m_nLines;
@@ -90,9 +108,13 @@ class CASC_CSV
bool LoadNextLine(CASC_CSV_LINE & Line);
bool ParseCsvData();
+ CASC_CSV_NEXTPROC PfnNextLine;
+ CASC_CSV_NEXTPROC PfnNextColumn;
+
CASC_CSV_LINE * m_pLines;
CASC_CSV_LINE Header;
BYTE HashTable[CSV_HASH_TABLE_SIZE];
+ void * m_pvUserData;
char * m_szCsvFile;
char * m_szCsvPtr;
size_t m_nCsvFile;
diff --git a/dep/CascLib/src/common/Directory.cpp b/dep/CascLib/src/common/Directory.cpp
index 57c7d552733..a306e54a91e 100644
--- a/dep/CascLib/src/common/Directory.cpp
+++ b/dep/CascLib/src/common/Directory.cpp
@@ -60,13 +60,11 @@ int ScanIndexDirectory(
#ifdef PLATFORM_WINDOWS
WIN32_FIND_DATA wf;
- LPTSTR szSearchMask;
HANDLE hFind;
+ TCHAR szSearchMask[MAX_PATH];
// Prepare the search mask
- szSearchMask = CombinePath(szIndexPath, _T("*"));
- if(szSearchMask == NULL)
- return ERROR_NOT_ENOUGH_MEMORY;
+ CombinePath(szSearchMask, _countof(szSearchMask), szIndexPath, _T("*"), NULL);
// Prepare directory search
hFind = FindFirstFile(szSearchMask, &wf);
@@ -87,8 +85,6 @@ int ScanIndexDirectory(
FindClose(hFind);
}
- CASC_FREE(szSearchMask);
-
#else // PLATFORM_WINDOWS
struct dirent * dir_entry;
diff --git a/dep/CascLib/src/common/FileStream.cpp b/dep/CascLib/src/common/FileStream.cpp
index cb0090f8dfb..e84c7cc3c4b 100644
--- a/dep/CascLib/src/common/FileStream.cpp
+++ b/dep/CascLib/src/common/FileStream.cpp
@@ -18,13 +18,17 @@
#include "../CascCommon.h"
#ifdef _MSC_VER
-#pragma comment(lib, "wininet.lib") // Internet functions for HTTP stream
#pragma warning(disable: 4800) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning)
#endif
//-----------------------------------------------------------------------------
// Local functions - platform-specific functions
+static ULONGLONG GetByteOffset(ULONGLONG * ByteOffset1, ULONGLONG ByteOffset2)
+{
+ return (ByteOffset1 != NULL) ? ByteOffset1[0] : ByteOffset2;
+}
+
static DWORD StringToInt(const char * szString)
{
DWORD dwValue = 0;
@@ -165,7 +169,7 @@ static bool BaseFile_Read(
// Synchronize the access to the TFileStream structure
CascLock(pStream->Lock);
{
- ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos;
+ ULONGLONG ByteOffset = GetByteOffset(pByteOffset, pStream->Base.File.FilePos);
#ifdef PLATFORM_WINDOWS
{
@@ -265,7 +269,7 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const
// Synchronize the access to the TFileStream structure
CascLock(pStream->Lock);
{
- ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos;
+ ULONGLONG ByteOffset = GetByteOffset(pByteOffset, pStream->Base.File.FilePos);
#ifdef PLATFORM_WINDOWS
{
@@ -557,7 +561,7 @@ static bool BaseMap_Read(
void * pvBuffer, // Pointer to data to be read
DWORD dwBytesToRead) // Number of bytes to read from the file
{
- ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Map.FilePos;
+ ULONGLONG ByteOffset = GetByteOffset(pByteOffset, pStream->Base.Map.FilePos);
// Do we have to read anything at all?
if(dwBytesToRead != 0)
@@ -607,150 +611,100 @@ static void BaseMap_Init(TFileStream * pStream)
//-----------------------------------------------------------------------------
// Local functions - base HTTP file support
-static LPCTSTR BaseHttp_ExtractServerName(LPCTSTR szFileName, LPTSTR szServerName)
+static DWORD BaseHttp_ParseURL(TFileStream * pStream, LPCTSTR szFileName)
{
- // Check for HTTP
- if(!_tcsnicmp(szFileName, _T("http://"), 7))
- szFileName += 7;
+ LPCTSTR szFilePtr = szFileName;
+ char * hostName;
+ char * fileName;
- // Cut off the server name
- if(szServerName != NULL)
- {
- while(szFileName[0] != 0 && szFileName[0] != _T('/'))
- *szServerName++ = *szFileName++;
- *szServerName = 0;
- }
- else
+ // Find the end od the host name
+ if((szFilePtr = _tcschr(szFileName, '/')) == NULL)
+ return ERROR_INVALID_PARAMETER;
+
+ // Allocate and copy the host name
+ if((hostName = CASC_ALLOC<char>(szFilePtr - szFileName + 1)) != NULL)
{
- while(szFileName[0] != 0 && szFileName[0] != _T('/'))
- szFileName++;
+ CascStrCopy(hostName, 256, szFileName, (szFilePtr - szFileName));
+
+ // Allocate and copy the resource name
+ if((fileName = CascNewStrT2A(szFilePtr)) != NULL)
+ {
+ pStream->Base.Socket.hostName = hostName;
+ pStream->Base.Socket.fileName = fileName;
+ return ERROR_SUCCESS;
+ }
+
+ CASC_FREE(hostName);
}
- // Return the remainder
- return szFileName;
+ return ERROR_NOT_ENOUGH_MEMORY;
}
-static bool BaseHttp_Open(TFileStream * pStream, LPCTSTR szFileName, DWORD dwStreamFlags)
+static bool BaseHttp_Download(TFileStream * pStream)
{
-#ifdef PLATFORM_WINDOWS
+ CASC_MIME Mime;
+ const char * request_mask = "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: Keep-Alive\r\n\r\n";
+ char * server_response;
+ char * fileName = pStream->Base.Socket.fileName;
+ char request[0x100];
+ size_t response_length = 0;
+ size_t request_length = 0;
+ DWORD dwErrCode;
- INTERNET_PORT ServerPort = INTERNET_DEFAULT_HTTP_PORT;
- HINTERNET hRequest;
- DWORD dwTemp = 0;
- bool bFileAvailable = false;
- DWORD dwErrCode = ERROR_SUCCESS;
-
- // Check alternate ports
- if(dwStreamFlags & STREAM_FLAG_USE_PORT_1119)
+ // If we already have the data, it's success
+ if(pStream->Base.Socket.fileData == NULL)
{
- ServerPort = 1119;
- }
+ // Construct the request, either HTTP or Ribbit (https://wowdev.wiki/Ribbit).
+ // Note that Ribbit requests don't start with slash
+ if((pStream->dwFlags & BASE_PROVIDER_MASK) == BASE_PROVIDER_RIBBIT)
+ {
+ if(fileName[0] == '/')
+ fileName++;
+ request_mask = "%s\r\n";
+ }
- // Don't download if we are not connected to the internet
- if(!InternetGetConnectedState(&dwTemp, 0))
- dwErrCode = GetCascError();
+ // Send the request and receive decoded response
+ request_length = CascStrPrintf(request, _countof(request), request_mask, fileName, pStream->Base.Socket.hostName);
+ server_response = pStream->Base.Socket.pSocket->ReadResponse(request, request_length, &response_length);
+ if(server_response != NULL)
+ {
+ // Decode the MIME document
+ if((dwErrCode = Mime.Load(server_response, response_length)) == ERROR_SUCCESS)
+ {
+ // Move the data from MIME to HTTP stream
+ pStream->Base.Socket.fileData = Mime.GiveAway(&pStream->Base.Socket.fileDataLength);
+ }
- // Initiate the connection to the internet
- if(dwErrCode == ERROR_SUCCESS)
- {
- pStream->Base.Http.hInternet = InternetOpen(_T("agent/2.17.2.6700"),
- INTERNET_OPEN_TYPE_PRECONFIG,
- NULL,
- NULL,
- 0);
- if(pStream->Base.Http.hInternet == NULL)
- dwErrCode = GetCascError();
+ CASC_FREE(server_response);
+ }
}
- // Connect to the server
- if(dwErrCode == ERROR_SUCCESS)
- {
- TCHAR szServerName[MAX_PATH];
- DWORD dwFlags = INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_UI | INTERNET_FLAG_NO_CACHE_WRITE;
+ // If we have data loaded, return true
+ return (pStream->Base.Socket.fileData != NULL);
+}
- // Initiate connection with the server
- szFileName = BaseHttp_ExtractServerName(szFileName, szServerName);
- pStream->Base.Http.hConnect = InternetConnect(pStream->Base.Http.hInternet,
- szServerName,
- ServerPort,
- NULL,
- NULL,
- INTERNET_SERVICE_HTTP,
- dwFlags,
- 0);
- if(pStream->Base.Http.hConnect == NULL)
- dwErrCode = GetCascError();
- }
+static bool BaseHttp_Open(TFileStream * pStream, LPCTSTR szFileName, DWORD dwStreamFlags)
+{
+ DWORD dwErrCode;
- // Now try to query the file size
- if(dwErrCode == ERROR_SUCCESS)
+ // Extract the server part
+ if((dwErrCode = BaseHttp_ParseURL(pStream, szFileName)) == ERROR_SUCCESS)
{
- // Open HTTP request to the file
- hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0);
- if(hRequest != NULL)
+ // Determine the proper port
+ PCASC_SOCKET pSocket;
+ int portNum = ((dwStreamFlags & BASE_PROVIDER_MASK) == BASE_PROVIDER_RIBBIT) ? CASC_PORT_RIBBIT : CASC_PORT_HTTP;
+
+ // Initiate the remote connection
+ if((pSocket = sockets_connect(pStream->Base.Socket.hostName, portNum)) != NULL)
{
- if(HttpSendRequest(hRequest, NULL, 0, NULL, 0))
- {
- ULONGLONG FileTime = 0;
- LPTSTR szEndPtr;
- TCHAR szStatusCode[0x10];
- DWORD dwStatusCode = 404;
- DWORD dwFileSize = 0;
- DWORD dwDataSize = sizeof(szStatusCode);
- DWORD dwIndex = 0;
-
- // Check whether the file exists
- if (HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, szStatusCode, &dwDataSize, &dwIndex))
- dwStatusCode = _tcstoul(szStatusCode, &szEndPtr, 10);
-
- // Compare the status code
- if (dwStatusCode == 200)
- {
- // Check if the archive has Last Modified field
- dwDataSize = sizeof(ULONGLONG);
- if (HttpQueryInfo(hRequest, HTTP_QUERY_LAST_MODIFIED | HTTP_QUERY_FLAG_SYSTEMTIME, &FileTime, &dwDataSize, &dwIndex))
- pStream->Base.Http.FileTime = FileTime;
-
- // Verify if the server supports random access
- dwDataSize = sizeof(DWORD);
- if (HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &dwFileSize, &dwDataSize, &dwIndex))
- {
- if (dwFileSize != 0)
- {
- pStream->Base.Http.FileSize = dwFileSize;
- pStream->Base.Http.FilePos = 0;
- bFileAvailable = true;
- }
- }
- }
- else
- {
- dwErrCode = ERROR_FILE_NOT_FOUND;
- }
- }
- InternetCloseHandle(hRequest);
+ pStream->Base.Socket.pSocket = pSocket;
+ return true;
}
}
- // If the file is not there and is not available for random access,
- // report error
- if(bFileAvailable == false)
- {
- pStream->BaseClose(pStream);
- SetCascError(dwErrCode);
- return false;
- }
-
- return true;
-
-#else
-
- // Not supported
- SetCascError(ERROR_NOT_SUPPORTED);
- pStream = pStream;
+ // Failure: set the last error and return false
+ SetCascError(dwErrCode);
return false;
-
-#endif
}
static bool BaseHttp_Read(
@@ -759,90 +713,94 @@ static bool BaseHttp_Read(
void * pvBuffer, // Pointer to data to be read
DWORD dwBytesToRead) // Number of bytes to read from the file
{
-#ifdef PLATFORM_WINDOWS
- ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Http.FilePos;
- DWORD dwTotalBytesRead = 0;
+ ULONGLONG ByteOffset = GetByteOffset(pByteOffset, pStream->Base.Socket.fileDataPos);
+ bool bCanReadTheWholeRange = true;
- // Do we have to read anything at all?
- if(dwBytesToRead != 0)
+ // Synchronize the access to the TFileStream structure
+ CascLock(pStream->Lock);
{
- HINTERNET hRequest;
- LPCTSTR szFileName;
- LPBYTE pbBuffer = (LPBYTE)pvBuffer;
- TCHAR szRangeRequest[0x80];
- DWORD dwStartOffset = (DWORD)ByteOffset;
- DWORD dwEndOffset = dwStartOffset + dwBytesToRead;
-
- // Open HTTP request to the file
- szFileName = BaseHttp_ExtractServerName(pStream->szFileName, NULL);
- hRequest = HttpOpenRequest(pStream->Base.Http.hConnect, _T("GET"), szFileName, NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, 0);
- if(hRequest != NULL)
+ // Do we have to read anything at all?
+ if(dwBytesToRead != 0)
{
- // Add range request to the HTTP headers
- // http://www.clevercomponents.com/articles/article015/resuming.asp
- CascStrPrintf(szRangeRequest, _countof(szRangeRequest), _T("Range: bytes=%u-%u"), (unsigned int)dwStartOffset, (unsigned int)dwEndOffset);
- HttpAddRequestHeaders(hRequest, szRangeRequest, 0xFFFFFFFF, HTTP_ADDREQ_FLAG_ADD_IF_NEW);
+ // Make sure that we have the file downloaded
+ if(!BaseHttp_Download(pStream))
+ {
+ CascUnlock(pStream->Lock);
+ return false;
+ }
- // Send the request to the server
- if(HttpSendRequest(hRequest, NULL, 0, NULL, 0))
+ // Are we trying to read more than available?
+ if(ByteOffset <= pStream->Base.Socket.fileDataLength)
{
- while(dwTotalBytesRead < dwBytesToRead)
+ if((ByteOffset + dwBytesToRead) > pStream->Base.Socket.fileDataLength)
{
- DWORD dwBlockBytesToRead = dwBytesToRead - dwTotalBytesRead;
- DWORD dwBlockBytesRead = 0;
-
- // Read the block from the file
- if(dwBlockBytesToRead > 0x200)
- dwBlockBytesToRead = 0x200;
- InternetReadFile(hRequest, pbBuffer, dwBlockBytesToRead, &dwBlockBytesRead);
-
- // Check for end
- if(dwBlockBytesRead == 0)
- break;
-
- // Move buffers
- dwTotalBytesRead += dwBlockBytesRead;
- pbBuffer += dwBlockBytesRead;
+ bCanReadTheWholeRange = false;
+ dwBytesToRead = (DWORD)(pStream->Base.Socket.fileDataLength - ByteOffset);
}
}
- InternetCloseHandle(hRequest);
+ else
+ {
+ bCanReadTheWholeRange = false;
+ dwBytesToRead = 0;
+ }
+
+ // Copy the data
+ if(dwBytesToRead != 0)
+ {
+ memcpy(pvBuffer, pStream->Base.Socket.fileData + ByteOffset, dwBytesToRead);
+ }
}
- }
- // Increment the current file position by number of bytes read
- pStream->Base.Http.FilePos = ByteOffset + dwTotalBytesRead;
+ // Increment the current file position by number of bytes read
+ pStream->Base.Socket.fileDataPos = (size_t)(ByteOffset + dwBytesToRead);
+ }
+ CascUnlock(pStream->Lock);
// If the number of bytes read doesn't match the required amount, return false
- if(dwTotalBytesRead != dwBytesToRead)
+ if(bCanReadTheWholeRange == false)
SetCascError(ERROR_HANDLE_EOF);
- return (dwTotalBytesRead == dwBytesToRead);
+ return bCanReadTheWholeRange;
+}
+
+// Gives the current file size
+static bool BaseHttp_GetSize(TFileStream * pStream, ULONGLONG * pFileSize)
+{
+ bool bResult;
-#else
+ // Synchronize the access to the TFileStream structure
+ CascLock(pStream->Lock);
+ {
+ // Make sure that we have the file data
+ bResult = BaseHttp_Download(pStream);
+ if(bResult)
+ {
+ *pFileSize = pStream->Base.Socket.fileDataLength;
+ }
+ }
+ CascUnlock(pStream->Lock);
- // Not supported
- pStream = pStream;
- pByteOffset = pByteOffset;
- pvBuffer = pvBuffer;
- dwBytesToRead = dwBytesToRead;
- SetCascError(ERROR_NOT_SUPPORTED);
- return false;
+ // Give the result
+ return bResult;
+}
-#endif
+static bool BaseHttp_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset)
+{
+ // Give the current position
+ *pByteOffset = pStream->Base.Socket.fileDataPos;
+ return true;
}
static void BaseHttp_Close(TFileStream * pStream)
{
-#ifdef PLATFORM_WINDOWS
- if(pStream->Base.Http.hConnect != NULL)
- InternetCloseHandle(pStream->Base.Http.hConnect);
- pStream->Base.Http.hConnect = NULL;
-
- if(pStream->Base.Http.hInternet != NULL)
- InternetCloseHandle(pStream->Base.Http.hInternet);
- pStream->Base.Http.hInternet = NULL;
-#else
- pStream = pStream;
-#endif
+ if(pStream->Base.Socket.fileData != NULL)
+ CASC_FREE(pStream->Base.Socket.fileData);
+ if(pStream->Base.Socket.hostName != NULL)
+ CASC_FREE(pStream->Base.Socket.hostName);
+ if(pStream->Base.Socket.fileName != NULL)
+ CASC_FREE(pStream->Base.Socket.fileName);
+ if(pStream->Base.Socket.pSocket != NULL)
+ pStream->Base.Socket.pSocket->Release();
+ memset(&pStream->Base.Socket, 0, sizeof(pStream->Base.Socket));
}
// Initializes base functions for the mapped file
@@ -851,8 +809,8 @@ static void BaseHttp_Init(TFileStream * pStream)
// Supply the stream functions
pStream->BaseOpen = BaseHttp_Open;
pStream->BaseRead = BaseHttp_Read;
- pStream->BaseGetSize = BaseFile_GetSize; // Reuse BaseFile function
- pStream->BaseGetPos = BaseFile_GetPos; // Reuse BaseFile function
+ pStream->BaseGetSize = BaseHttp_GetSize;
+ pStream->BaseGetPos = BaseHttp_GetPos;
pStream->BaseClose = BaseHttp_Close;
// HTTP files are read-only
@@ -894,7 +852,7 @@ static bool BlockStream_Read(
return true;
// Get the current position in the stream
- ByteOffset = (pByteOffset != NULL) ? pByteOffset[0] : pStream->StreamPos;
+ ByteOffset = GetByteOffset(pByteOffset, pStream->StreamPos);
EndOffset = ByteOffset + dwBytesToRead;
if(EndOffset > pStream->StreamSize)
{
@@ -1029,11 +987,12 @@ static void BlockStream_Close(TBlockStream * pStream)
//-----------------------------------------------------------------------------
// File stream allocation function
-static STREAM_INIT StreamBaseInit[4] =
+static STREAM_INIT StreamBaseInit[5] =
{
BaseFile_Init,
BaseMap_Init,
BaseHttp_Init,
+ BaseHttp_Init, // Ribbit provider shares code with HTTP provider
BaseNone_Init
};
@@ -2526,42 +2485,50 @@ size_t FileStream_Prefix(LPCTSTR szFileName, DWORD * pdwProvider)
nPrefixLength1 = 5;
}
+ // Cut out the stream provider
+ szFileName += nPrefixLength1;
+
//
// Determine the base provider
//
- if(!_tcsnicmp(szFileName+nPrefixLength1, _T("file:"), 5))
+ if(!_tcsnicmp(szFileName, _T("file:"), 5))
{
dwProvider |= BASE_PROVIDER_FILE;
nPrefixLength2 = 5;
}
- else if(!_tcsnicmp(szFileName+nPrefixLength1, _T("map:"), 4))
+ else if(!_tcsnicmp(szFileName, _T("map:"), 4))
{
dwProvider |= BASE_PROVIDER_MAP;
nPrefixLength2 = 4;
}
- else if(!_tcsnicmp(szFileName+nPrefixLength1, _T("http:"), 5))
+ else if(!_tcsnicmp(szFileName, _T("http:"), 5))
{
dwProvider |= BASE_PROVIDER_HTTP;
nPrefixLength2 = 5;
}
+ else if(!_tcsnicmp(szFileName, _T("ribbit:"), 7))
+ {
+ dwProvider |= BASE_PROVIDER_RIBBIT;
+ nPrefixLength2 = 7;
+ }
+
// Only accept stream provider if we recognized the base provider
if(nPrefixLength2 != 0)
{
// It is also allowed to put "//" after the base provider, e.g. "file://", "http://"
- if(szFileName[nPrefixLength1+nPrefixLength2] == '/' && szFileName[nPrefixLength1+nPrefixLength2+1] == '/')
+ if(szFileName[nPrefixLength2] == '/' && szFileName[nPrefixLength2+1] == '/')
nPrefixLength2 += 2;
if(pdwProvider != NULL)
*pdwProvider = dwProvider;
- return nPrefixLength1 + nPrefixLength2;
}
}
- return 0;
+ return nPrefixLength1 + nPrefixLength2;
}
/**
diff --git a/dep/CascLib/src/common/FileStream.h b/dep/CascLib/src/common/FileStream.h
index acd0683a596..df782200626 100644
--- a/dep/CascLib/src/common/FileStream.h
+++ b/dep/CascLib/src/common/FileStream.h
@@ -16,7 +16,8 @@
#define BASE_PROVIDER_FILE 0x00000000 // Base data source is a file
#define BASE_PROVIDER_MAP 0x00000001 // Base data source is memory-mapped file
-#define BASE_PROVIDER_HTTP 0x00000002 // Base data source is a file on web server
+#define BASE_PROVIDER_HTTP 0x00000002 // Base data source is a file on web server via the HTTP protocol
+#define BASE_PROVIDER_RIBBIT 0x00000003 // Base data source is a file on web server via the Ribbit protocol
#define BASE_PROVIDER_MASK 0x0000000F // Mask for base provider value
#define STREAM_PROVIDER_FLAT 0x00000000 // Stream is linear with no offset mapping
@@ -29,7 +30,6 @@
#define STREAM_FLAG_WRITE_SHARE 0x00000200 // Allow write sharing when open for write
#define STREAM_FLAG_USE_BITMAP 0x00000400 // If the file has a file bitmap, load it and use it
#define STREAM_FLAG_FILL_MISSING 0x00000800 // If less than expected was read from the file, fill the missing part with zeros
-#define STREAM_FLAG_USE_PORT_1119 0x00001000 // For HTTP streams, use port 1119
#define STREAM_OPTIONS_MASK 0x0000FF00 // Mask for stream options
#define STREAM_PROVIDERS_MASK 0x000000FF // Mask to get stream providers
@@ -172,12 +172,13 @@ union TBaseProviderData
struct
{
- ULONGLONG FileSize; // Size of the file
- ULONGLONG FilePos; // Current file position
- ULONGLONG FileTime; // Last write time
- HANDLE hInternet; // Internet handle
- HANDLE hConnect; // Connection to the internet server
- } Http;
+ class CASC_SOCKET * pSocket; // An open socket
+ unsigned char * fileData; // Raw response converted to file data
+ char * hostName; // Name of the remote host
+ char * fileName; // Name of the remote resource
+ size_t fileDataLength; // Length of the file data, in bytes
+ size_t fileDataPos; // Current position in the data
+ } Socket;
};
struct TFileStream
@@ -249,7 +250,7 @@ struct TEncryptedStream : public TBlockStream
// Public functions for file stream
TFileStream * FileStream_CreateFile(LPCTSTR szFileName, DWORD dwStreamFlags);
-TFileStream * FileStream_OpenFile(LPCTSTR szFileName, DWORD dwStreamFlags);
+TFileStream * FileStream_OpenFile(LPCTSTR szFileName, DWORD dwStreamFlags = 0);
LPCTSTR FileStream_GetFileName(TFileStream * pStream);
size_t FileStream_Prefix(LPCTSTR szFileName, DWORD * pdwProvider);
diff --git a/dep/CascLib/src/common/Mime.cpp b/dep/CascLib/src/common/Mime.cpp
new file mode 100644
index 00000000000..52a3a5f645f
--- /dev/null
+++ b/dep/CascLib/src/common/Mime.cpp
@@ -0,0 +1,674 @@
+/*****************************************************************************/
+/* Mime.cpp Copyright (c) Ladislav Zezula 2021 */
+/*---------------------------------------------------------------------------*/
+/* Mime functions for CascLib */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 21.01.21 1.00 Lad Created */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "../CascLib.h"
+#include "../CascCommon.h"
+
+//-----------------------------------------------------------------------------
+// Local variables
+
+#define BASE64_INVALID_CHAR 0xFF
+#define BASE64_WHITESPACE_CHAR 0xFE
+
+static const char * CascBase64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static unsigned char CascBase64ToBits[0x80] = {0};
+
+//-----------------------------------------------------------------------------
+// CASC_MIME_HTTP implementation
+
+static size_t DecodeValueInt32(const char * string, const char * string_end)
+{
+ size_t result = 0;
+
+ while(string < string_end && isdigit(string[0]))
+ {
+ result = (result * 10) + (string[0] - '0');
+ string++;
+ }
+
+ return result;
+}
+
+bool CASC_MIME_HTTP::IsDataComplete(const char * response, size_t response_length)
+{
+ const char * content_length_ptr;
+ const char * content_begin_ptr;
+
+ // Do not parse the HTTP response multiple times
+ if(response_valid == 0 && response_length > 8)
+ {
+ // Check the begin of the response
+ if(!strncmp(response, "HTTP/1.1", 8))
+ {
+ // Check if there's begin of the content
+ if((content_begin_ptr = strstr(response, "\r\n\r\n")) != NULL)
+ {
+ // HTTP responses contain "Content-Length: %u\n\r"
+ if((content_length_ptr = strstr(response, "Content-Length: ")) != NULL)
+ {
+ // The content length info muse be before the actual content
+ if(content_length_ptr < content_begin_ptr)
+ {
+ // Fill the HTTP info cache
+ response_valid = 0x48545450; // 'HTTP'
+ content_offset = (content_begin_ptr + 4) - response;
+ content_length = DecodeValueInt32(content_length_ptr + 16, content_begin_ptr);
+ total_length = content_offset + content_length;
+ }
+ }
+ }
+ }
+ }
+
+ // If we know the expected total length, we can tell whether it's complete or not
+ return ((response_valid != 0) && (total_length == response_length));
+}
+
+//-----------------------------------------------------------------------------
+// The MIME blob class
+
+CASC_MIME_BLOB::CASC_MIME_BLOB(char * mime_ptr, char * mime_end)
+{
+ ptr = mime_ptr;
+ end = mime_end;
+}
+
+CASC_MIME_BLOB::~CASC_MIME_BLOB()
+{
+ ptr = end = NULL;
+}
+
+char * CASC_MIME_BLOB::GetNextLine()
+{
+ char * mime_line = ptr;
+
+ while(ptr < end)
+ {
+ // Every line, even the last one, must be terminated with 0D 0A
+ if(ptr[0] == 0x0D && ptr[1] == 0x0A)
+ {
+ // If space or tabulator follows, then this is continuation of the line
+ if(ptr[2] == 0x09 || ptr[2] == 0x20)
+ {
+ ptr = ptr + 2;
+ continue;
+ }
+
+ // Terminate the line and return its begin
+ ptr[0] = 0;
+ ptr[1] = 0;
+ ptr = ptr + 2;
+ return mime_line;
+ }
+
+ // Move to tne next character
+ ptr++;
+ }
+
+ // No EOL terminated line found, break the search
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// The MIME element class
+
+CASC_MIME_ELEMENT::CASC_MIME_ELEMENT()
+{
+ memset(this, 0, sizeof(CASC_MIME_ELEMENT));
+}
+
+CASC_MIME_ELEMENT::~CASC_MIME_ELEMENT()
+{
+ // Free the children and next elements
+ if(folder.pChild != NULL)
+ delete folder.pChild;
+ folder.pChild = NULL;
+
+ if(folder.pNext != NULL)
+ delete folder.pNext;
+ folder.pNext = NULL;
+
+ // Free the data
+ if(data.begin != NULL)
+ CASC_FREE(data.begin);
+ data.begin = NULL;
+}
+
+unsigned char * CASC_MIME_ELEMENT::GiveAway(size_t * ptr_data_length)
+{
+ unsigned char * give_away_data = data.begin;
+ size_t give_away_length = data.length;
+
+ // Clear the data (DO NOT FREE)
+ data.begin = NULL;
+ data.length = 0;
+
+ // Copy the data to local buffer
+ if(ptr_data_length != NULL)
+ ptr_data_length[0] = give_away_length;
+ return give_away_data;
+}
+
+DWORD CASC_MIME_ELEMENT::Load(char * mime_data_begin, char * mime_data_end, const char * boundary_ptr)
+{
+ CASC_MIME_ENCODING Encoding = MimeEncodingTextPlain;
+ CASC_MIME_BLOB mime_data(mime_data_begin, mime_data_end);
+ CASC_MIME_HTTP HttpInfo;
+ size_t length_begin;
+ size_t length_end;
+ char * mime_line;
+ char boundary_begin[MAX_LENGTH_BOUNDARY + 2];
+ char boundary_end[MAX_LENGTH_BOUNDARY + 4];
+ DWORD dwErrCode = ERROR_SUCCESS;
+ bool mime_version = false;
+
+ // Diversion for HTTP: No need to parse the entire headers and stuff.
+ // Just give the data right away
+ if(HttpInfo.IsDataComplete(mime_data_begin, (mime_data_end - mime_data_begin)))
+ {
+ if((data.begin = CASC_ALLOC<BYTE>(HttpInfo.content_length)) == NULL)
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ memcpy(data.begin, mime_data_begin + HttpInfo.content_offset, HttpInfo.content_length);
+ data.length = HttpInfo.content_length;
+ return ERROR_SUCCESS;
+ }
+
+ // Reset the boundary
+ boundary[0] = 0;
+
+ // Parse line-by-line untile we find the end of data
+ while((mime_line = mime_data.GetNextLine()) != NULL)
+ {
+ // If the line is empty, this means that it's the end of the MIME header and begin of the MIME data
+ if(mime_line[0] == 0)
+ {
+ mime_data.ptr = mime_line + 2;
+ break;
+ }
+
+ // Root nodes are required to have MIME version
+ if(boundary_ptr == NULL && mime_version == false)
+ {
+ if(!strcmp(mime_line, "MIME-Version: 1.0"))
+ {
+ mime_version = true;
+ continue;
+ }
+ if(!strcmp(mime_line, "HTTP/1.1 200 OK"))
+ {
+ mime_version = true;
+ continue;
+ }
+ }
+
+ // Get the encoding
+ if(!strncmp(mime_line, "Content-Transfer-Encoding: ", 27))
+ {
+ ExtractEncoding(mime_line + 27, Encoding);
+ continue;
+ }
+
+ // Is there content type?
+ if(!strncmp(mime_line, "Content-Type: ", 14))
+ {
+ ExtractBoundary(mime_line + 14);
+ continue;
+ }
+ }
+
+ // Keep going only if we have MIME version
+ if(boundary_ptr != NULL || mime_version == true)
+ {
+ // If we have boundary, it means that the element begin
+ // and end is marked by the boundary
+ if(boundary[0])
+ {
+ CASC_MIME_ELEMENT * pLast = NULL;
+ CASC_MIME_ELEMENT * pChild;
+ CASC_MIME_BLOB sub_mime(mime_data.ptr, NULL);
+
+ // Construct the begin of the boundary. Don't include newline there,
+ // as it must also match end of the boundary
+ length_begin = CascStrPrintf(boundary_begin, _countof(boundary_begin), "--%s", boundary);
+ dwErrCode = ERROR_SUCCESS;
+
+ // The current line must point to the begin of the boundary
+ // Find the end of the boundary
+ if(memcmp(sub_mime.ptr, boundary_begin, length_begin))
+ return ERROR_BAD_FORMAT;
+ sub_mime.ptr += length_begin;
+
+ // Find the end of the boundary
+ length_end = CascStrPrintf(boundary_end, _countof(boundary_end), "--%s--\r\n", boundary);
+ sub_mime.end = strstr(sub_mime.ptr, boundary_end);
+ if(sub_mime.end == NULL)
+ return ERROR_BAD_FORMAT;
+
+ // This is the end of the MIME section. Cut it to blocks
+ // and put each into the separate CASC_MIME_ELEMENT
+ while(sub_mime.ptr < sub_mime.end)
+ {
+ char * sub_mime_next;
+
+ // At this point, there must be newline in the current data pointer
+ if(sub_mime.ptr[0] != 0x0D || sub_mime.ptr[1] != 0x0A)
+ return ERROR_BAD_FORMAT;
+ sub_mime.ptr += 2;
+
+ // Find the next MIME element. This must succeed, as the last element also matches the first element
+ sub_mime_next = strstr(sub_mime.ptr, boundary_begin);
+ if(sub_mime_next == NULL)
+ return ERROR_BAD_FORMAT;
+
+ // Allocate the element
+ pChild = AllocateAndLoadElement(sub_mime.ptr, sub_mime_next - 2, boundary_begin);
+ if(pChild == NULL)
+ {
+ dwErrCode = GetCascError();
+ break;
+ }
+
+ // Link the element
+ if(folder.pChild == NULL)
+ folder.pChild = pChild;
+ if(pLast != NULL)
+ pLast->folder.pNext = pChild;
+ pLast = pChild;
+
+ // Move to the next MIME element. Note that if we are at the ending boundary,
+ // this moves past the end, but it's OK, because the while loop will catch that
+ sub_mime.ptr = sub_mime_next + length_begin;
+ }
+ }
+ else
+ {
+ CASC_MIME_BLOB content(mime_data.ptr, NULL);
+ unsigned char * data_buffer;
+ size_t data_length = 0;
+ size_t raw_length;
+
+ // If we have boundary pointer, we need to cut the data up to the boundary end.
+ // Otherwise, we decode the data to the end of the document
+ if(boundary_ptr != NULL)
+ {
+ // Find the end of the current data by the boundary. It is 2 characters before the next boundary
+ content.end = strstr(content.ptr, boundary_ptr);
+ if(content.end == NULL)
+ return ERROR_BAD_FORMAT;
+ if((content.ptr + 2) >= content.end)
+ return ERROR_BAD_FORMAT;
+ content.end -= 2;
+ }
+ else
+ {
+ content.end = mime_data_end - 2;
+ if(content.end[0] != 0x0D || content.end[1] != 0x0A)
+ return ERROR_BAD_FORMAT;
+ if((content.ptr + 2) >= content.end)
+ return ERROR_BAD_FORMAT;
+ }
+
+ // Allocate buffer for decoded data.
+ // Make it the same size like source data plus zero at the end
+ raw_length = (content.end - content.ptr);
+ data_buffer = CASC_ALLOC<unsigned char>(raw_length);
+ if(data_buffer != NULL)
+ {
+ // Decode the data
+ switch(Encoding)
+ {
+ case MimeEncodingTextPlain:
+ dwErrCode = DecodeTextPlain(content.ptr, content.end, data_buffer, &data_length);
+ break;
+
+ case MimeEncodingQuotedPrintable:
+ dwErrCode = DecodeQuotedPrintable(content.ptr, content.end, data_buffer, &data_length);
+ break;
+
+ case MimeEncodingBase64:
+ dwErrCode = DecodeBase64(content.ptr, content.end, data_buffer, &data_length);
+ break;
+
+ default:;
+ // to remove warning
+ }
+
+ // If failed, free the buffer back
+ if(dwErrCode != ERROR_SUCCESS)
+ {
+ CASC_FREE(data_buffer);
+ data_buffer = NULL;
+ data_length = 0;
+ }
+ }
+ else
+ {
+ dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ // Put the data there, even if they are invalid
+ data.begin = data_buffer;
+ data.length = data_length;
+ }
+ }
+ else
+ {
+ dwErrCode = ERROR_NOT_SUPPORTED;
+ }
+
+ // Return the result of the decoding
+ return dwErrCode;
+}
+
+#ifdef _DEBUG
+#define MAX_LEVEL 0x10
+void CASC_MIME_ELEMENT::Print(size_t nLevel, size_t nIndex)
+{
+ char Prefix[(MAX_LEVEL * 4) + 0x20 + 1] = {0};
+ size_t nSpaces;
+
+ // Fill-in the spaces
+ nSpaces = (nLevel < MAX_LEVEL) ? (nLevel * 4) : (MAX_LEVEL * 4);
+ memset(Prefix, ' ', nSpaces);
+
+ // Print the spaces and index
+ nSpaces = printf("%s* [%u]: ", Prefix, (int)nIndex);
+ memset(Prefix, ' ', nSpaces);
+
+ // Is this a folder item?
+ if(folder.pChild != NULL)
+ {
+ printf("Folder item (boundary: %s)\n", boundary);
+ folder.pChild->Print(nLevel + 1, 0);
+ }
+ else
+ {
+ char data_printable[0x20] = {0};
+
+ for(size_t i = 0; (i < data.length && i < _countof(data_printable) - 1); i++)
+ {
+ if(0x20 <= data.begin[i] && data.begin[i] <= 0x7F)
+ data_printable[i] = data.begin[i];
+ else
+ data_printable[i] = '.';
+ }
+
+ printf("Data item (%u bytes): \"%s\"\n", (int)data.length, data_printable);
+ }
+
+ // Do we have a next element?
+ if(folder.pNext != NULL)
+ {
+ folder.pNext->Print(nLevel, nIndex + 1);
+ }
+}
+#endif
+
+CASC_MIME_ELEMENT * CASC_MIME_ELEMENT::AllocateAndLoadElement(char * a_mime_data, char * a_mime_data_end, const char * boundary_begin)
+{
+ CASC_MIME_ELEMENT * pElement;
+ DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
+
+ // Allocate the element
+ if((pElement = new CASC_MIME_ELEMENT()) != NULL)
+ {
+ // Load the element
+ dwErrCode = pElement->Load(a_mime_data, a_mime_data_end, boundary_begin);
+ if(dwErrCode == ERROR_SUCCESS)
+ return pElement;
+
+ // Free the element on failure
+ delete pElement;
+ }
+
+ SetCascError(dwErrCode);
+ return NULL;
+}
+
+bool CASC_MIME_ELEMENT::ExtractEncoding(const char * line, CASC_MIME_ENCODING & Encoding)
+{
+ if(!_stricmp(line, "base64"))
+ {
+ Encoding = MimeEncodingBase64;
+ return true;
+ }
+
+ if(!_stricmp(line, "quoted-printable"))
+ {
+ Encoding = MimeEncodingQuotedPrintable;
+ return true;
+ }
+
+ // Unknown encoding
+ return false;
+}
+
+
+bool CASC_MIME_ELEMENT::ExtractBoundary(const char * line)
+{
+ const char * begin;
+ const char * end;
+
+ // Find the begin of the boundary
+ if((begin = strstr(line, "boundary=\"")) != NULL)
+ {
+ // Set begin of the boundary
+ begin = begin + 10;
+
+ // Is there also end?
+ if((end = strchr(begin, '\"')) != NULL)
+ {
+ CascStrCopy(boundary, _countof(boundary), begin, end - begin);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+DWORD CASC_MIME_ELEMENT::DecodeTextPlain(char * content_begin, char * content_end, unsigned char * data_buffer, size_t * ptr_length)
+{
+ size_t data_length = (size_t)(content_end - content_begin);
+
+ // Sanity checks
+ assert(content_begin && content_end);
+ assert(content_end > content_begin);
+
+ // Plain copy
+ memcpy(data_buffer, content_begin, data_length);
+
+ // Give the result
+ if(ptr_length != NULL)
+ ptr_length[0] = data_length;
+ return ERROR_SUCCESS;
+}
+
+
+DWORD CASC_MIME_ELEMENT::DecodeQuotedPrintable(char * content_begin, char * content_end, unsigned char * data_buffer, size_t * ptr_length)
+{
+ unsigned char * save_data_buffer = data_buffer;
+ char * content_ptr;
+ DWORD dwErrCode;
+
+ // Sanity checks
+ assert(content_begin && content_end);
+ assert(content_end > content_begin);
+
+ // Decode the data
+ for(content_ptr = content_begin; content_ptr < content_end; )
+ {
+ // If the data begins with '=', there is either newline or 2-char hexa number
+ if(content_ptr[0] == '=')
+ {
+ // Is there a newline after the equal sign?
+ if(content_ptr[1] == 0x0D && content_ptr[2] == 0x0A)
+ {
+ content_ptr += 3;
+ continue;
+ }
+
+ // Is there hexa number after the equal sign?
+ dwErrCode = BinaryFromString(content_ptr + 1, 2, data_buffer);
+ if(dwErrCode != ERROR_SUCCESS)
+ return dwErrCode;
+
+ content_ptr += 3;
+ data_buffer++;
+ continue;
+ }
+ else
+ {
+ *data_buffer++ = (unsigned char)(*content_ptr++);
+ }
+ }
+
+ if(ptr_length != NULL)
+ ptr_length[0] = (size_t)(data_buffer - save_data_buffer);
+ return ERROR_SUCCESS;
+}
+
+DWORD CASC_MIME_ELEMENT::DecodeBase64(char * content_begin, char * content_end, unsigned char * data_buffer, size_t * ptr_length)
+{
+ unsigned char * save_data_buffer = data_buffer;
+ DWORD BitBuffer = 0;
+ DWORD BitCount = 0;
+ BYTE OneByte;
+
+ // One time preparation of the conversion table
+ if(CascBase64ToBits[0] == 0)
+ {
+ // Fill the entire table with 0xFF to mark invalid characters
+ memset(CascBase64ToBits, BASE64_INVALID_CHAR, sizeof(CascBase64ToBits));
+
+ // Set all whitespace characters
+ for(BYTE i = 1; i <= 0x20; i++)
+ CascBase64ToBits[i] = BASE64_WHITESPACE_CHAR;
+
+ // Set all valid characters
+ for(BYTE i = 0; CascBase64Table[i] != 0; i++)
+ {
+ OneByte = CascBase64Table[i];
+ CascBase64ToBits[OneByte] = i;
+ }
+ }
+
+ // Do the decoding
+ while(content_begin < content_end && content_begin[0] != '=')
+ {
+ // Check for end of string
+ if(content_begin[0] > sizeof(CascBase64ToBits))
+ return ERROR_BAD_FORMAT;
+ if((OneByte = CascBase64ToBits[*content_begin++]) == BASE64_INVALID_CHAR)
+ return ERROR_BAD_FORMAT;
+ if(OneByte == BASE64_WHITESPACE_CHAR)
+ continue;
+
+ // Put the 6 bits into the bit buffer
+ BitBuffer = (BitBuffer << 6) | OneByte;
+ BitCount += 6;
+
+ // Flush all values
+ while(BitCount >= 8)
+ {
+ // Decrement the bit count in the bit buffer
+ BitCount -= 8;
+
+ // The byte is the upper 8 bits of the bit buffer
+ OneByte = (BYTE)(BitBuffer >> BitCount);
+ BitBuffer = BitBuffer % (1 << BitCount);
+
+ // Put the byte value. The buffer can not overflow,
+ // because it is guaranteed to be equal to the length of the base64 string
+ *data_buffer++ = OneByte;
+ }
+ }
+
+ // Return the decoded length
+ if(ptr_length != NULL)
+ ptr_length[0] = (data_buffer - save_data_buffer);
+ return ERROR_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// The MIME class
+
+CASC_MIME::CASC_MIME()
+{}
+
+CASC_MIME::~CASC_MIME()
+{}
+
+unsigned char * CASC_MIME::GiveAway(size_t * ptr_data_length)
+{
+ CASC_MIME_ELEMENT * pElement = &root;
+ unsigned char * data;
+
+ // 1) Give the data from the root
+ if((data = root.GiveAway(ptr_data_length)) != NULL)
+ return data;
+
+ // 2) If we have children, then give away from the first child
+ pElement = root.GetChild();
+ if(pElement && (data = pElement->GiveAway(ptr_data_length)) != NULL)
+ return data;
+
+ // Return NULL
+ if(ptr_data_length != NULL)
+ ptr_data_length[0] = 0;
+ return NULL;
+}
+
+DWORD CASC_MIME::Load(char * data, size_t length)
+{
+ // Clear the root element
+ memset(&root, 0, sizeof(CASC_MIME_ELEMENT));
+
+ //FILE * fp = fopen("E:\\html_response.txt", "wb");
+ //if(fp != NULL)
+ //{
+ // fwrite(data, 1, length, fp);
+ // fclose(fp);
+ //}
+
+ // Load the root element
+ return root.Load(data, data + length);
+}
+
+DWORD CASC_MIME::Load(LPCTSTR szFileName)
+{
+ char * szFileData;
+ DWORD cbFileData = 0;
+ DWORD dwErrCode = ERROR_SUCCESS;
+
+ // Note that LoadFileToMemory allocated one byte more and puts zero at the end
+ // Thus, we can treat it as zero-terminated string
+ szFileData = (char *)LoadFileToMemory(szFileName, &cbFileData);
+ if(szFileData != NULL)
+ {
+ dwErrCode = Load(szFileData, cbFileData);
+ CASC_FREE(szFileData);
+ }
+ else
+ {
+ dwErrCode = GetCascError();
+ }
+
+ return dwErrCode;
+}
+
+#ifdef _DEBUG
+void CASC_MIME::Print()
+{
+ root.Print(0, 0);
+}
+#endif
+
diff --git a/dep/CascLib/src/common/Mime.h b/dep/CascLib/src/common/Mime.h
new file mode 100644
index 00000000000..01dc35fdac9
--- /dev/null
+++ b/dep/CascLib/src/common/Mime.h
@@ -0,0 +1,130 @@
+/*****************************************************************************/
+/* Mime.h Copyright (c) Ladislav Zezula 2021 */
+/*---------------------------------------------------------------------------*/
+/* MIME parsing functions for CascLib */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 21.01.21 1.00 Lad Created */
+/*****************************************************************************/
+
+#ifndef __MIME_H__
+#define __MIME_H__
+
+//-----------------------------------------------------------------------------
+// MIME constants
+
+#define MAX_LENGTH_BOUNDARY 128
+
+enum CASC_MIME_ENCODING
+{
+ MimeEncodingTextPlain,
+ MimeEncodingBase64,
+ MimeEncodingQuotedPrintable,
+ MimeEncodingMax
+};
+
+//-----------------------------------------------------------------------------
+// Structure for caching parsed HTTP response information
+
+struct CASC_MIME_HTTP
+{
+ CASC_MIME_HTTP()
+ {
+ response_valid = content_length = content_offset = total_length = 0;
+ }
+
+ bool IsDataComplete(const char * response, size_t response_length);
+
+ size_t response_valid; // Nonzero if this is an already parsed HTTP response
+ size_t content_length; // Parsed value of "Content-Length"
+ size_t content_offset; // Offset of the HTTP data, relative to the begin of the response
+ size_t total_length; // Expected total length of the HTTP response (content_offset + content_size)
+};
+
+//-----------------------------------------------------------------------------
+// MIME blob class
+
+struct CASC_MIME_BLOB
+{
+ CASC_MIME_BLOB(char * mime_ptr, char * mime_end);
+ ~CASC_MIME_BLOB();
+
+ char * GetNextLine();
+
+ char * ptr;
+ char * end;
+};
+
+//-----------------------------------------------------------------------------
+// MIME class
+
+class CASC_MIME_ELEMENT
+{
+ public:
+
+ CASC_MIME_ELEMENT();
+ ~CASC_MIME_ELEMENT();
+
+ unsigned char * GiveAway(size_t * ptr_data_length);
+
+ DWORD Load(char * mime_data_begin, char * mime_data_end, const char * boundary_ptr = NULL);
+
+ CASC_MIME_ELEMENT * GetChild() { return folder.pChild; }
+
+#ifdef _DEBUG
+ void Print(size_t nLevel, size_t nIndex);
+#endif
+
+ protected:
+
+ CASC_MIME_ELEMENT * AllocateAndLoadElement(char * a_mime_data, char * a_mime_data_end, const char * boundary_begin);
+ bool ExtractEncoding(const char * line, CASC_MIME_ENCODING & Encoding);
+ bool ExtractBoundary(const char * line);
+
+ DWORD DecodeTextPlain(char * content_begin, char * content_end, unsigned char * data_ptr, size_t * ptr_length);
+ DWORD DecodeQuotedPrintable(char * content_begin, char * content_end, unsigned char * data_ptr, size_t * ptr_length);
+ DWORD DecodeBase64(char * content_begin, char * content_end, unsigned char * data_ptr, size_t * ptr_length);
+
+ struct
+ {
+ CASC_MIME_ELEMENT * pChild; // Pointer to the first child
+ CASC_MIME_ELEMENT * pNext; // Pointer to the next-in-folder element
+ } folder;
+
+ struct
+ {
+ unsigned char * begin;
+ size_t length;
+ } data;
+
+ char boundary[MAX_LENGTH_BOUNDARY];
+};
+
+class CASC_MIME
+{
+ public:
+
+ CASC_MIME();
+ ~CASC_MIME();
+
+ unsigned char * GiveAway(size_t * ptr_data_length);
+
+ DWORD Load(char * data, size_t length);
+ DWORD Load(LPCTSTR fileName);
+
+#ifdef _DEBUG
+ void Print();
+#endif
+
+ protected:
+
+ CASC_MIME_ELEMENT root;
+};
+
+//-----------------------------------------------------------------------------
+// HTTP helpers
+
+bool IsHttpResponseComplete(CASC_MIME_HTTP & HttpInfo, const char * response, size_t response_length);
+
+#endif // __MIME_H__
diff --git a/dep/CascLib/src/common/Path.h b/dep/CascLib/src/common/Path.h
index b77dde0cc3d..98ba601d33b 100644
--- a/dep/CascLib/src/common/Path.h
+++ b/dep/CascLib/src/common/Path.h
@@ -17,11 +17,11 @@
template <typename xchar>
struct CASC_PATH
{
- CASC_PATH(xchar chSeparator = PATH_SEP_CHAR)
+ CASC_PATH(int chSeparator = PATH_SEP_CHAR)
{
m_szBufferBegin = m_szBufferPtr = m_Buffer;
m_szBufferEnd = m_szBufferBegin + _countof(m_Buffer);
- m_chSeparator = chSeparator;
+ m_chSeparator = (xchar)chSeparator;
m_Buffer[0] = 0;
}
diff --git a/dep/CascLib/src/common/Sockets.cpp b/dep/CascLib/src/common/Sockets.cpp
new file mode 100644
index 00000000000..e1f58f6f79c
--- /dev/null
+++ b/dep/CascLib/src/common/Sockets.cpp
@@ -0,0 +1,441 @@
+/*****************************************************************************/
+/* Sockets.cpp Copyright (c) Ladislav Zezula 2021 */
+/*---------------------------------------------------------------------------*/
+/* Don't call this module "Socket.cpp", otherwise VS 2019 will not link it */
+/* Socket functions for CascLib. */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 13.02.21 1.00 Lad Created */
+/*****************************************************************************/
+
+#define __CASCLIB_SELF__
+#include "../CascLib.h"
+#include "../CascCommon.h"
+
+//-----------------------------------------------------------------------------
+// Local variables
+
+CASC_SOCKET_CACHE SocketCache;
+
+//-----------------------------------------------------------------------------
+// CASC_SOCKET functions
+
+// Guarantees that there is zero terminator after the response
+char * CASC_SOCKET::ReadResponse(const char * request, size_t request_length, size_t * PtrLength)
+{
+ CASC_MIME_HTTP HttpInfo;
+ char * server_response = NULL;
+ size_t total_received = 0;
+ size_t block_increment = 0x8000;
+ size_t buffer_size = block_increment;
+ int bytes_received = 0;
+
+ // Pre-set the result length
+ if(PtrLength != NULL)
+ PtrLength[0] = 0;
+ if(request_length == 0)
+ request_length = strlen(request);
+
+ // Lock the socket
+ CascLock(Lock);
+
+ // Send the request to the remote host. On Linux, this call may send signal(SIGPIPE),
+ // we need to prevend that by using the MSG_NOSIGNAL flag. On Windows, it fails normally.
+ while(send(sock, request, (int)request_length, MSG_NOSIGNAL) == SOCKET_ERROR)
+ {
+ // If the connection was closed by the remote host, we try to reconnect
+ if(ReconnectAfterShutdown(sock, remoteItem) == INVALID_SOCKET)
+ {
+ SetCascError(ERROR_NETWORK_NOT_AVAILABLE);
+ CascUnlock(Lock);
+ return NULL;
+ }
+ }
+
+ // Allocate buffer for server response. Allocate one extra byte for zero terminator
+ if((server_response = CASC_ALLOC<char>(buffer_size + 1)) != NULL)
+ {
+ for(;;)
+ {
+ // Reallocate the buffer size, if needed
+ if(total_received == buffer_size)
+ {
+ if((server_response = CASC_REALLOC(char, server_response, buffer_size + block_increment + 1)) == NULL)
+ {
+ SetCascError(ERROR_NOT_ENOUGH_MEMORY);
+ CascUnlock(Lock);
+ return NULL;
+ }
+ buffer_size += block_increment;
+ }
+
+ // Receive the next part of the response, up to buffer size
+ // Return value 0 means "connection closed", -1 means an error
+ bytes_received = recv(sock, server_response + total_received, (int)(buffer_size - total_received), 0);
+ if(bytes_received <= 0)
+ break;
+
+ // Append the number of bytes received. Also terminate response with zero
+ total_received += bytes_received;
+ server_response[total_received] = 0;
+
+ // On a HTTP protocol, we need to check whether we received all data
+ if(HttpInfo.IsDataComplete(server_response, total_received))
+ break;
+ }
+ }
+
+ // Unlock the socket
+ CascUnlock(Lock);
+
+ // Give the result to the caller
+ if(PtrLength != NULL)
+ PtrLength[0] = total_received;
+ return server_response;
+}
+
+DWORD CASC_SOCKET::AddRef()
+{
+ return CascInterlockedIncrement(&dwRefCount);
+}
+
+void CASC_SOCKET::Release()
+{
+ // Note: If this is a cached socket, there will be extra reference from the cache
+ if(CascInterlockedDecrement(&dwRefCount) == 0)
+ {
+ Delete();
+ }
+}
+
+int CASC_SOCKET::GetSockError()
+{
+#ifdef PLATFORM_WINDOWS
+ return WSAGetLastError();
+#else
+ return errno;
+#endif
+}
+
+DWORD CASC_SOCKET::GetAddrInfoWrapper(const char * hostName, unsigned portNum, PADDRINFO hints, PADDRINFO * ppResult)
+{
+ char portNumString[16];
+
+ // Prepare the port number
+ CascStrPrintf(portNumString, _countof(portNumString), "%d", portNum);
+
+ // Attempt to connect
+ for(;;)
+ {
+ // Attempt to call the addrinfo
+ DWORD dwErrCode = getaddrinfo(hostName, portNumString, hints, ppResult);
+
+ // Error-specific handling
+ switch(dwErrCode)
+ {
+#ifdef PLATFORM_WINDOWS
+ case WSANOTINITIALISED: // Windows-specific: WSAStartup not called
+ {
+ WSADATA wsd;
+
+ WSAStartup(MAKEWORD(2, 2), &wsd);
+ continue;
+ }
+#endif
+ case EAI_AGAIN: // Temporary error, try again
+ continue;
+
+ default: // Any other result, incl. ERROR_SUCCESS
+ return dwErrCode;
+ }
+ }
+}
+
+SOCKET CASC_SOCKET::CreateAndConnect(addrinfo * remoteItem)
+{
+ SOCKET sock;
+
+ // Create new socket
+ // On error, returns returns INVALID_SOCKET (-1) on Windows, -1 on Linux
+ if((sock = socket(remoteItem->ai_family, remoteItem->ai_socktype, remoteItem->ai_protocol)) > 0)
+ {
+ // Connect to the remote host
+ // On error, returns SOCKET_ERROR (-1) on Windows, -1 on Linux
+ if(connect(sock, remoteItem->ai_addr, (int)remoteItem->ai_addrlen) == 0)
+ return sock;
+
+ // Failed. Close the socket and return 0
+ closesocket(sock);
+ sock = INVALID_SOCKET;
+ }
+
+ return sock;
+}
+
+SOCKET CASC_SOCKET::ReconnectAfterShutdown(SOCKET & sock, addrinfo * remoteItem)
+{
+ // Retrieve the error code related to previous socket operation
+ switch(GetSockError())
+ {
+ case EPIPE: // Non-Windows
+ case WSAECONNRESET: // Windows
+ {
+ // Close the old socket
+ if(sock != INVALID_SOCKET)
+ closesocket(sock);
+
+ // Attempt to reconnect
+ sock = CreateAndConnect(remoteItem);
+ return sock;
+ }
+ }
+
+ // Another problem
+ return INVALID_SOCKET;
+}
+
+PCASC_SOCKET CASC_SOCKET::New(addrinfo * remoteList, addrinfo * remoteItem, const char * hostName, unsigned portNum, SOCKET sock)
+{
+ PCASC_SOCKET pSocket;
+ size_t length = strlen(hostName);
+
+ // Allocate enough bytes
+ pSocket = (PCASC_SOCKET)CASC_ALLOC<BYTE>(sizeof(CASC_SOCKET) + length);
+ if(pSocket != NULL)
+ {
+ // Fill the entire object with zero
+ memset(pSocket, 0, sizeof(CASC_SOCKET) + length);
+ pSocket->remoteList = remoteList;
+ pSocket->remoteItem = remoteItem;
+ pSocket->dwRefCount = 1;
+ pSocket->portNum = portNum;
+ pSocket->sock = sock;
+
+ // Init the remote host name
+ CascStrCopy((char *)pSocket->hostName, length + 1, hostName);
+
+ // Init the socket lock
+ CascInitLock(pSocket->Lock);
+ }
+
+ return pSocket;
+}
+
+PCASC_SOCKET CASC_SOCKET::Connect(const char * hostName, unsigned portNum)
+{
+ PCASC_SOCKET pSocket;
+ addrinfo * remoteList;
+ addrinfo * remoteItem;
+ addrinfo hints = {0};
+ SOCKET sock;
+ int nErrCode;
+
+ // Retrieve the information about the remote host
+ // This will fail immediately if there is no connection to the internet
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ nErrCode = GetAddrInfoWrapper(hostName, portNum, &hints, &remoteList);
+
+ // Handle error code
+ if(nErrCode == 0)
+ {
+ // Try to connect to any address provided by the getaddrinfo()
+ for(remoteItem = remoteList; remoteItem != NULL; remoteItem = remoteItem->ai_next)
+ {
+ // Create new socket and connect to the remote host
+ if((sock = CreateAndConnect(remoteItem)) != 0)
+ {
+ // Create new instance of the CASC_SOCKET structure
+ if((pSocket = CASC_SOCKET::New(remoteList, remoteItem, hostName, portNum, sock)) != NULL)
+ {
+ return pSocket;
+ }
+
+ // Close the socket
+ closesocket(sock);
+ }
+ }
+
+ // Couldn't find a network
+ nErrCode = ERROR_NETWORK_NOT_AVAILABLE;
+ }
+
+ SetCascError(nErrCode);
+ return NULL;
+}
+
+void CASC_SOCKET::Delete()
+{
+ PCASC_SOCKET pThis = this;
+
+ // Remove the socket from the cache
+ if(pCache != NULL)
+ pCache->UnlinkSocket(this);
+ pCache = NULL;
+
+ // Close the socket, if any
+ if(sock != 0)
+ closesocket(sock);
+ sock = 0;
+
+ // Free the lock
+ CascFreeLock(Lock);
+
+ // Free the socket itself
+ CASC_FREE(pThis);
+}
+
+//-----------------------------------------------------------------------------
+// The CASC_SOCKET_CACHE class
+
+CASC_SOCKET_CACHE::CASC_SOCKET_CACHE()
+{
+ pFirst = pLast = NULL;
+ dwRefCount = 0;
+}
+
+CASC_SOCKET_CACHE::~CASC_SOCKET_CACHE()
+{
+ PurgeAll();
+}
+
+PCASC_SOCKET CASC_SOCKET_CACHE::Find(const char * hostName, unsigned portNum)
+{
+ PCASC_SOCKET pSocket;
+
+ for(pSocket = pFirst; pSocket != NULL; pSocket = pSocket->pNext)
+ {
+ if(!_stricmp(pSocket->hostName, hostName) && (pSocket->portNum == portNum))
+ break;
+ }
+
+ return pSocket;
+}
+
+PCASC_SOCKET CASC_SOCKET_CACHE::InsertSocket(PCASC_SOCKET pSocket)
+{
+ if(pSocket != NULL && pSocket->pCache == NULL)
+ {
+ // Do we have caching turned on?
+ if(dwRefCount > 0)
+ {
+ // Insert one reference to the socket to mark it as cached
+ pSocket->AddRef();
+
+ // Insert the socket to the chain
+ if(pFirst == NULL && pLast == NULL)
+ {
+ pFirst = pLast = pSocket;
+ }
+ else
+ {
+ pSocket->pPrev = pLast;
+ pLast->pNext = pSocket;
+ pLast = pSocket;
+ }
+
+ // Mark the socket as cached
+ pSocket->pCache = this;
+ }
+ }
+
+ return pSocket;
+}
+
+void CASC_SOCKET_CACHE::UnlinkSocket(PCASC_SOCKET pSocket)
+{
+ // Only if it's a valid socket
+ if(pSocket != NULL)
+ {
+ // Check the first and the last items
+ if(pSocket == pFirst)
+ pFirst = pSocket->pNext;
+ if(pSocket == pLast)
+ pLast = pSocket->pPrev;
+
+ // Disconnect the socket from the chain
+ if(pSocket->pPrev != NULL)
+ pSocket->pPrev->pNext = pSocket->pNext;
+ if(pSocket->pNext != NULL)
+ pSocket->pNext->pPrev = pSocket->pPrev;
+ }
+}
+
+void CASC_SOCKET_CACHE::SetCaching(bool bAddRef)
+{
+ PCASC_SOCKET pSocket;
+ PCASC_SOCKET pNext;
+
+ // We need to increment reference count for each enabled caching
+ if(bAddRef)
+ {
+ // Add one reference to all currently held sockets
+ if(dwRefCount == 0)
+ {
+ for(pSocket = pFirst; pSocket != NULL; pSocket = pSocket->pNext)
+ pSocket->AddRef();
+ }
+
+ // Increment of references for the future sockets
+ CascInterlockedIncrement(&dwRefCount);
+ }
+ else
+ {
+ // Sanity check for multiple calls to dereference
+ assert(dwRefCount > 0);
+
+ // Dereference the reference count. If drops to zero, dereference all sockets as well
+ if(CascInterlockedDecrement(&dwRefCount) == 0)
+ {
+ for(pSocket = pFirst; pSocket != NULL; pSocket = pNext)
+ {
+ pNext = pSocket->pNext;
+ pSocket->Release();
+ }
+ }
+ }
+}
+
+void CASC_SOCKET_CACHE::PurgeAll()
+{
+ PCASC_SOCKET pSocket;
+ PCASC_SOCKET pNext;
+
+ // Dereference all current sockets
+ for(pSocket = pFirst; pSocket != NULL; pSocket = pNext)
+ {
+ pNext = pSocket->pNext;
+ pSocket->Delete();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+PCASC_SOCKET sockets_connect(const char * hostName, unsigned portNum)
+{
+ PCASC_SOCKET pSocket;
+
+ // Try to find the item in the cache
+ if((pSocket = SocketCache.Find(hostName, portNum)) != NULL)
+ {
+ pSocket->AddRef();
+ }
+ else
+ {
+ // Create new socket and connect it to the remote host
+ pSocket = CASC_SOCKET::Connect(hostName, portNum);
+
+ // Insert it to the cache, if it's a HTTP connection
+ if(pSocket->portNum == CASC_PORT_HTTP)
+ pSocket = SocketCache.InsertSocket(pSocket);
+ }
+
+ return pSocket;
+}
+
+void sockets_set_caching(bool caching)
+{
+ SocketCache.SetCaching(caching);
+}
diff --git a/dep/CascLib/src/common/Sockets.h b/dep/CascLib/src/common/Sockets.h
new file mode 100644
index 00000000000..5d1aa677f85
--- /dev/null
+++ b/dep/CascLib/src/common/Sockets.h
@@ -0,0 +1,115 @@
+/*****************************************************************************/
+/* Sockets.h Copyright (c) Ladislav Zezula 2021 */
+/*---------------------------------------------------------------------------*/
+/* MIME parsing functions for CascLib */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 13.02.21 1.00 Lad Created */
+/*****************************************************************************/
+
+#ifndef __SOCKET_H__
+#define __SOCKET_H__
+
+//-----------------------------------------------------------------------------
+// Defines
+
+#ifndef INVALID_SOCKET
+#define INVALID_SOCKET (SOCKET)(-1)
+#endif
+
+#ifndef SOCKET_ERROR
+#define SOCKET_ERROR (-1)
+#endif
+
+#ifndef MSG_NOSIGNAL
+#define MSG_NOSIGNAL 0
+#endif
+
+#ifndef WSAECONNRESET
+#define WSAECONNRESET 10054L
+#endif
+
+#ifndef EPIPE
+#define EPIPE 32
+#endif
+
+#define CASC_PORT_HTTP 80
+#define CASC_PORT_RIBBIT 1119
+
+//-----------------------------------------------------------------------------
+// The CASC_SOCKET class
+
+typedef class CASC_SOCKET_CACHE * PCASC_SOCKET_CACHE;
+typedef class CASC_SOCKET * PCASC_SOCKET;
+typedef struct addrinfo * PADDRINFO;
+
+class CASC_SOCKET
+{
+ public:
+
+ char * ReadResponse(const char * request, size_t request_length = 0, size_t * PtrLength = NULL);
+ DWORD AddRef();
+ void Release();
+
+ private:
+
+ // Constructor and destructor
+ static int GetSockError();
+ static DWORD GetAddrInfoWrapper(const char * hostName, unsigned portNum, PADDRINFO hints, PADDRINFO * ppResult);
+ static SOCKET CreateAndConnect(addrinfo * remoteItem);
+ static SOCKET ReconnectAfterShutdown(SOCKET & sock, addrinfo * remoteItem);
+ static PCASC_SOCKET New(addrinfo * remoteList, addrinfo * remoteItem, const char * hostName, unsigned portNum, SOCKET sock);
+ static PCASC_SOCKET Connect(const char * hostName, unsigned portNum);
+
+ // Frees all resources and deletes the socket
+ void Delete();
+
+ // Entities allowed to manipulate with the class
+ friend CASC_SOCKET * sockets_connect(const char * hostName, unsigned portNum);
+ friend char * sockets_read_response(PCASC_SOCKET pSocket, const char * request, size_t request_length, size_t * PtrLength);
+ friend class CASC_SOCKET_CACHE;
+
+ PCASC_SOCKET_CACHE pCache; // Pointer to the cache. If NULL, the socket is not cached
+ PCASC_SOCKET pPrev; // Pointer to the prev socket in the list
+ PCASC_SOCKET pNext; // Pointer to the next socket in the list
+ PADDRINFO remoteList; // List of the remote host informations
+ PADDRINFO remoteItem; // The particular host picked during the last connection attempt
+ CASC_LOCK Lock; // Lock for single threaded access
+ SOCKET sock; // Opened and connected socket
+ DWORD dwRefCount; // Number of references
+ DWORD portNum; // Port number
+ char hostName[1]; // Buffer for storing remote host (variable length)
+};
+
+//-----------------------------------------------------------------------------
+// Socket cache class
+
+class CASC_SOCKET_CACHE
+{
+ public:
+
+ CASC_SOCKET_CACHE();
+ ~CASC_SOCKET_CACHE();
+
+ PCASC_SOCKET Find(const char * hostName, unsigned portNum);
+ PCASC_SOCKET InsertSocket(PCASC_SOCKET pSocket);
+ void UnlinkSocket(PCASC_SOCKET pSocket);
+
+ void SetCaching(bool bAddRef);
+ void PurgeAll();
+
+ private:
+
+ PCASC_SOCKET pFirst;
+ PCASC_SOCKET pLast;
+ DWORD dwRefCount;
+};
+
+//-----------------------------------------------------------------------------
+// Public functions
+
+PCASC_SOCKET sockets_connect(const char * hostName, unsigned portNum);
+void sockets_set_caching(bool caching);
+
+#endif // __SOCKET_H__
diff --git a/dep/PackageList.txt b/dep/PackageList.txt
index b4dc2b760c0..a6769d98e7c 100644
--- a/dep/PackageList.txt
+++ b/dep/PackageList.txt
@@ -58,7 +58,7 @@ recastnavigation (Recast is state of the art navigation mesh construction toolse
CascLib (An open-source implementation of library for reading CASC storage from Blizzard games since 2014)
https://github.com/ladislav-zezula/CascLib
- Version: 737a8705b5b8f7ce3917f5d5ff9767b18de1285e
+ Version: 37a948bdb5f493b6a0959489baa07e1636002c3b
rapidjson (A fast JSON parser/generator for C++ with both SAX/DOM style API http://rapidjson.org/)
https://github.com/miloyip/rapidjson