23#include <boost/asio/streambuf.hpp>
24#include <boost/asio/read.hpp>
25#include <boost/asio/read_until.hpp>
26#include <boost/asio/write.hpp>
27#include <boost/asio/ssl/stream.hpp>
28#include <boost/filesystem/operations.hpp>
35 case ERROR_SUCCESS:
return "SUCCESS";
36 case ERROR_FILE_CORRUPT:
return "FILE_CORRUPT";
37 case ERROR_CAN_NOT_COMPLETE:
return "CAN_NOT_COMPLETE";
38 case ERROR_HANDLE_EOF:
return "HANDLE_EOF";
39 case ERROR_NO_MORE_FILES:
return "NO_MORE_FILES";
40 case ERROR_BAD_FORMAT:
return "BAD_FORMAT";
41 case ERROR_INSUFFICIENT_BUFFER:
return "INSUFFICIENT_BUFFER";
42 case ERROR_ALREADY_EXISTS:
return "ALREADY_EXISTS";
43 case ERROR_DISK_FULL:
return "DISK_FULL";
44 case ERROR_INVALID_PARAMETER:
return "INVALID_PARAMETER";
45 case ERROR_NOT_SUPPORTED:
return "NOT_SUPPORTED";
46 case ERROR_NOT_ENOUGH_MEMORY:
return "NOT_ENOUGH_MEMORY";
47 case ERROR_INVALID_HANDLE:
return "INVALID_HANDLE";
48 case ERROR_ACCESS_DENIED:
return "ACCESS_DENIED";
49 case ERROR_FILE_NOT_FOUND:
return "FILE_NOT_FOUND";
50 case ERROR_FILE_ENCRYPTED:
return "FILE_ENCRYPTED";
51 case ERROR_FILE_OFFLINE:
return "FILE_OFFLINE";
52 default:
return "UNKNOWN";
60 boost::system::error_code error;
62 boost::asio::ssl::context sslContext(boost::asio::ssl::context::sslv23);
63 sslContext.set_options(boost::asio::ssl::context::no_sslv2, error);
64 sslContext.set_options(boost::asio::ssl::context::no_sslv3, error);
65 sslContext.set_options(boost::asio::ssl::context::no_tlsv1, error);
66 sslContext.set_options(boost::asio::ssl::context::no_tlsv1_1, error);
67 sslContext.set_default_verify_paths(error);
75 boost::asio::ssl::stream<Trinity::Net::IoContextTcpSocket> socket(ioContext, sslContext);
76 socket.set_verify_mode(boost::asio::ssl::verify_none, error);
80 socket.lowest_layer().connect(*endpoint, error);
84 if (!SSL_set_tlsext_host_name(socket.native_handle(), serverName.c_str()))
87 socket.handshake(boost::asio::ssl::stream_base::client, error);
91 boost::asio::streambuf request;
92 std::ostream request_stream(&request);
94 request_stream <<
"GET " << getCommand <<
" HTTP/1.0\r\n";
95 request_stream <<
"Host: " << serverName <<
"\r\n";
96 request_stream <<
"Connection: close\r\n\r\n";
99 boost::asio::write(socket, request);
102 boost::asio::streambuf response;
103 boost::asio::read_until(socket, response,
"\r\n", error);
106 printf(
"Downloading tact key list failed to read HTTP response status %s", error.message().c_str());
111 std::string http_version;
113 std::string status_message;
114 std::istream response_stream(&response);
116 response_stream >> http_version;
117 response_stream >> status_code;
118 std::getline(response_stream, status_message);
120 if (status_code != 200)
122 printf(
"Downloading tact key list failed with server response %u %s", status_code, status_message.c_str());
127 boost::asio::read_until(socket, response,
"\r\n\r\n");
130 printf(
"Downloading tact key list failed to read HTTP response headers %s", error.message().c_str());
136 while (std::getline(response_stream, header) && header !=
"\r")
140 std::stringstream rawBody;
143 if (response.size() > 0)
144 rawBody << &response;
147 while (boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error))
148 rawBody << &response;
150 return rawBody.str();
154 bool GetStorageInfo(HANDLE storage, CASC_STORAGE_INFO_CLASS storageInfoClass, T* value)
156 size_t infoDataSizeNeeded = 0;
157 return ::CascGetStorageInfo(storage, storageInfoClass, value,
sizeof(T), &infoDataSizeNeeded);
163using CASCCharType = std::remove_const_t<std::remove_pointer_t<
decltype(CASC_OPEN_STORAGE_ARGS::szLocalPath)>>;
173 static Optional<std::string> const tactKeys = DownloadFile(
"raw.githubusercontent.com", 443,
"/wowdev/TACTKeys/master/WoW.txt");
175 return tactKeys && CascImportKeysFromString(
_handle, tactKeys->c_str());
187 CASC_OPEN_STORAGE_ARGS args = {};
188 args.Size =
sizeof(CASC_OPEN_STORAGE_ARGS);
189 args.szLocalPath = strPath.c_str();
190 args.szCodeName = strProduct.c_str();
191 args.dwLocaleMask = localeMask;
192 HANDLE handle =
nullptr;
193 if (!CascOpenStorageEx(
nullptr, &args,
false, &handle))
195 DWORD lastError = GetCascError();
196 printf(
"Error opening casc storage '%s': %s\n", path.string().c_str(),
HumanReadableCASCError(lastError));
197 CascCloseStorage(handle);
198 SetCascError(lastError);
202 printf(
"Opened casc storage '%s'\n", path.string().c_str());
206 printf(
"Failed to load additional online encryption keys, some files might not be extracted.\n");
216 CASC_OPEN_STORAGE_ARGS args = {};
217 args.Size =
sizeof(CASC_OPEN_STORAGE_ARGS);
218 args.szLocalPath = strPath.c_str();
219 args.szCodeName = strProduct.c_str();
220 args.szRegion = strRegion.c_str();
221 args.dwLocaleMask = localeMask;
223 HANDLE handle =
nullptr;
224 if (!::CascOpenStorageEx(
nullptr, &args,
true, &handle))
226 DWORD lastError = GetCascError();
228 CascCloseStorage(handle);
229 SetCascError(lastError);
234 if (!GetStorageInfo(handle, CascStorageFeatures, &features) || !(features & CASC_FEATURE_ONLINE))
236 printf(
"Local casc storage detected in cache path \"%s\" (or its parent directory). Remote storage not opened!\n", path.string().c_str());
237 CascCloseStorage(handle);
238 SetCascError(ERROR_FILE_OFFLINE);
242 printf(
"Opened remote casc storage '%s'\n", path.string().c_str());
246 printf(
"Failed to load additional online encryption keys, some files might not be extracted.\n");
253 CASC_STORAGE_PRODUCT product;
254 if (GetStorageInfo(
_handle, CascStorageProduct, &product))
255 return product.BuildNumber;
263 if (GetStorageInfo(
_handle, CascStorageInstalledLocales, &locales))
271 return CascFindEncryptionKey(
_handle, keyLookup) !=
nullptr;
276 DWORD openFlags = CASC_OPEN_BY_NAME;
277 if (zerofillEncryptedParts)
278 openFlags |= CASC_OVERCOME_ENCRYPTED;
280 HANDLE handle =
nullptr;
281 if (!::CascOpenFile(
_handle, fileName, localeMask, openFlags, &handle))
283 DWORD lastError = GetCascError();
285 fprintf(stderr,
"Failed to open '%s' in CASC storage: %s\n", fileName,
HumanReadableCASCError(lastError));
287 CascCloseFile(handle);
288 SetCascError(lastError);
292 return new File(handle);
297 DWORD openFlags = CASC_OPEN_BY_FILEID;
298 if (zerofillEncryptedParts)
299 openFlags |= CASC_OVERCOME_ENCRYPTED;
301 HANDLE handle =
nullptr;
302 if (!::CascOpenFile(
_handle, CASC_FILE_DATA_ID(fileDataId), localeMask, openFlags, &handle))
304 DWORD lastError = GetCascError();
306 fprintf(stderr,
"Failed to open 'FileDataId %u' in CASC storage: %s\n", fileDataId,
HumanReadableCASCError(lastError));
308 CascCloseFile(handle);
309 SetCascError(lastError);
313 return new File(handle);
327 CASC_FILE_FULL_INFO info;
328 if (!::CascGetFileInfo(
_handle, CascFileFullInfo, &info,
sizeof(info),
nullptr))
329 return CASC_INVALID_ID;
331 return info.FileDataId;
337 if (!::CascGetFileSize64(
_handle, &size))
346 if (!::CascSetFilePointer64(
_handle, 0, &position, FILE_CURRENT))
349 return int64(position);
355 memcpy(parts, &position,
sizeof(parts));
356 return ::CascSetFilePointer64(
_handle, position,
nullptr, FILE_BEGIN);
361 DWORD bytesReadDWORD;
362 if (!::CascReadFile(
_handle, buffer, bytes, &bytesReadDWORD))
366 *bytesRead = bytesReadDWORD;
std::optional< T > Optional
Optional helper class to wrap optional values within.
bool SetPointer(int64 position)
bool ReadFile(void *buffer, uint32 bytes, uint32 *bytesRead)
static Storage * Open(boost::filesystem::path const &path, uint32 localeMask, char const *product)
static Storage * OpenRemote(boost::filesystem::path const &path, uint32 localeMask, char const *product, char const *region)
File * OpenFile(char const *fileName, uint32 localeMask, bool printErrors=false, bool zerofillEncryptedParts=false) const
bool HasTactKey(uint64 keyLookup) const
uint32 GetBuildNumber() const
uint32 GetInstalledLocalesMask() const
bool LoadOnlineTactKeys()
std::remove_const_t< std::remove_pointer_t< decltype(CASC_OPEN_STORAGE_ARGS::szLocalPath)> > CASCCharType
char const * HumanReadableCASCError(uint32 error)
std::basic_string< CASCCharType > CASCStringType