initial commit
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
add_executable(sgutil
|
||||
sgu_main.cpp
|
||||
sgu_stdafx.h
|
||||
sgu_arg_opt.h
|
||||
sgu_download_ant.cpp
|
||||
sgu_download_ant.h
|
||||
sgu_download_cos.cpp
|
||||
sgu_download_cos.h
|
||||
sgu_mode_apply_patch.cpp
|
||||
sgu_mode_apply_patch.h
|
||||
sgu_mode_download.cpp
|
||||
sgu_mode_download.h
|
||||
sgu_mode_gen_rand_file.cpp
|
||||
sgu_mode_gen_rand_file.h
|
||||
sgu_mode_hash_file.cpp
|
||||
sgu_mode_hash_file.h
|
||||
sgu_mode_make_diff.cpp
|
||||
sgu_mode_make_diff.h
|
||||
sgu_mode_rand_insert.cpp
|
||||
sgu_mode_rand_insert.h
|
||||
sgu_mode_rand_modify.cpp
|
||||
sgu_mode_rand_modify.h
|
||||
sgu_mode_rand_remove.cpp
|
||||
sgu_mode_rand_remove.h
|
||||
sgu_mode_npk_dump_list.h
|
||||
sgu_mode_npk_dump_list.cpp
|
||||
sgu_mode_npk_extract_files.h
|
||||
sgu_mode_npk_extract_files.cpp
|
||||
sgu_utils.cpp
|
||||
sgu_utils.h
|
||||
)
|
||||
|
||||
target_include_directories(sgutil
|
||||
PRIVATE
|
||||
../bsdiff
|
||||
../bzip2
|
||||
../cos-cpp-sdk/include
|
||||
../cos-cpp-sdk/third_party/include
|
||||
../libnpk
|
||||
../libTinyEncrypt
|
||||
)
|
||||
|
||||
target_link_directories(sgutil
|
||||
PRIVATE
|
||||
../cos-cpp-sdk/libs/x64
|
||||
../cos-cpp-sdk/third_party/lib/x64/poco
|
||||
)
|
||||
|
||||
target_link_libraries(sgutil
|
||||
PRIVATE
|
||||
libbsdiff
|
||||
bzip2
|
||||
libNPK
|
||||
libTinyEncrypt
|
||||
shlwapi.lib
|
||||
wininet.lib
|
||||
# cossdk.lib
|
||||
# PocoFoundation.lib
|
||||
)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,650 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_download_ant.h"
|
||||
#include "sgu_utils.h"
|
||||
|
||||
#include "libEncrypt_MD5.h"
|
||||
|
||||
#define debugLog(...) __noop
|
||||
|
||||
bool parseURL(const wchar_t* pstrURL, INTERNET_SCHEME& schemeType, wchar_t* strServer, wchar_t* strObject, INTERNET_PORT& nPort);
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
CDownloadAnt::CDownloadAnt()
|
||||
{
|
||||
m_hThread = 0;
|
||||
|
||||
m_theStateCategory = IDLE;
|
||||
m_theState = IDLE_NOTINIT;
|
||||
|
||||
m_wszURLToDownload[0] = 0;
|
||||
m_wszFileToDownloadInto[0] = 0;
|
||||
|
||||
m_pOwner = NULL;
|
||||
|
||||
m_schemeType = INTERNET_SCHEME_UNKNOWN;
|
||||
m_wszServer[0] = 0;
|
||||
m_wszObject[0] = 0;
|
||||
m_nPort = 80;
|
||||
|
||||
m_hInternetSession = NULL;
|
||||
m_hHttpConnection = NULL;
|
||||
m_hHttpFile = NULL;
|
||||
|
||||
m_dwUserCmd = MANAGER_CMD_CONTINUE;
|
||||
m_nOverTime = INFINITE;
|
||||
|
||||
m_bGotFileSize = FALSE;
|
||||
m_dwFileSizeTotal = 0;
|
||||
m_dwFileSizeRead = 0;
|
||||
m_fpFileToWrite = NULL;
|
||||
|
||||
UserCallBack = NULL;
|
||||
|
||||
m_wszStatusInfor[0] = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
CDownloadAnt::~CDownloadAnt()
|
||||
{
|
||||
//关闭
|
||||
UserCallBack = NULL;
|
||||
if (m_hHttpFile)
|
||||
{
|
||||
::InternetCloseHandle(m_hHttpFile);
|
||||
m_hHttpFile = NULL;
|
||||
}
|
||||
if (m_hHttpConnection)
|
||||
{
|
||||
::InternetCloseHandle(m_hHttpConnection);
|
||||
m_hHttpConnection = NULL;
|
||||
}
|
||||
if (m_hInternetSession)
|
||||
{
|
||||
::InternetCloseHandle(m_hInternetSession);
|
||||
m_hInternetSession = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
bool CDownloadAnt::init(const wchar_t* wszURLToDownload, const wchar_t* wszFileToDownloadInto,
|
||||
DOWNANT_STATUS_CALLBACK funcUserCallBack, void* pOwner, unsigned int nOverTime)
|
||||
{
|
||||
assert(m_theStateCategory != BUSY);
|
||||
assert(wszURLToDownload && wszFileToDownloadInto);
|
||||
|
||||
StringCchCopyW(m_wszURLToDownload, INTERNET_MAX_URL_LENGTH, wszURLToDownload);
|
||||
StringCchCopyW(m_wszFileToDownloadInto, MAX_PATH, wszFileToDownloadInto);
|
||||
|
||||
debugLog(L"build ant download \"%s\" to \"%s\"", m_wszURLToDownload, m_wszFileToDownloadInto);
|
||||
|
||||
m_pOwner = pOwner;
|
||||
UserCallBack = funcUserCallBack;
|
||||
|
||||
if (wcslen(m_wszURLToDownload) > 0 && wcslen(m_wszFileToDownloadInto) > 0)
|
||||
{
|
||||
if (!parseURL(m_wszURLToDownload, m_schemeType, m_wszServer, m_wszObject, m_nPort) ||
|
||||
m_schemeType == INTERNET_SCHEME_UNKNOWN)
|
||||
{
|
||||
const wchar_t* wsz = L"Parser URL Error: %s";
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"Parser URL Error: %s", m_wszURLToDownload);
|
||||
setStatus(IDLE_INIT_FAILED);
|
||||
return FALSE;
|
||||
}
|
||||
setStatus(IDLE_INIT_SUCCESS);
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"url and local file address can't be empty!");
|
||||
setStatus(IDLE_INIT_FAILED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_nOverTime = nOverTime;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
void CDownloadAnt::begin(bool bBlock)
|
||||
{
|
||||
assert(m_theState == IDLE_INIT_SUCCESS);
|
||||
|
||||
UINT nThreadID;
|
||||
m_hThread = (HANDLE)::_beginthreadex(NULL, 0, _downloadThread, this, CREATE_SUSPENDED, &nThreadID);
|
||||
|
||||
if (m_hThread == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus(BUSY_BEGIN_THREAD);
|
||||
m_beginTime = ::GetTickCount64();
|
||||
ResumeThread(m_hThread);
|
||||
|
||||
if (bBlock)
|
||||
{
|
||||
::WaitForSingleObject(m_hThread, INFINITE);
|
||||
//关闭句柄
|
||||
CloseHandle(m_hThread); m_hThread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
UINT CDownloadAnt::_downloadThread(void* pParam)
|
||||
{
|
||||
CDownloadAnt* pAnt = (CDownloadAnt*)pParam;
|
||||
assert(pAnt);
|
||||
|
||||
try
|
||||
{
|
||||
pAnt->downloadThread();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
void CDownloadAnt::downloadThread(void)
|
||||
{
|
||||
assert(m_theState == BUSY_BEGIN_THREAD);
|
||||
assert(m_hInternetSession == NULL);
|
||||
assert(m_fpFileToWrite == NULL);
|
||||
assert(m_hHttpConnection == NULL);
|
||||
assert(m_hHttpFile == NULL);
|
||||
|
||||
_wfopen_s(&m_fpFileToWrite, m_wszFileToDownloadInto, L"wb");
|
||||
if (m_fpFileToWrite == NULL)
|
||||
{
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"Create local file error: %s, Error:%d", m_wszFileToDownloadInto, ::GetLastError());
|
||||
setStatus(FINISH_ERROR_TRY_OPEN_LOCALFILE);
|
||||
return;
|
||||
}
|
||||
|
||||
//打开Internet句柄
|
||||
m_hInternetSession = InternetOpenW(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
|
||||
if (m_hInternetSession == NULL)
|
||||
{
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"(::InternetOpen) Error:%d\n", ::GetLastError());
|
||||
setStatus(FINISH_ERROR_INTERNET_OPEN);
|
||||
return;
|
||||
}
|
||||
if (_checkAbort()) return;
|
||||
|
||||
DWORD dwTemp = 0, dwSize = sizeof(DWORD);
|
||||
BOOL bRet = InternetQueryOptionW(m_hInternetSession, INTERNET_OPTION_MAX_CONNS_PER_SERVER, &dwTemp, &dwSize);
|
||||
DWORD dwErr = GetLastError();
|
||||
|
||||
//设置状态回调函数
|
||||
if (::InternetSetStatusCallbackW(m_hInternetSession, _onWininetStatusCallBack) == INTERNET_INVALID_STATUS_CALLBACK)
|
||||
{
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"(::InternetSetStatusCallback), Error:%d\n", ::GetLastError());
|
||||
setStatus(FINISH_ERROR_SETSTATUSCALLBACK);
|
||||
return;
|
||||
}
|
||||
if (_checkAbort()) return;
|
||||
|
||||
DWORD serviceType = 0;
|
||||
if(m_schemeType == INTERNET_SCHEME_HTTP||m_schemeType== INTERNET_SCHEME_HTTPS)
|
||||
{
|
||||
serviceType = INTERNET_SERVICE_HTTP;
|
||||
}
|
||||
else if (m_schemeType == INTERNET_SCHEME_FTP)
|
||||
{
|
||||
serviceType = INTERNET_SERVICE_FTP;
|
||||
}
|
||||
else
|
||||
{
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"Unsupported scheme type: %d", m_schemeType);
|
||||
setStatus(FINISH_ERROR_UNSUPPORTED_SCHEME);
|
||||
return;
|
||||
}
|
||||
|
||||
//打开Internet连接
|
||||
m_hHttpConnection = InternetConnectW(m_hInternetSession, m_wszServer, m_nPort, NULL,
|
||||
NULL, serviceType, 0, (DWORD_PTR)this);
|
||||
|
||||
if (m_hHttpConnection == NULL)
|
||||
{
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"(::InternetConnect), Error:%d\n", ::GetLastError());
|
||||
setStatus(FINISH_ERROR_INTERNETCONNECT);
|
||||
return;
|
||||
}
|
||||
if (_checkAbort()) return;
|
||||
|
||||
//生成Request
|
||||
const wchar_t* ppszAcceptTypes[2];
|
||||
ppszAcceptTypes[0] = L"*/*";
|
||||
ppszAcceptTypes[1] = NULL;
|
||||
|
||||
DWORD dwFlags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES;
|
||||
if(serviceType==INTERNET_SERVICE_HTTP && m_schemeType == INTERNET_SCHEME_HTTPS)
|
||||
{
|
||||
dwFlags |= INTERNET_FLAG_SECURE;
|
||||
}
|
||||
|
||||
m_hHttpFile = HttpOpenRequestW(m_hHttpConnection,
|
||||
L"GET", m_wszObject, NULL, NULL, ppszAcceptTypes,
|
||||
dwFlags,
|
||||
(DWORD_PTR)this);
|
||||
if (m_hHttpFile == NULL)
|
||||
{
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"(::HttpOpenRequest), Error:%d\n", ::GetLastError());
|
||||
setStatus(FINISH_ERROR_OPENREQUEST);
|
||||
return;
|
||||
}
|
||||
if (_checkAbort()) return;
|
||||
setStatus(BUSY_OPEN_REQUEST);
|
||||
|
||||
//发送Request
|
||||
BOOL bSend = HttpSendRequestW(m_hHttpFile, NULL, 0, NULL, 0);
|
||||
if (!bSend)
|
||||
{
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"(::HttpSendRequest), Error:%d\n", ::GetLastError());
|
||||
setStatus(FINISH_ERROR_SENDREQUEST);
|
||||
return;
|
||||
}
|
||||
if (_checkAbort()) return;
|
||||
|
||||
//查询状态
|
||||
DWORD dwInfoSize = 64;
|
||||
CHAR szStatusCode[64] = { 0 };
|
||||
if (!::HttpQueryInfoW(m_hHttpFile, HTTP_QUERY_STATUS_CODE, szStatusCode, &dwInfoSize, NULL))
|
||||
{
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"Failed in call to HttpQueryInfo for HTTP query status code, Error:%d\n", ::GetLastError());
|
||||
setStatus(FINISH_ERROR_QUERYINFO);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
long nStatusCode = atoi(szStatusCode);
|
||||
|
||||
if (nStatusCode != HTTP_STATUS_OK)
|
||||
{
|
||||
m_theState = FINISH_ERROR_INVALID_HTTP_RESPONSE;
|
||||
|
||||
if (nStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
|
||||
{
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"Authentication errors, Status Code:%d\n", nStatusCode);
|
||||
setStatus(FINISH_ERROR_NEED_PROXY_AUTH_REQ);
|
||||
return;
|
||||
}
|
||||
else if (nStatusCode == HTTP_STATUS_DENIED)
|
||||
{
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"Authentication errors, Status Code:%d\n", nStatusCode);
|
||||
setStatus(FINISH_ERROR_DENIED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_checkAbort()) return;
|
||||
|
||||
// 取得文件大小
|
||||
dwInfoSize = 64;
|
||||
wchar_t wszContentLength[64] = { 0 };
|
||||
if (HttpQueryInfoW(m_hHttpFile, HTTP_QUERY_CONTENT_LENGTH, wszContentLength, &dwInfoSize, NULL))
|
||||
{
|
||||
m_bGotFileSize = TRUE;
|
||||
m_dwFileSizeTotal = _wtoi64(wszContentLength);
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"FileSize: %I64d", m_dwFileSizeTotal);
|
||||
}
|
||||
|
||||
debugLog(L"Get file size %I64d, begin download...", m_dwFileSizeTotal);
|
||||
//得到文件大小,让Manager处理位置
|
||||
setStatus(BUSY_GET_FILE_INFORMATION);
|
||||
if (_checkAbort()) return;
|
||||
|
||||
//开始下载
|
||||
ULONGLONG startTicks = ::GetTickCount64();
|
||||
DWORD dwBytesRead = 0;
|
||||
char szReadBuf[DOWNANT_MINSIZE] = { 0 };
|
||||
DWORD dwBytesToRead = DOWNANT_MINSIZE;
|
||||
m_dwFileSizeRead = 0;
|
||||
|
||||
MD5Context md5Ctx;
|
||||
md5Init(&md5Ctx);
|
||||
|
||||
setStatus(BUSY_BEGIN_DOWNFILE);
|
||||
do
|
||||
{
|
||||
if (!InternetReadFile(m_hHttpFile, szReadBuf, dwBytesToRead, &dwBytesRead))
|
||||
{
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"(::InternetReadFile), Error:%d\n", ::GetLastError());
|
||||
setStatus(FINISH_ERROR_READFILE);
|
||||
return;
|
||||
}
|
||||
else if (dwBytesRead && m_dwUserCmd == MANAGER_CMD_CONTINUE)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (dwBytesRead != ::fwrite(szReadBuf, 1, dwBytesRead, m_fpFileToWrite))
|
||||
{
|
||||
throw FINISH_ERROR_EXCEPTION;
|
||||
}
|
||||
|
||||
md5Update(&md5Ctx, (unsigned char*)szReadBuf, dwBytesRead);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
StringCchPrintfW(m_wszStatusInfor, MAX_PATH, L"write local file error!, Error:%d", ::GetLastError());
|
||||
setStatus(FINISH_ERROR_EXCEPTION);
|
||||
return;
|
||||
}
|
||||
|
||||
m_dwFileSizeRead += dwBytesRead;
|
||||
onStatusCallback();
|
||||
}
|
||||
|
||||
if (_checkAbort())
|
||||
{
|
||||
if (m_fpFileToWrite)::fclose(m_fpFileToWrite);
|
||||
m_fpFileToWrite = NULL;
|
||||
return;
|
||||
}
|
||||
} while (dwBytesRead && m_dwUserCmd == MANAGER_CMD_CONTINUE);
|
||||
|
||||
//生成MD5
|
||||
m_fileMD5 = md5Finalize(&md5Ctx);
|
||||
|
||||
if (m_fpFileToWrite)::fclose(m_fpFileToWrite);
|
||||
m_fpFileToWrite = NULL;
|
||||
|
||||
debugLog(L"Download success over!");
|
||||
|
||||
setStatus(FINISH_SUCCESS_OVER);
|
||||
return;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
bool CDownloadAnt::checkMD5(const char* md5_string) const
|
||||
{
|
||||
MD5Digest md5;
|
||||
if (!string2MD5(md5_string, md5)) return false;
|
||||
|
||||
return (0 == memcmp(&md5, &m_fileMD5, md5.size()));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
void CALLBACK CDownloadAnt::_onWininetStatusCallBack(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
|
||||
LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
|
||||
{
|
||||
CDownloadAnt* pDownMan = (CDownloadAnt*)dwContext;
|
||||
assert(pDownMan);
|
||||
|
||||
pDownMan->onWininetStatusCallBack(hInternet, dwInternetStatus, lpvStatusInformation, dwStatusInformationLength);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
void CDownloadAnt::onWininetStatusCallBack(HINTERNET /*hInternet*/, DWORD dwInternetStatus,
|
||||
LPVOID lpvStatusInformation, DWORD /*dwStatusInformationLength*/)
|
||||
{
|
||||
switch (dwInternetStatus)
|
||||
{
|
||||
case INTERNET_STATUS_RESOLVING_NAME:
|
||||
{
|
||||
StringCchCopyW(m_wszStatusInfor, MAX_PATH, (LPCWSTR)lpvStatusInformation);
|
||||
setStatus(BUSY_RESOLVING_NAME);
|
||||
break;
|
||||
}
|
||||
case INTERNET_STATUS_NAME_RESOLVED:
|
||||
{
|
||||
StringCchCopyW(m_wszStatusInfor, MAX_PATH, (LPCWSTR)lpvStatusInformation);
|
||||
setStatus(BUSY_RESOLVED_NAME);
|
||||
break;
|
||||
}
|
||||
case INTERNET_STATUS_CONNECTING_TO_SERVER:
|
||||
{
|
||||
StringCchCopyW(m_wszStatusInfor, MAX_PATH, (LPCWSTR)lpvStatusInformation);
|
||||
setStatus(BUSY_CONNECTING);
|
||||
break;
|
||||
}
|
||||
case INTERNET_STATUS_CONNECTED_TO_SERVER:
|
||||
{
|
||||
StringCchCopyW(m_wszStatusInfor, MAX_PATH, (LPCWSTR)lpvStatusInformation);
|
||||
setStatus(BUSY_CONNECTED);
|
||||
break;
|
||||
}
|
||||
case INTERNET_STATUS_REDIRECT:
|
||||
{
|
||||
StringCchCopyW(m_wszStatusInfor, MAX_PATH, (LPCWSTR)lpvStatusInformation);
|
||||
setStatus(BUSY_REDIRECTING);
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
void CDownloadAnt::setStatus(CDownloadAnt::DOWN_STATE newState)
|
||||
{
|
||||
m_theState = newState;
|
||||
onStatusCallback();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
void CDownloadAnt::onStatusCallback(void)
|
||||
{
|
||||
switch (m_theState)
|
||||
{
|
||||
case IDLE_NOTINIT:
|
||||
case IDLE_INIT_SUCCESS:
|
||||
case IDLE_INIT_FAILED:
|
||||
{
|
||||
m_theStateCategory = IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case BUSY_BEGIN_THREAD:
|
||||
case BUSY_OPEN_REQUEST:
|
||||
case BUSY_RESOLVING_NAME:
|
||||
case BUSY_RESOLVED_NAME:
|
||||
case BUSY_CONNECTING:
|
||||
case BUSY_CONNECTED:
|
||||
case BUSY_REDIRECTING:
|
||||
case BUSY_GET_FILE_INFORMATION:
|
||||
case BUSY_BEGIN_DOWNFILE:
|
||||
{
|
||||
m_theStateCategory = BUSY;
|
||||
}
|
||||
break;
|
||||
|
||||
case FINISH_SUCCESS_OVER:
|
||||
case FINISH_SUCCESS_STOPHERE:
|
||||
{
|
||||
m_theStateCategory = FINISH_SUCCESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case FINISH_FORCEABORT:
|
||||
case FINISH_ERROR_TIMEOUT:
|
||||
case FINISH_ERROR_TRY_OPEN_LOCALFILE:
|
||||
case FINISH_ERROR_INTERNET_OPEN:
|
||||
case FINISH_ERROR_SETSTATUSCALLBACK:
|
||||
case FINISH_ERROR_INTERNETCONNECT:
|
||||
case FINISH_ERROR_OPENREQUEST:
|
||||
case FINISH_ERROR_SENDREQUEST:
|
||||
case FINISH_ERROR_QUERYINFO:
|
||||
case FINISH_ERROR_NEED_PROXY_AUTH_REQ:
|
||||
case FINISH_ERROR_DENIED:
|
||||
case FINISH_ERROR_INVALID_HTTP_RESPONSE:
|
||||
case FINISH_ERROR_READFILE:
|
||||
case FINISH_ERROR_EXCEPTION:
|
||||
{
|
||||
m_theStateCategory = FINISH_NOT_SUCCESS;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_theStateCategory == FINISH_SUCCESS || m_theStateCategory == FINISH_NOT_SUCCESS)
|
||||
{
|
||||
if (m_fpFileToWrite)
|
||||
{
|
||||
::fclose(m_fpFileToWrite);
|
||||
m_fpFileToWrite = NULL;
|
||||
}
|
||||
|
||||
if (m_hHttpFile)
|
||||
{
|
||||
::InternetCloseHandle(m_hHttpFile);
|
||||
m_hHttpFile = NULL;
|
||||
}
|
||||
if (m_hHttpConnection)
|
||||
{
|
||||
::InternetCloseHandle(m_hHttpConnection);
|
||||
m_hHttpConnection = NULL;
|
||||
}
|
||||
if (m_hInternetSession)
|
||||
{
|
||||
::InternetCloseHandle(m_hInternetSession);
|
||||
m_hInternetSession = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (UserCallBack)
|
||||
{
|
||||
m_dwUserCmd = UserCallBack(m_pOwner, this);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------------
|
||||
BOOL CDownloadAnt::_checkAbort(void)
|
||||
{
|
||||
HANDLE hCloseMutex = OpenMutexW(SYNCHRONIZE, FALSE, L"Global\\8e931dcb-868e-4ba6-aff4-d1a22bc9d476");
|
||||
if (hCloseMutex != 0)
|
||||
{
|
||||
CloseHandle(hCloseMutex);
|
||||
debugLog(L"[ANT]Receive autoclose signal, quit!");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (MANAGER_CMD_FORCEABORT == m_dwUserCmd)
|
||||
{
|
||||
setStatus(FINISH_FORCEABORT);
|
||||
return TRUE;
|
||||
}
|
||||
if (MANAGER_CMD_STOPHERE == m_dwUserCmd)
|
||||
{
|
||||
setStatus(FINISH_SUCCESS_STOPHERE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (m_nOverTime != INFINITE && m_theStateCategory == BUSY)
|
||||
{
|
||||
ULONGLONG timeNow = ::GetTickCount64();
|
||||
ULONGLONG timeRun;
|
||||
if (timeNow >= m_beginTime) timeRun = timeNow - m_beginTime;
|
||||
else timeRun = timeNow + (0XFFFFFFFFFFFFFFFFULL - m_beginTime);
|
||||
|
||||
if (timeRun > m_nOverTime * 1000ULL)
|
||||
{
|
||||
setStatus(FINISH_ERROR_TIMEOUT);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
BOOL MyAfxIsValidAddress(const void* lp, UINT nBytes, BOOL bReadWrite /* = TRUE */)
|
||||
{
|
||||
// simple version using Win-32 APIs for pointer validation.
|
||||
return (lp != NULL && !IsBadReadPtr(lp, nBytes) &&
|
||||
(!bReadWrite || !IsBadWritePtr((LPVOID)lp, nBytes)));
|
||||
}
|
||||
|
||||
bool _AfxParseURLWorker(const wchar_t* pstrURL, LPURL_COMPONENTSW lpComponents,
|
||||
INTERNET_SCHEME& schemeType, INTERNET_PORT& nPort, DWORD dwFlags)
|
||||
{
|
||||
// this function will return bogus stuff if lpComponents
|
||||
// isn't set up to copy the components
|
||||
|
||||
assert(lpComponents != NULL && pstrURL != NULL);
|
||||
if (lpComponents == NULL || pstrURL == NULL)
|
||||
return FALSE;
|
||||
assert(lpComponents->dwHostNameLength == 0 ||
|
||||
lpComponents->lpszHostName != NULL);
|
||||
assert(lpComponents->dwUrlPathLength == 0 ||
|
||||
lpComponents->lpszUrlPath != NULL);
|
||||
assert(lpComponents->dwUserNameLength == 0 ||
|
||||
lpComponents->lpszUserName != NULL);
|
||||
assert(lpComponents->dwPasswordLength == 0 ||
|
||||
lpComponents->lpszPassword != NULL);
|
||||
|
||||
assert(MyAfxIsValidAddress(lpComponents, sizeof(URL_COMPONENTS), TRUE));
|
||||
|
||||
wchar_t* pstrCanonicalizedURL;
|
||||
wchar_t szCanonicalizedURL[INTERNET_MAX_URL_LENGTH];
|
||||
DWORD dwNeededLength = INTERNET_MAX_URL_LENGTH;
|
||||
BOOL bRetVal;
|
||||
BOOL bMustFree = FALSE;
|
||||
DWORD dwCanonicalizeFlags = dwFlags &
|
||||
(ICU_NO_ENCODE | ICU_DECODE | ICU_NO_META |
|
||||
ICU_ENCODE_SPACES_ONLY | ICU_BROWSER_MODE);
|
||||
DWORD dwCrackFlags = dwFlags & (ICU_ESCAPE | ICU_USERNAME);
|
||||
|
||||
bRetVal = InternetCanonicalizeUrlW(pstrURL, szCanonicalizedURL,
|
||||
&dwNeededLength, dwCanonicalizeFlags);
|
||||
|
||||
if (!bRetVal)
|
||||
{
|
||||
if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
return FALSE;
|
||||
|
||||
pstrCanonicalizedURL = new wchar_t[dwNeededLength];
|
||||
bMustFree = TRUE;
|
||||
bRetVal = InternetCanonicalizeUrlW(pstrURL, pstrCanonicalizedURL,
|
||||
&dwNeededLength, dwCanonicalizeFlags);
|
||||
if (!bRetVal)
|
||||
{
|
||||
delete[] pstrCanonicalizedURL;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
pstrCanonicalizedURL = szCanonicalizedURL;
|
||||
|
||||
// now that it's safely canonicalized, crack it
|
||||
bRetVal = InternetCrackUrlW(pstrCanonicalizedURL, 0, dwCrackFlags, lpComponents);
|
||||
if (bMustFree)
|
||||
delete[] pstrCanonicalizedURL;
|
||||
|
||||
if (bRetVal)
|
||||
{
|
||||
nPort = lpComponents->nPort;
|
||||
schemeType = lpComponents->nScheme;
|
||||
}
|
||||
else
|
||||
{
|
||||
schemeType = INTERNET_SCHEME_UNKNOWN;
|
||||
}
|
||||
|
||||
return bRetVal == TRUE;
|
||||
}
|
||||
|
||||
bool parseURL(const wchar_t* pstrURL, INTERNET_SCHEME& schemeType, wchar_t* strServer, wchar_t* strObject, INTERNET_PORT& nPort)
|
||||
{
|
||||
schemeType = INTERNET_SCHEME_UNKNOWN;
|
||||
|
||||
assert(pstrURL != NULL);
|
||||
if (pstrURL == NULL)
|
||||
return FALSE;
|
||||
|
||||
URL_COMPONENTSW urlComponents;
|
||||
memset(&urlComponents, 0, sizeof(URL_COMPONENTS));
|
||||
urlComponents.dwStructSize = sizeof(URL_COMPONENTS);
|
||||
|
||||
urlComponents.dwHostNameLength = MAX_PATH;
|
||||
urlComponents.lpszHostName = strServer;
|
||||
urlComponents.dwUrlPathLength = INTERNET_MAX_URL_LENGTH;
|
||||
urlComponents.lpszUrlPath = strObject;
|
||||
|
||||
return _AfxParseURLWorker(pstrURL, &urlComponents, schemeType, nPort, ICU_BROWSER_MODE);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
#pragma once
|
||||
|
||||
#include "libEncrypt_MD5.h"
|
||||
|
||||
typedef DWORD (WINAPI *DOWNANT_STATUS_CALLBACK)(VOID* pOwner, VOID* pDownAnt);
|
||||
|
||||
#define MANAGER_CMD_CONTINUE (0) //继续下载
|
||||
#define MANAGER_CMD_FORCEABORT (1) //强制中止
|
||||
#define MANAGER_CMD_STOPHERE (2) //下载的目前为止
|
||||
|
||||
/** 下载器,用于下载一个单独文件
|
||||
*/
|
||||
class CDownloadAnt
|
||||
{
|
||||
public:
|
||||
//主状态
|
||||
enum DOWN_STATE_CATEGORY
|
||||
{
|
||||
IDLE, //空闲
|
||||
BUSY, //忙碌中
|
||||
FINISH_SUCCESS, //成功下载完毕
|
||||
FINISH_NOT_SUCCESS, //下载失败
|
||||
};
|
||||
|
||||
enum DOWN_STATE
|
||||
{
|
||||
IDLE_NOTINIT, //空闲 - 尚未初始化
|
||||
IDLE_INIT_SUCCESS, //空闲 - 初始化成功
|
||||
IDLE_INIT_FAILED, //空闲 - 初始化失败
|
||||
|
||||
BUSY_BEGIN_THREAD, //忙碌中 - 启动下载线程
|
||||
BUSY_OPEN_REQUEST, //忙碌中 - 发送下载请求
|
||||
BUSY_RESOLVING_NAME, //忙碌中 - 查找服务器中
|
||||
BUSY_RESOLVED_NAME, //忙碌中 - 查找完服务器
|
||||
BUSY_CONNECTING, //忙碌中 - 连接服务器中
|
||||
BUSY_CONNECTED, //忙碌中 - 连接完服务器
|
||||
BUSY_REDIRECTING, //忙碌中 - 重新定位中
|
||||
BUSY_GET_FILE_INFORMATION, //忙碌中 - 取得文件信息
|
||||
BUSY_BEGIN_DOWNFILE, //忙碌中 - 开始下载文件中
|
||||
|
||||
FINISH_SUCCESS_OVER, //成功下载完毕
|
||||
FINISH_SUCCESS_STOPHERE, //用户定义下载完毕
|
||||
|
||||
FINISH_FORCEABORT, //下载失败 - 用于取消
|
||||
FINISH_ERROR_UNSUPPORTED_SCHEME, //下载失败 - 不支持的协议
|
||||
FINISH_ERROR_TIMEOUT, //下载失败 - 超时
|
||||
FINISH_ERROR_TRY_OPEN_LOCALFILE, //下载失败 - 生成本地文件失败
|
||||
FINISH_ERROR_INTERNET_OPEN, //下载失败 - 打开Internet句柄失败
|
||||
FINISH_ERROR_SETSTATUSCALLBACK, //下载失败 - 设置状态回调函数失败
|
||||
FINISH_ERROR_INTERNETCONNECT, //下载失败 - 连接Internet失败
|
||||
FINISH_ERROR_OPENREQUEST, //下载失败 - 生成Request失败
|
||||
FINISH_ERROR_SENDREQUEST, //下载失败 - 发送Request失败
|
||||
FINISH_ERROR_QUERYINFO, //下载失败 - 查询信息
|
||||
FINISH_ERROR_NEED_PROXY_AUTH_REQ, //下载失败 - 需要代理服务器登录
|
||||
FINISH_ERROR_DENIED, //下载失败 - 被服务器拒绝
|
||||
FINISH_ERROR_INVALID_HTTP_RESPONSE, //下载失败 - 非法的HTTP请求
|
||||
FINISH_ERROR_READFILE, //下载失败 - 读取Internet文件失败
|
||||
FINISH_ERROR_EXCEPTION, //下载失败 - 有异常抛出
|
||||
};
|
||||
|
||||
public:
|
||||
/** 初始化下载器
|
||||
@param wszURLToDownload - 网络文件地址
|
||||
@param wszFileToDownloadInto - 本地文件地址
|
||||
@param pOwner - 参数,会被传入回调函数
|
||||
@param funcUserCallBack - 回调函数
|
||||
@param nOverTime - 超时时间
|
||||
*/
|
||||
bool init(const wchar_t* wszURLToDownload, const wchar_t* wszFileToDownloadInto,
|
||||
DOWNANT_STATUS_CALLBACK funcUserCallBack = NULL, void* pOwner = NULL, unsigned int nOverTime = INFINITE);
|
||||
|
||||
/** 启动下载
|
||||
@param block - 是否等待下载结束后再退出
|
||||
*/
|
||||
void begin(bool block);
|
||||
|
||||
const wchar_t* getDownUrl(void) const { return m_wszURLToDownload; }
|
||||
const wchar_t* getLocalFile(void) const { return m_wszFileToDownloadInto; }
|
||||
const wchar_t* getServer(void) const { return m_wszServer; }
|
||||
bool checkMD5(const char* md5_string) const;
|
||||
|
||||
DOWN_STATE getState(void) const { return m_theState; }
|
||||
DOWN_STATE_CATEGORY getStateCategory(void) const { return m_theStateCategory; }
|
||||
|
||||
protected:
|
||||
//下载单位4K
|
||||
enum { DOWNANT_MINSIZE = 1024*4 };
|
||||
|
||||
HANDLE m_hThread; //!< 下载线程句柄
|
||||
DOWN_STATE_CATEGORY m_theStateCategory; //!< 主状态
|
||||
DOWN_STATE m_theState; //!< 详细状态
|
||||
void* m_pOwner; //!< 附加属性,这个值会传给回调函数
|
||||
|
||||
/** 下载文件的地址 */
|
||||
wchar_t m_wszURLToDownload[INTERNET_MAX_URL_LENGTH];
|
||||
/** 本地存贮地址 */
|
||||
wchar_t m_wszFileToDownloadInto[MAX_PATH];
|
||||
MD5Digest m_fileMD5; //!< 所下载文件的文件MD5码
|
||||
|
||||
INTERNET_SCHEME m_schemeType; //!< 服务段类型http,ftp
|
||||
wchar_t m_wszServer[MAX_PATH]; //!< 服务器地址
|
||||
wchar_t m_wszObject[INTERNET_MAX_URL_LENGTH]; //!< 文件在服务器上的位置
|
||||
WORD m_nPort; //!< 服务器端口
|
||||
|
||||
/// WinInet所用的句柄
|
||||
HINTERNET m_hInternetSession;
|
||||
HINTERNET m_hHttpConnection;
|
||||
HINTERNET m_hHttpFile;
|
||||
|
||||
//用户命令
|
||||
DWORD m_dwUserCmd;
|
||||
//超时时限(秒)
|
||||
DWORD m_nOverTime;
|
||||
//开始时间
|
||||
ULONGLONG m_beginTime;
|
||||
//是否得到了文件大小
|
||||
BOOL m_bGotFileSize;
|
||||
//文件总大小
|
||||
int64_t m_dwFileSizeTotal;
|
||||
//已经下载的文件大小
|
||||
int64_t m_dwFileSizeRead;
|
||||
//文件存储句柄
|
||||
FILE* m_fpFileToWrite;
|
||||
//回调函数
|
||||
DOWNANT_STATUS_CALLBACK UserCallBack;
|
||||
//状态描述
|
||||
wchar_t m_wszStatusInfor[MAX_PATH];
|
||||
public:
|
||||
CDownloadAnt();
|
||||
~CDownloadAnt();
|
||||
|
||||
private:
|
||||
/** wininet 回调函数
|
||||
*/
|
||||
static void CALLBACK _onWininetStatusCallBack(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
|
||||
LPVOID lpvStatusInformation, DWORD dwStatusInformationLength);
|
||||
void onWininetStatusCallBack(HINTERNET hInternet, DWORD dwInternetStatus,
|
||||
LPVOID lpvStatusInformation, DWORD dwStatusInformationLength);
|
||||
/** 线程入口函数
|
||||
*/
|
||||
static UINT CALLBACK _downloadThread(void* pParam);
|
||||
void downloadThread(void);
|
||||
|
||||
/** 改变状态 */
|
||||
void setStatus(DOWN_STATE newState);
|
||||
/** 状态改变回调函数 */
|
||||
void onStatusCallback(void);
|
||||
|
||||
/** 检查是否需要强制退出(超时或者用户强制终止)
|
||||
*/
|
||||
BOOL _checkAbort(void);
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_download_cos.h"
|
||||
|
||||
#if 1
|
||||
bool downloadFileFromCOS(const char* cosFilePathName, const char* localFilePath)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
#include "cos_api.h"
|
||||
#include "cos_sys_config.h"
|
||||
#include "cos_defines.h"
|
||||
|
||||
static void TestLogCallback(const std::string& log)
|
||||
{
|
||||
//printf("ERR:%s\n", log.c_str());
|
||||
}
|
||||
|
||||
//
|
||||
// https://console.cloud.tencent.com/cam/capi
|
||||
//
|
||||
const uint64_t gAppID = 1313389434ULL;
|
||||
const std::string gSecretId = "AKIDGMFKI2av3g6JPTKzRffS1NWR9eXDgwym";
|
||||
const std::string gSecretKey = "f7LMDtNdnlXDpEQRGxVqxrbBYjlcbP2U";
|
||||
const std::string gRegion = "ap-shanghai";
|
||||
const std::string gBucketName = "dnftest-1313389434";
|
||||
|
||||
bool downloadFileFromCOS(const char* cosFilePathName, const char* localFilePath)
|
||||
{
|
||||
qcloud_cos::CosConfig config(gAppID, gSecretId, gSecretKey, gRegion);
|
||||
config.SetLogCallback(&TestLogCallback);
|
||||
qcloud_cos::CosAPI cos(config);
|
||||
|
||||
// 2. 构造下载对象的请求
|
||||
std::string objectName = cosFilePathName;
|
||||
std::string localPath = localFilePath;
|
||||
// request 需要提供 appid、bucketname、object,以及本地的路径(包含文件名)
|
||||
qcloud_cos::GetObjectByFileReq req(gBucketName, objectName, localPath);
|
||||
qcloud_cos::GetObjectByFileResp resp;
|
||||
|
||||
// 3. 调用下载对象接口
|
||||
qcloud_cos::CosResult result = cos.GetObject(req, &resp);
|
||||
|
||||
// 4. 处理调用结果
|
||||
if(result.IsSucc()) {
|
||||
// 下载文件成功
|
||||
std::cout << "Download file success!" << std::endl;
|
||||
}
|
||||
else {
|
||||
// 下载文件失败,可以调用 CosResult 的成员函数输出错误信息,例如 requestID 等
|
||||
std::cout << "HttpStatus=" << result.GetHttpStatus() << std::endl;
|
||||
std::cout << "ErrorCode=" << result.GetErrorCode() << std::endl;
|
||||
std::cout << "ErrorMsg=" << result.GetErrorMsg() << std::endl;
|
||||
std::cout << "ResourceAddr=" << result.GetResourceAddr() << std::endl;
|
||||
std::cout << "XCosRequestId=" << result.GetXCosRequestId() << std::endl;
|
||||
std::cout << "XCosTraceId=" << result.GetXCosTraceId() << std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
bool downloadFileFromCOS(const char* cosFilePath, const char* localFilePath);
|
||||
@@ -0,0 +1,431 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_arg_opt.h"
|
||||
#include "sgu_utils.h"
|
||||
#include "sgu_mode_gen_rand_file.h"
|
||||
#include "sgu_mode_rand_modify.h"
|
||||
#include "sgu_mode_rand_remove.h"
|
||||
#include "sgu_mode_rand_insert.h"
|
||||
#include "sgu_mode_make_diff.h"
|
||||
#include "sgu_mode_apply_patch.h"
|
||||
#include "sgu_mode_hash_file.h"
|
||||
#include "sgu_mode_npk_dump_list.h"
|
||||
#include "sgu_mode_npk_extract_files.h"
|
||||
#include "sgu_mode_download.h"
|
||||
|
||||
#include "libEncrypt_AES.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
enum { OPT_MODE,
|
||||
OPT_INPUT, OPT_INPUT2, OPT_OUTPUT,
|
||||
OPT_MIN_SIZE, OPT_MAX_SIZE,
|
||||
OPT_COUNTS,
|
||||
OPT_TYPE,
|
||||
OPT_VERBOSE_MODE,
|
||||
OPT_HELP
|
||||
};
|
||||
|
||||
CSimpleOptA::SOption g_rgOptions[] = {
|
||||
{ OPT_MODE, "-m", SO_REQ_SEP }, // "-m mode"
|
||||
{ OPT_INPUT, "-i", SO_REQ_SEP }, // "-i input.bin"
|
||||
{ OPT_INPUT2, "-i2", SO_REQ_SEP }, // "-i2 input2.bin"
|
||||
{ OPT_OUTPUT, "-o", SO_REQ_SEP }, // "-o output.bin"
|
||||
{ OPT_MIN_SIZE, "-min", SO_REQ_SEP }, // "-min 100|201MB|2.5GB"
|
||||
{ OPT_MAX_SIZE, "-max", SO_REQ_SEP }, // "-max 100|201MB|2.5GB"
|
||||
{ OPT_COUNTS, "-c", SO_REQ_SEP }, // "-c 5"
|
||||
{ OPT_TYPE, "-t", SO_REQ_SEP }, // "-t md5|sha256"
|
||||
{ OPT_VERBOSE_MODE, "-v", SO_NONE }, // "-v"
|
||||
|
||||
{ OPT_HELP, "-?", SO_NONE }, // "-?"
|
||||
{ OPT_HELP, "--help", SO_NONE }, // "--help"
|
||||
SO_END_OF_OPTIONS // END
|
||||
};
|
||||
|
||||
static void printUsage(const char* moduleName)
|
||||
{
|
||||
printf("===== DNF SideGameplay Utils =====\n");
|
||||
printf("Usage: %s -m [MODE] [OPTIONS]\n", moduleName);
|
||||
printf("\t -m Mode\tfunctioning mode\n");
|
||||
printf("\t -i FileName\tInput file name\n");
|
||||
printf("\t -i2 FileName\tSecond Input file name\n");
|
||||
printf("\t -o FileName\tOutput file name\n");
|
||||
printf("\t -min Size\tMini size[kb|mb|gb]\n");
|
||||
printf("\t -max Size\tMax size[kb|mb|gb]\n");
|
||||
printf("\t -c Counts\n");
|
||||
printf("\t -t Type\n");
|
||||
printf("\t -v\t\tVerbose Mode\n");
|
||||
printf("\t --help -?\tShow help info\n");
|
||||
printf("\nMode List:\n");
|
||||
printf(" GenRandFile\tGenerate random file with specified size range\n");
|
||||
printf(" RandModifyFile\tModifies an input file by replacing multiple randomly selected segments with random data.\n");
|
||||
printf(" RandInsertFile\tInserts random data segments at random positions within an input file.\n");
|
||||
printf(" RandRemoveFile\tRemoves multiple randomly selected segments from an input file.\n");
|
||||
printf(" MakeDiff\tCreates a binary diff patch file between two input files using BSDIFF43.\n");
|
||||
printf(" ApplyPatch\tApplies a BSDIFF43 binary patch to an input file and writes the patched result to an output file.\n");
|
||||
printf(" HashFile\tCalculates the hash(MD5|SHA256) of the contents of a specified file.\n");
|
||||
printf(" NPKDumpList\tDumps the file list from an NPK archive to a text file.\n");
|
||||
printf(" NPKExtractFiles\tExtracts all files from an NPK archive to a specified output directory.\n");
|
||||
printf(" HttpDownload\tDownloads a file from a specified URL and saves it to a local file.\n");
|
||||
printf("\nSample:\n");
|
||||
printf(" -m GenRandFile -min 500 -max 1k -o rand.bin\n");
|
||||
printf(" -m RandModifyFile -i ver01.bin -min 100 -max 1k -c 3 -o ver02.bin\n");
|
||||
printf(" -m RandInsertFile -i ver01.bin -min 1g -max 2g -c 5 -o ver02.bin\n");
|
||||
printf(" -m RandRemoveFile -i ver01.bin -min 10m -max 20m -c 2 -o ver02.bin\n");
|
||||
printf(" -m MakeDiff -i ver01.bin -i2 ver02.bin -o patch_ver01_ver02.diff\n");
|
||||
printf(" -m ApplyPatch -i ver01.bin -i2 patch_ver01_ver02.diff -o ver02.bin\n");
|
||||
printf(" -m HashFile -t md5 -i ver01.bin\n");
|
||||
printf(" -m HashFile -t sha256 -i ver01.bin\n");
|
||||
printf(" -m NPKDumpList -i abc.npk -o abc_file_list.txt\n");
|
||||
printf(" -m NPKExtractFiles -i abc.npk -o output\n");
|
||||
printf(" -m HttpDownload -i http://download.com/file.txt -o file.txt\n");
|
||||
}
|
||||
|
||||
enum WorkMode {
|
||||
WM_NONE=0,
|
||||
|
||||
WM_GEN_RANDOM_FILE,
|
||||
WM_RANDOM_MODIFY_FILE,
|
||||
WM_RANDOM_INSERT_FILE,
|
||||
WM_RANDOM_REMOVE_FILE,
|
||||
WM_MAKE_DIFF,
|
||||
WM_APPLY_PATCH,
|
||||
WM_HASH_FILE,
|
||||
WM_HTTP_DOWNLOAD,
|
||||
WM_NPK_DUMPLIST,
|
||||
WM_NPK_EXTRACT_FILES,
|
||||
};
|
||||
|
||||
enum HashType {
|
||||
HT_NONE = 0,
|
||||
|
||||
HT_MD5,
|
||||
HT_SHA256,
|
||||
HT_CRC32
|
||||
};
|
||||
|
||||
WorkMode parserWorkMode(const char* modeStr)
|
||||
{
|
||||
if(modeStr== nullptr || *modeStr == '\0') {
|
||||
return WM_NONE;
|
||||
}
|
||||
|
||||
if (_stricmp(modeStr, "GenRandFile") == 0) {
|
||||
return WM_GEN_RANDOM_FILE;
|
||||
}
|
||||
else if (_stricmp(modeStr, "RandModifyFile") == 0) {
|
||||
return WM_RANDOM_MODIFY_FILE;
|
||||
}
|
||||
else if (_stricmp(modeStr, "RandInsertFile") == 0) {
|
||||
return WM_RANDOM_INSERT_FILE;
|
||||
}
|
||||
else if (_stricmp(modeStr, "RandRemoveFile") == 0) {
|
||||
return WM_RANDOM_REMOVE_FILE;
|
||||
}
|
||||
else if (_stricmp(modeStr, "MakeDiff") == 0) {
|
||||
return WM_MAKE_DIFF;
|
||||
}
|
||||
else if (_stricmp(modeStr, "ApplyPatch") == 0) {
|
||||
return WM_APPLY_PATCH;
|
||||
}
|
||||
else if (_stricmp(modeStr, "HashFile") == 0) {
|
||||
return WM_HASH_FILE;
|
||||
}
|
||||
else if (_stricmp(modeStr, "HttpDownload") == 0) {
|
||||
return WM_HTTP_DOWNLOAD;
|
||||
}
|
||||
else if (_stricmp(modeStr, "NPKDumpList") == 0) {
|
||||
return WM_NPK_DUMPLIST;
|
||||
}
|
||||
else if (_stricmp(modeStr, "NPKExtractFiles") == 0) {
|
||||
return WM_NPK_EXTRACT_FILES;
|
||||
}
|
||||
return WM_NONE;
|
||||
}
|
||||
|
||||
HashType parserHashType(const char* typeStr)
|
||||
{
|
||||
if(typeStr == nullptr || *typeStr == '\0') {
|
||||
return HT_NONE;
|
||||
}
|
||||
|
||||
if (_stricmp(typeStr, "md5") == 0) {
|
||||
return HT_MD5;
|
||||
}
|
||||
else if (_stricmp(typeStr, "sha256") == 0) {
|
||||
return HT_SHA256;
|
||||
}
|
||||
else if (_stricmp(typeStr, "crc32") == 0) {
|
||||
return HT_CRC32;
|
||||
}
|
||||
return HT_NONE; // default to HT_NONE
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
CSimpleOptA args(argc, argv, g_rgOptions);
|
||||
|
||||
WorkMode workMode = WM_NONE;
|
||||
const char* inputFilename = nullptr;
|
||||
const char* inputFilename2 = nullptr;
|
||||
const char* outputFilename = nullptr;
|
||||
int64_t minSize = 0;
|
||||
int64_t maxSize = 0;
|
||||
int32_t counts = 0;
|
||||
const char* stringType = nullptr;
|
||||
bool verboseMode = false;
|
||||
|
||||
while (args.Next()) {
|
||||
if (args.LastError() == SO_SUCCESS) {
|
||||
if (args.OptionId() == OPT_HELP) {
|
||||
printUsage(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
else if (args.OptionId() == OPT_MODE) {
|
||||
workMode = parserWorkMode(args.OptionArg());
|
||||
}
|
||||
else if (args.OptionId() == OPT_INPUT) {
|
||||
inputFilename = args.OptionArg();
|
||||
}
|
||||
else if (args.OptionId() == OPT_INPUT2) {
|
||||
inputFilename2 = args.OptionArg();
|
||||
}
|
||||
else if (args.OptionId() == OPT_OUTPUT) {
|
||||
outputFilename = args.OptionArg();
|
||||
}
|
||||
else if (args.OptionId() == OPT_MIN_SIZE) {
|
||||
minSize = sizeFromString(args.OptionArg());
|
||||
}
|
||||
else if (args.OptionId() == OPT_MAX_SIZE) {
|
||||
maxSize = sizeFromString(args.OptionArg());
|
||||
}
|
||||
else if (args.OptionId() == OPT_COUNTS) {
|
||||
counts = atoi(args.OptionArg());
|
||||
}
|
||||
else if (args.OptionId() == OPT_TYPE) {
|
||||
stringType = args.OptionArg();
|
||||
}
|
||||
else if (args.OptionId() == OPT_VERBOSE_MODE) {
|
||||
verboseMode = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("Invalid argument: %s\n", args.OptionText());
|
||||
printUsage(::PathFindFileNameA(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
//init random seed
|
||||
std::srand((unsigned int)time(NULL));
|
||||
|
||||
switch (workMode)
|
||||
{
|
||||
case WM_GEN_RANDOM_FILE:
|
||||
{
|
||||
if(outputFilename==nullptr || minSize==0 || maxSize==0) {
|
||||
printf("Error: For 'gen' mode, you must specify output file and size range.\n");
|
||||
printUsage(::PathFindFileNameA(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
printf("Generating random file with min size: %s, max size: %s, output: %s\n",
|
||||
sizeToString(minSize).c_str(), sizeToString(maxSize).c_str(), outputFilename);
|
||||
if (!generateRandomFile(minSize, maxSize, outputFilename))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_RANDOM_MODIFY_FILE:
|
||||
{
|
||||
if(inputFilename == nullptr || outputFilename == nullptr || minSize == 0 || maxSize == 0 || counts==0)
|
||||
{
|
||||
printf("Error: For 'modify' mode, you must specify input file, output file, size range and counts.\n");
|
||||
printUsage(::PathFindFileNameA(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
if(!randomModifyFile(inputFilename, outputFilename, minSize, maxSize, counts))
|
||||
{
|
||||
printf("Error: Failed to modify file.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_RANDOM_REMOVE_FILE:
|
||||
{
|
||||
if (inputFilename == nullptr || outputFilename == nullptr || minSize == 0 || maxSize == 0 || counts == 0)
|
||||
{
|
||||
printf("Error: For 'remove' mode, you must specify input file, output file, size range and counts.\n");
|
||||
printUsage(::PathFindFileNameA(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
if (!randRemovePartOfFile(inputFilename, outputFilename, minSize, maxSize, counts))
|
||||
{
|
||||
printf("Error: Failed to remove part of file.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_RANDOM_INSERT_FILE:
|
||||
{
|
||||
if (inputFilename == nullptr || outputFilename == nullptr || minSize == 0 || maxSize == 0 || counts == 0)
|
||||
{
|
||||
printf("Error: For 'insert' mode, you must specify input file, output file, size range and counts.\n");
|
||||
printUsage(::PathFindFileNameA(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
if(!insertRandomDataInFile(inputFilename, outputFilename, minSize, maxSize, counts))
|
||||
{
|
||||
printf("Error: Failed to insert data in file.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WM_HASH_FILE:
|
||||
{
|
||||
if(inputFilename==nullptr )
|
||||
{
|
||||
printf("Error: For 'hash' mode, you must specify input file.\n");
|
||||
printUsage(::PathFindFileNameA(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
HashType hashType = parserHashType(stringType);
|
||||
if(hashType==HT_NONE)
|
||||
{
|
||||
printf("Error: Invalid hash type '%s'. Supported types are 'md5' and 'sha256'.\n", stringType);
|
||||
printUsage(::PathFindFileNameA(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
printf("Calculating hash for file[%s]: %s\n", stringType, inputFilename);
|
||||
|
||||
if(hashType == HT_SHA256)
|
||||
{
|
||||
Sha256Digest result;
|
||||
if (!sha256File(inputFilename, result))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
printf("SHA256(%s) = %s\n", inputFilename, sha256ToString(result).c_str());
|
||||
}
|
||||
else if (hashType == HT_MD5)
|
||||
{
|
||||
MD5Digest result;
|
||||
if (!md5File(inputFilename, result))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
printf("MD5(%s) = %s\n", inputFilename, md5ToString(result).c_str());
|
||||
}
|
||||
else if(hashType == HT_CRC32)
|
||||
{
|
||||
uint32_t crc32;
|
||||
if (!crc32File(inputFilename, crc32))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
printf("CRC32(%s) = %08X\n", inputFilename, crc32);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error: Unsupported hash type.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_MAKE_DIFF:
|
||||
{
|
||||
if (inputFilename == nullptr || inputFilename2 == nullptr || outputFilename == nullptr)
|
||||
{
|
||||
printf("Error: For 'diff' mode, you must specify two input files and an output file.\n");
|
||||
printUsage(::PathFindFileNameA(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
printf("Creating diff between %s and %s, output: %s\n", inputFilename, inputFilename2, outputFilename);
|
||||
if (!createDiff(inputFilename, inputFilename2, outputFilename))
|
||||
{
|
||||
printf("Error: Failed to create diff file.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_APPLY_PATCH:
|
||||
{
|
||||
if (inputFilename == nullptr || inputFilename2 == nullptr || outputFilename == nullptr)
|
||||
{
|
||||
printf("Error: For 'patch' mode, you must specify two input files and an output file.\n");
|
||||
printUsage(::PathFindFileNameA(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
printf("Applying patch from %s with patch %s, output: %s\n", inputFilename, inputFilename2, outputFilename);
|
||||
if (!applyPatch(inputFilename, inputFilename2, outputFilename))
|
||||
{
|
||||
printf("Error: Failed to apply patch.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WM_HTTP_DOWNLOAD:
|
||||
{
|
||||
if (inputFilename == nullptr || outputFilename == nullptr)
|
||||
{
|
||||
printf("Error: For 'download' mode, you must specify input URL and output file.\n");
|
||||
printUsage(::PathFindFileNameA(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
printf("Downloading from %s to %s\n", inputFilename, outputFilename);
|
||||
if (!downloadFile(inputFilename, outputFilename))
|
||||
{
|
||||
printf("Error: Failed to download file.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_NPK_DUMPLIST:
|
||||
{
|
||||
if (inputFilename == nullptr || outputFilename == nullptr)
|
||||
{
|
||||
printf("Error: For 'NPKDumpList' mode, you must specify input NPK file and output file.\n");
|
||||
printUsage(::PathFindFileNameA(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
printf("Dumping NPK list from %s to %s\n", inputFilename, outputFilename);
|
||||
if (!dumpNPKFileList(inputFilename, outputFilename))
|
||||
{
|
||||
printf("Error: Failed to dump NPK list.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_NPK_EXTRACT_FILES:
|
||||
{
|
||||
if (inputFilename == nullptr || outputFilename == nullptr)
|
||||
{
|
||||
printf("Error: For 'NPKExtractFiles' mode, you must specify input NPK file and output directory.\n");
|
||||
printUsage(::PathFindFileNameA(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
printf("Extracting files from NPK '%s' to directory '%s'\n", inputFilename, outputFilename);
|
||||
if(!extractNPKFiles(inputFilename, outputFilename))
|
||||
{
|
||||
printf("Error: Failed to extract files from NPK.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_NONE:
|
||||
default:
|
||||
{
|
||||
printf("Error: Mode is not specified. Use -m option.\n");
|
||||
printUsage(::PathFindFileNameA(argv[0]));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_mode_apply_patch.h"
|
||||
#include "sgu_utils.h"
|
||||
|
||||
#include "bzlib.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "bspatch.h"
|
||||
}
|
||||
|
||||
|
||||
static int patchFileReader(const struct bspatch_stream* stream, void* buffer, int length)
|
||||
{
|
||||
FILE* fp = (FILE*)stream->opaque;
|
||||
size_t n = fread(buffer, 1, (size_t)length, fp);
|
||||
if (n != length)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bzPatchFileReader(const struct bspatch_stream* stream, void* buffer, int length)
|
||||
{
|
||||
BZFILE* bz2 = (BZFILE*)stream->opaque;
|
||||
|
||||
int bz2err;
|
||||
int n = BZ2_bzRead(&bz2err, bz2, buffer, length);
|
||||
if (n != length)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool applyPatch(const char* inputFilename, const char* patchFilename, const char* outputFilename)
|
||||
{
|
||||
if (!inputFilename || inputFilename[0] == 0 || !patchFilename || patchFilename[0] == 0)
|
||||
{
|
||||
printf("Error: Input or Patch filename is not specified.\n");
|
||||
return false;
|
||||
}
|
||||
if (!outputFilename || outputFilename[0] == 0)
|
||||
{
|
||||
printf("Error: Output filename is not specified.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* pInputBuffer = nullptr;
|
||||
FILE* patchFile = nullptr;
|
||||
FILE* outputFile = nullptr;
|
||||
uint8_t* pOutputBuffer = nullptr;
|
||||
|
||||
bool result = false;
|
||||
do {
|
||||
// Open the input file
|
||||
int64_t inputFileSize;
|
||||
pInputBuffer = readFileToBuffer(inputFilename, inputFileSize);
|
||||
if (pInputBuffer == nullptr)
|
||||
{
|
||||
printf("Error: Failed to read input file: %s\n", inputFilename);
|
||||
break;
|
||||
}
|
||||
|
||||
// Open the patch file
|
||||
fopen_s(&patchFile, patchFilename, "rb");
|
||||
if (!patchFile)
|
||||
{
|
||||
printf("Error: Could not open patch file %s\n", patchFilename);
|
||||
break;
|
||||
}
|
||||
|
||||
// Open the output file
|
||||
fopen_s(&outputFile, outputFilename, "wb");
|
||||
if (!outputFile)
|
||||
{
|
||||
printf("Error: Could not open output file %s\n", outputFilename);
|
||||
break;
|
||||
}
|
||||
|
||||
//read the patch file header and check for appropriate magic
|
||||
uint8_t header[24];
|
||||
if (fread(header, 1, 24, patchFile) != 24 || memcmp(header, "ENDSLEY/BSDIFF43", 16) != 0)
|
||||
{
|
||||
printf("Error: Corrupt patch file %s\n", patchFilename);
|
||||
break;
|
||||
}
|
||||
|
||||
// Read output file lengths from header
|
||||
int64_t outputFileSize = *(int64_t*)(header + 16);
|
||||
pOutputBuffer = new uint8_t[outputFileSize + 1];
|
||||
if(pOutputBuffer == nullptr)
|
||||
{
|
||||
printf("Error: Failed to allocate memory[%lld] for output buffer\n", outputFileSize+1);
|
||||
break;
|
||||
}
|
||||
|
||||
int bz2err;
|
||||
BZFILE* bzHandle = BZ2_bzReadOpen(&bz2err, patchFile, 0, 0, NULL, 0);
|
||||
if (bzHandle == nullptr)
|
||||
{
|
||||
printf("BZ2_bzReadOpen, bz2err=%d", bz2err);
|
||||
break;
|
||||
}
|
||||
|
||||
// Apply the patch
|
||||
struct bspatch_stream stream;
|
||||
stream.read = bzPatchFileReader;
|
||||
stream.opaque = bzHandle;
|
||||
if (bspatch(pInputBuffer, inputFileSize, pOutputBuffer, outputFileSize, &stream))
|
||||
{
|
||||
printf("Error: Failed to apply patch\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// Clean up the bzip2 reads
|
||||
BZ2_bzReadClose(&bz2err, bzHandle);
|
||||
|
||||
// Write the output buffer to the output file
|
||||
if (outputFileSize != fwrite(pOutputBuffer, 1, outputFileSize, outputFile))
|
||||
{
|
||||
printf("Error: Failed to write output file %s\n", outputFilename);
|
||||
break;
|
||||
}
|
||||
|
||||
result = true;
|
||||
printf("Patch applied successfully. Output file: %s\n", outputFilename);
|
||||
}while (false);
|
||||
|
||||
// Close all files and free buffers
|
||||
freeFileBuffer(pInputBuffer);
|
||||
pInputBuffer = nullptr;
|
||||
|
||||
if (pOutputBuffer != nullptr)
|
||||
{
|
||||
delete[] pOutputBuffer;
|
||||
pOutputBuffer = nullptr;
|
||||
}
|
||||
|
||||
if (patchFile != nullptr)
|
||||
{
|
||||
fclose(patchFile);
|
||||
patchFile = nullptr;
|
||||
}
|
||||
if (outputFile != nullptr)
|
||||
{
|
||||
fclose(outputFile);
|
||||
outputFile = nullptr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Applies a BSDIFF43 binary patch to an input file and writes the patched result to an output file.
|
||||
*
|
||||
* @param inputFilename The path to the original input file to be patched.
|
||||
* @param patchFilename The path to the BSDIFF43 patch file (bzip2-compressed).
|
||||
* @param outputFilename The path to the output file where the patched result will be written.
|
||||
* @return true if the patch is applied successfully; false otherwise.
|
||||
*
|
||||
* The function performs the following steps:
|
||||
* 1. Validates input, patch, and output file names.
|
||||
* 2. Reads the input file into memory.
|
||||
* 3. Opens the patch file and verifies its BSDIFF43 header.
|
||||
* 4. Reads the expected output file size from the patch header and allocates a buffer for the output.
|
||||
* 5. Initializes bzip2 decompression for the patch data.
|
||||
* 6. Uses the bspatch algorithm to apply the patch, writing the result to the output buffer.
|
||||
* 7. Writes the patched data to the output file.
|
||||
* 8. Cleans up all resources and returns true on success, or false if any error occurs.
|
||||
*/
|
||||
bool applyPatch(const char* inputFilename, const char* patchFilename, const char* outputFilename);
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_mode_download.h"
|
||||
|
||||
#include "sgu_download_ant.h"
|
||||
|
||||
#include "sgu_utils.h"
|
||||
#include "sgu_download_cos.h"
|
||||
|
||||
bool downloadFile(const char* szDownloadUrl, const char* szLocalFile)
|
||||
{
|
||||
if (szDownloadUrl == nullptr || szLocalFile == nullptr)
|
||||
{
|
||||
printf("Error: download url or local file is null.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string urlStr = szDownloadUrl;
|
||||
|
||||
if (_stricmp(urlStr.substr(0, 6).c_str(), "cos://") == 0)
|
||||
{
|
||||
downloadFileFromCOS(urlStr.substr(6).c_str(), szLocalFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
CDownloadAnt ant;
|
||||
ant.init(convertAnsiStringToWide(szDownloadUrl).c_str(), convertAnsiStringToWide(szLocalFile).c_str());
|
||||
|
||||
// Wait for download to finish
|
||||
ant.begin(true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
bool downloadFile(const char* szDownloadUrl, const char* szLocalFile);
|
||||
@@ -0,0 +1,59 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_mode_gen_rand_file.h"
|
||||
#include "sgu_utils.h"
|
||||
|
||||
bool generateRandomFile(size_t minSize, size_t maxSize, const char* outputFilename)
|
||||
{
|
||||
if (!outputFilename || !*outputFilename) {
|
||||
printf("Error: Output filename is not specified.\n");
|
||||
return false;
|
||||
}
|
||||
if (minSize > maxSize) {
|
||||
printf("Error: Min size cannot be greater than max size.\n");
|
||||
return false;
|
||||
}
|
||||
if (maxSize - minSize >= 2ULL * 1024ULL * 1024ULL * 1024ULL) {
|
||||
printf("Error: Size range is too large(>2GB). Please specify a smaller range.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t fileSize = randomInRange(minSize, maxSize);
|
||||
|
||||
FILE* file = nullptr;
|
||||
fopen_s(&file, outputFilename, "wb");
|
||||
if (!file) {
|
||||
printf("Error: Cannot open output file %s for writing.\n", outputFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t kMaxBufSize = 1 * 1024 * 1024; // 1 MB
|
||||
uint8_t* buffer = new uint8_t[kMaxBufSize];
|
||||
if (!buffer) {
|
||||
fclose(file);
|
||||
printf("Error: Memory allocation failed for buffer.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t remainSize = fileSize;
|
||||
|
||||
while (remainSize > 0)
|
||||
{
|
||||
size_t chunkSize = (remainSize > kMaxBufSize) ? kMaxBufSize : remainSize;
|
||||
// Fill the buffer with random data
|
||||
fillRandomData(buffer, chunkSize);
|
||||
|
||||
// Write the buffer to the file
|
||||
if (fwrite(buffer, 1, chunkSize, file) != chunkSize)
|
||||
{
|
||||
fclose(file);
|
||||
delete[] buffer;
|
||||
printf("Error: Failed to write to output file %s.\n", outputFilename);
|
||||
return false;
|
||||
}
|
||||
remainSize -= chunkSize;
|
||||
}
|
||||
fclose(file);
|
||||
delete[] buffer;
|
||||
printf("Random file generated successfully: %s (%s)\n", outputFilename, sizeToString(fileSize).c_str());
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
bool generateRandomFile(size_t minSize, size_t maxSize, const char* outputFilename);
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_mode_hash_file.h"
|
||||
|
||||
bool md5File(const char* szFileName, MD5Digest& result)
|
||||
{
|
||||
if (!szFileName || szFileName[0]==0) {
|
||||
printf("Error: Input filename is not specified.\n");
|
||||
return false;
|
||||
}
|
||||
FILE* file = nullptr;
|
||||
fopen_s(&file, szFileName, "rb");
|
||||
if (!file) {
|
||||
printf("Error: Cannot open input file %s for reading.\n", szFileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
const size_t kBufferSize = 1024;
|
||||
size_t readSize = 0;
|
||||
uint8_t* buffer = new uint8_t[kBufferSize];
|
||||
|
||||
MD5Context ctx;
|
||||
md5Init(&ctx);
|
||||
|
||||
while ((readSize = fread(buffer, 1, kBufferSize, file)) > 0)
|
||||
{
|
||||
md5Update(&ctx, (uint8_t*)buffer, readSize);
|
||||
}
|
||||
result = md5Finalize(&ctx);
|
||||
fclose(file);
|
||||
|
||||
delete[] buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sha256File(const char* szFileName, Sha256Digest& result)
|
||||
{
|
||||
if (!szFileName || szFileName[0] == 0) {
|
||||
printf("Error: Input filename is not specified.\n");
|
||||
return false;
|
||||
}
|
||||
FILE* file = nullptr;
|
||||
fopen_s(&file, szFileName, "rb");
|
||||
if (!file) {
|
||||
printf("Error: Cannot open input file %s for reading.\n", szFileName);
|
||||
return false;
|
||||
}
|
||||
const size_t kBufferSize = 1024;
|
||||
size_t readSize = 0;
|
||||
uint8_t* buffer = new uint8_t[kBufferSize];
|
||||
Sha256Context ctx;
|
||||
sha256Initialise(&ctx);
|
||||
while ((readSize = fread(buffer, 1, kBufferSize, file)) > 0)
|
||||
{
|
||||
sha256Update(&ctx, (uint8_t*)buffer, readSize);
|
||||
}
|
||||
sha256Finalise(&ctx, result);
|
||||
fclose(file);
|
||||
delete[] buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool crc32File(const char* szFileName, uint32_t& result)
|
||||
{
|
||||
if (!szFileName || szFileName[0] == 0) {
|
||||
printf("Error: Input filename is not specified.\n");
|
||||
return false;
|
||||
}
|
||||
FILE* file = nullptr;
|
||||
fopen_s(&file, szFileName, "rb");
|
||||
if (!file) {
|
||||
printf("Error: Cannot open input file %s for reading.\n", szFileName);
|
||||
return false;
|
||||
}
|
||||
const size_t kBufferSize = 1024;
|
||||
size_t readSize = 0;
|
||||
uint8_t* buffer = new uint8_t[kBufferSize];
|
||||
|
||||
result = 0;
|
||||
while ((readSize = fread(buffer, 1, kBufferSize, file)) > 0)
|
||||
{
|
||||
result = crcMemory32(buffer, static_cast<uint32_t>(readSize), result);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
delete[] buffer;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "libEncrypt_MD5.h"
|
||||
#include "libEncrypt_SHA256.h"
|
||||
#include "libEncrypt_CRC32.h"
|
||||
|
||||
/**
|
||||
* Calculates the MD5 hash of the contents of a specified file.
|
||||
*
|
||||
* @param szFileName The path to the input file to be hashed.
|
||||
* @param result Reference to an MD5Digest object where the computed hash will be stored.
|
||||
* @return true if the hash is calculated successfully; false otherwise.
|
||||
*
|
||||
* The function performs the following steps:
|
||||
* 1. Validates the input file name.
|
||||
* 2. Opens the file in binary read mode.
|
||||
* 3. Reads the file in 1024-byte chunks and updates the MD5 context with each chunk.
|
||||
* 4. Finalizes the MD5 calculation and stores the result in the provided MD5Digest reference.
|
||||
* 5. Cleans up resources and returns true on success, or false if any error occurs.
|
||||
*/
|
||||
bool md5File(const char* szFileName, MD5Digest& result);
|
||||
|
||||
bool sha256File(const char* szFileName, Sha256Digest& result);
|
||||
|
||||
bool crc32File(const char* szFileName, uint32_t& result);
|
||||
@@ -0,0 +1,128 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_mode_make_diff.h"
|
||||
#include "sgu_utils.h"
|
||||
|
||||
#include "bzlib.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "bsdiff.h"
|
||||
}
|
||||
|
||||
static int diffFileWriter(struct bsdiff_stream* stream, const void* buffer, int size)
|
||||
{
|
||||
FILE* fp = (FILE*)stream->opaque;
|
||||
if (size != fwrite(buffer, 1, size, fp))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bzPatchFileWriter(struct bsdiff_stream* stream, const void* buffer, int size)
|
||||
{
|
||||
BZFILE* bz2 = (BZFILE*)stream->opaque;
|
||||
|
||||
int bz2err;
|
||||
BZ2_bzWrite(&bz2err, bz2, (void*)buffer, size);
|
||||
|
||||
if (bz2err != BZ_STREAM_END && bz2err != BZ_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool createDiff(const char* inputFilename1, const char* inputFilename2, const char* outputFilename)
|
||||
{
|
||||
if (!inputFilename1 || inputFilename1[0] == 0 || !inputFilename2 || inputFilename2[0] == 0)
|
||||
{
|
||||
printf("Error: Input filename is not specified.\n");
|
||||
return false;
|
||||
}
|
||||
if (!outputFilename || outputFilename[0] == 0)
|
||||
{
|
||||
printf("Error: Output filename is not specified.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
uint8_t* pBuffer1 = nullptr;
|
||||
uint8_t* pBuffer2 = nullptr;
|
||||
FILE* fpOutput = nullptr;
|
||||
do {
|
||||
int64_t fileSize1;
|
||||
pBuffer1 = readFileToBuffer(inputFilename1, fileSize1);
|
||||
if (pBuffer1 == nullptr)
|
||||
{
|
||||
printf("Error: Failed to read input file 1: %s\n", inputFilename1);
|
||||
break;
|
||||
}
|
||||
|
||||
int64_t fileSize2;
|
||||
pBuffer2 = readFileToBuffer(inputFilename2, fileSize2);
|
||||
if (pBuffer2 == nullptr)
|
||||
{
|
||||
printf("Error: Failed to read input file 2: %s\n", inputFilename2);
|
||||
break;
|
||||
}
|
||||
|
||||
// Open the output file
|
||||
fopen_s(&fpOutput, outputFilename, "wb");
|
||||
if (fpOutput == nullptr)
|
||||
{
|
||||
printf("Error: Failed to open output file: %s\n", outputFilename);
|
||||
break;
|
||||
}
|
||||
|
||||
// Write the header
|
||||
if (fwrite("ENDSLEY/BSDIFF43", 16, 1, fpOutput) != 1 ||
|
||||
fwrite(&fileSize2, sizeof(fileSize2), 1, fpOutput) != 1)
|
||||
{
|
||||
printf("Failed to write header\n");
|
||||
break;
|
||||
}
|
||||
|
||||
int bz2err;
|
||||
BZFILE* bzHandle = BZ2_bzWriteOpen(&bz2err, fpOutput, 9, 0, 0);
|
||||
if (bzHandle == nullptr || bz2err != BZ_OK)
|
||||
{
|
||||
printf("BZ2_bzWriteOpen failed, bz2err=%d\n", bz2err);
|
||||
break;
|
||||
}
|
||||
|
||||
//generate diff
|
||||
struct bsdiff_stream stream;
|
||||
stream.malloc = malloc;
|
||||
stream.free = free;
|
||||
stream.write = bzPatchFileWriter;
|
||||
stream.opaque = bzHandle;
|
||||
if (bsdiff(pBuffer1, fileSize1, pBuffer2, fileSize2, &stream))
|
||||
{
|
||||
printf("bsdiff error!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
BZ2_bzWriteClose(&bz2err, bzHandle, 0, NULL, NULL);
|
||||
if (bz2err != BZ_OK)
|
||||
{
|
||||
printf("BZ2_bzWriteClose failed, bz2err=%d", bz2err);
|
||||
break;
|
||||
}
|
||||
|
||||
result = true;
|
||||
printf("Diff file created successfully: %s\n", outputFilename);
|
||||
} while (false);
|
||||
|
||||
if (fpOutput != nullptr)
|
||||
{
|
||||
fclose(fpOutput);
|
||||
fpOutput = nullptr;
|
||||
}
|
||||
freeFileBuffer(pBuffer1);
|
||||
pBuffer1 = nullptr;
|
||||
|
||||
freeFileBuffer(pBuffer2);
|
||||
pBuffer2 = nullptr;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Creates a binary diff patch file between two input files using the BSDIFF43 format and bzip2 compression.
|
||||
*
|
||||
* @param inputFilename1 The path to the original (old) input file.
|
||||
* @param inputFilename2 The path to the new input file to compare against.
|
||||
* @param outputFilename The path to the output file where the diff patch will be written.
|
||||
* @return true if the diff file is created successfully; false otherwise.
|
||||
*
|
||||
* The function performs the following steps:
|
||||
* 1. Validates input and output file names.
|
||||
* 2. Reads both input files into memory buffers.
|
||||
* 3. Opens the output file for writing and writes the BSDIFF43 header and the new file size.
|
||||
* 4. Initializes bzip2 compression for the output file.
|
||||
* 5. Uses the bsdiff algorithm to generate the binary diff between the two buffers, writing the result through a bzip2 stream.
|
||||
* 6. Finalizes the bzip2 stream and closes all files and buffers.
|
||||
* 7. Returns true on success, or false if any error occurs during processing.
|
||||
*/
|
||||
bool createDiff(const char* inputFilename, const char* inputFilename2, const char* outputFilename);
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_mode_npk_dump_list.h"
|
||||
|
||||
#include "libNPK.h"
|
||||
|
||||
bool dumpNPKFileList(const char* inputFilename, const char* outputFilename)
|
||||
{
|
||||
if (!inputFilename || inputFilename[0] == 0)
|
||||
{
|
||||
printf("Error: Input filename is not specified.\n");
|
||||
return false;
|
||||
}
|
||||
if (!outputFilename || outputFilename[0] == 0)
|
||||
{
|
||||
printf("Error: Output filename is not specified.\n");
|
||||
return false;
|
||||
}
|
||||
NPK::INPKFile* pakFile = NPK::createNPKFile();
|
||||
if (!(pakFile->openPakFile(inputFilename)))
|
||||
{
|
||||
printf("Error: Failed to open NPK file: %s, ErrorCode:%d\n", inputFilename, NPK::getLastErrorCode());
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE* fpOutput = fopen(outputFilename, "a+");
|
||||
if (!fpOutput)
|
||||
{
|
||||
printf("Error: Failed to open output file: %s\n", outputFilename);
|
||||
return false;
|
||||
}
|
||||
int32_t fileCount = pakFile->getFileCount();
|
||||
|
||||
// Dump file list
|
||||
for(int32_t i=0; i < fileCount; i++)
|
||||
{
|
||||
NPK::INPKFile::FileNode fileNode;
|
||||
if (!pakFile->getFileNode(i, fileNode))
|
||||
{
|
||||
printf("Error: Failed to get file node at index %d\n", i);
|
||||
fclose(fpOutput);
|
||||
return false;
|
||||
}
|
||||
NPK::IFileStream* fileStream = pakFile->openFileStream(fileNode.fileName.c_str());
|
||||
if (!fileStream)
|
||||
{
|
||||
printf("Error: Failed to open file stream for '%s', Error=%s\n", fileNode.fileName.c_str(), NPK::getLastErrorMessage());
|
||||
fclose(fpOutput);
|
||||
return false;
|
||||
}
|
||||
uint32_t crc32 = fileStream->crc32();
|
||||
fprintf(fpOutput, "%s\t%08x\t%u\n", fileNode.fileName.c_str(), crc32, fileNode.fileSize);
|
||||
|
||||
pakFile->closeFileStream(fileStream);
|
||||
}
|
||||
|
||||
fclose(fpOutput);
|
||||
|
||||
pakFile->closePakFile();
|
||||
NPK::closeNPKFile(pakFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Dumps the file list from an NPK archive to a text file.
|
||||
*
|
||||
* @param inputFilename The path to the NPK archive file to be read.
|
||||
* @param outputFilename The path to the output text file where the file list will be written.
|
||||
* @return true if the operation succeeds; false otherwise.
|
||||
*
|
||||
* The function performs the following steps:
|
||||
* 1. Validates the input and output file names.
|
||||
* 2. Opens the NPK archive and retrieves the number of contained files.
|
||||
* 3. Iterates through each file entry in the archive, extracting its name, offset, and size.
|
||||
* 4. Writes the file list to the output text file in tab-separated format: fileName, fileOffset, fileSize.
|
||||
* 5. Closes all files and releases resources.
|
||||
* 6. Returns true on success, or false if any error occurs during processing.
|
||||
*/
|
||||
bool dumpNPKFileList(const char* szNPKFileName, const char* szListFileName);
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_mode_npk_extract_files.h"
|
||||
|
||||
|
||||
bool extractNPKFiles(const char* szNPKFileName, const char* szOutputDir)
|
||||
{
|
||||
if (!szNPKFileName || !szOutputDir || szNPKFileName[0] == 0 || szOutputDir[0] == 0)
|
||||
{
|
||||
printf("Error: Invalid parameters. NPK file name and output directory must be specified.\n");
|
||||
return false; // Invalid parameters
|
||||
}
|
||||
|
||||
if (!createEmptyPath(szOutputDir))
|
||||
{
|
||||
printf("Error: Failed to create output directory '%s'.\n", szOutputDir);
|
||||
return false;
|
||||
}
|
||||
|
||||
NPK::INPKFile* npkFile = NPK::createNPKFile();
|
||||
if (!npkFile)
|
||||
{
|
||||
return false; // Failed to create NPK file interface
|
||||
}
|
||||
if (!npkFile->openPakFile(szNPKFileName))
|
||||
{
|
||||
printf("Failed to open NPK file: '%s', Error=%s\n", szNPKFileName, NPK::getLastErrorMessage());
|
||||
NPK::closeNPKFile(npkFile);
|
||||
return false; // Failed to open NPK file
|
||||
}
|
||||
int32_t fileCount = npkFile->getFileCount();
|
||||
for (int32_t i = 0; i < fileCount; ++i)
|
||||
{
|
||||
NPK::INPKFile::FileNode fileNode;
|
||||
if (npkFile->getFileNode(i, fileNode))
|
||||
{
|
||||
std::string outputPathName = std::string(szOutputDir) + "/" + fileNode.fileName;
|
||||
|
||||
char szOutputPath[MAX_PATH];
|
||||
StringCbCopyA(szOutputPath, sizeof(szOutputPath), outputPathName.c_str());
|
||||
getParentPath(szOutputPath);// Get the directory part of the path
|
||||
|
||||
if (!forceCreatePath(szOutputPath))
|
||||
{
|
||||
printf("Error: Failed to create output path '%s'.\n", szOutputPath);
|
||||
NPK::closeNPKFile(npkFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
NPK::IFileStream* fileStream = npkFile->openFileStream(fileNode.fileName.c_str());
|
||||
if(fileStream == nullptr)
|
||||
{
|
||||
printf("Error: Failed to open file stream for '%s', Error=%s\n", fileNode.fileName.c_str(), NPK::getLastErrorMessage());
|
||||
NPK::closeNPKFile(npkFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE* fpOutput = fopen(outputPathName.c_str(), "wb");
|
||||
if (!fpOutput)
|
||||
{
|
||||
printf("Error: Failed to open output file '%s' for writing.\n", outputPathName.c_str());
|
||||
npkFile->closeFileStream(fileStream);
|
||||
NPK::closeNPKFile(npkFile);
|
||||
return false; // Failed to open output file
|
||||
}
|
||||
|
||||
char buffer[4096] = { 0 };
|
||||
uint32_t bytesRead = 0;
|
||||
while ((bytesRead = fileStream->read(buffer, sizeof(buffer))) > 0)
|
||||
{
|
||||
if (fwrite(buffer, 1, bytesRead, fpOutput) != bytesRead)
|
||||
{
|
||||
printf("Error: Failed to write to output file '%s'.\n", outputPathName.c_str());
|
||||
fclose(fpOutput);
|
||||
npkFile->closeFileStream(fileStream);
|
||||
NPK::closeNPKFile(npkFile);
|
||||
return false; // Failed to write to output file
|
||||
}
|
||||
}
|
||||
fclose(fpOutput);
|
||||
npkFile->closeFileStream(fileStream);
|
||||
}
|
||||
}
|
||||
NPK::closeNPKFile(npkFile);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Extracts all files from an NPK archive to a specified output directory.
|
||||
*
|
||||
* @param szNPKFileName The path to the NPK archive file to be extracted.
|
||||
* @param szOutputDir The path to the output directory where files will be extracted.
|
||||
* @return true if extraction succeeds; false otherwise.
|
||||
*
|
||||
* The function performs the following steps:
|
||||
* 1. Validates the input parameters.
|
||||
* 2. Creates or clears the output directory.
|
||||
* 3. Opens the NPK archive and retrieves the file list.
|
||||
* 4. For each file in the archive:
|
||||
* - Ensures the output path exists.
|
||||
* - Opens a stream to the file in the archive.
|
||||
* - Creates the corresponding output file.
|
||||
* - Copies the file data from the archive to the output file in chunks.
|
||||
* 5. Closes all resources and returns true on success, or false if any error occurs.
|
||||
*/
|
||||
bool extractNPKFiles(const char* szNPKFileName, const char* szOutputDir);
|
||||
@@ -0,0 +1,142 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_mode_rand_insert.h"
|
||||
#include "sgu_utils.h"
|
||||
|
||||
bool insertRandomDataInFile(const char* inputFileName, const char* outputFileName, int64_t minSize, int64_t maxSize, int32_t counts)
|
||||
{
|
||||
if (!inputFileName || inputFileName[0] == 0 || !outputFileName || outputFileName[0] == 0)
|
||||
{
|
||||
printf("Invalid input or output file name.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minSize == 0 || maxSize == 0 || minSize > maxSize)
|
||||
{
|
||||
printf("Invalid size parameters.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if input and output file names are the same
|
||||
if (compareTwoFileName(inputFileName, outputFileName) == 0)
|
||||
{
|
||||
printf("Input and output file names cannot be the same.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open the input file in binary mode
|
||||
FILE* inputFile;
|
||||
fopen_s(&inputFile, inputFileName, "rb");
|
||||
if (!inputFile)
|
||||
{
|
||||
printf("Error opening input file:%s\n", inputFileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open the output file in binary mode
|
||||
FILE* outputFile;
|
||||
fopen_s(&outputFile, outputFileName, "wb");
|
||||
if (!outputFile)
|
||||
{
|
||||
printf("Error opening output file:%s", outputFileName);
|
||||
fclose(inputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the size of the input file
|
||||
fseek(inputFile, 0, SEEK_END);
|
||||
int64_t inputFileSize = (int64_t)ftell(inputFile);
|
||||
fseek(inputFile, 0, SEEK_SET);
|
||||
|
||||
std::vector<int64_t> insertOffsets;
|
||||
generateRandomPointsInSegment(inputFileSize, counts, insertOffsets);
|
||||
|
||||
printf("Input file size: %lld bytes\n", inputFileSize);
|
||||
printf("Insert %d segments, size range: %lld ~ %lld bytes\n", counts, minSize, maxSize);
|
||||
int64_t totalSize = 0;
|
||||
|
||||
//Temp memory
|
||||
std::vector<uint8_t> buffer;
|
||||
int64_t offset = 0;
|
||||
|
||||
for (int64_t insertOffset : insertOffsets)
|
||||
{
|
||||
int64_t dataSize = insertOffset - offset;
|
||||
|
||||
// Read file content before segment
|
||||
if (dataSize > 0)
|
||||
{
|
||||
buffer.resize(dataSize);
|
||||
int64_t bytesRead = fread(buffer.data(), 1, dataSize, inputFile);
|
||||
if (bytesRead != dataSize)
|
||||
{
|
||||
printf("Error reading segment before offset %lld, expected %lld bytes, got %lld bytes.\n",
|
||||
offset, dataSize, bytesRead);
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return false;
|
||||
}
|
||||
offset += dataSize;
|
||||
|
||||
// Write the readed content to the output file
|
||||
int64_t bytesWrite = fwrite(buffer.data(), 1, dataSize, outputFile);
|
||||
if (bytesWrite != dataSize)
|
||||
{
|
||||
printf("Error writing segment before offset %lld, expected %lld bytes, wrote %lld bytes.\n",
|
||||
offset, dataSize, bytesWrite);
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int64_t segmentSize = randomInRange(minSize, maxSize);
|
||||
totalSize += segmentSize;
|
||||
printf("Insert segment: offset=%lld, size=%lld\n", insertOffset, segmentSize);
|
||||
|
||||
// create random segment (fill with random data)
|
||||
buffer.resize(segmentSize);
|
||||
fillRandomData(buffer.data(), segmentSize);
|
||||
|
||||
// Write the random segment to the output file
|
||||
if (segmentSize != fwrite(buffer.data(), 1, segmentSize, outputFile))
|
||||
{
|
||||
printf("Error writing random segment size %lld bytes.\n", segmentSize);
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
printf("Total insert size: %lld bytes\n", totalSize);
|
||||
|
||||
// Read and write remain data
|
||||
int64_t remainDataSize = inputFileSize - offset;
|
||||
if (remainDataSize > 0)
|
||||
{
|
||||
buffer.resize(remainDataSize);
|
||||
int64_t bytesRead = fread(buffer.data(), 1, remainDataSize, inputFile);
|
||||
if (bytesRead != remainDataSize)
|
||||
{
|
||||
printf("Error reading remain data offset %lld, expected %lld bytes, got %lld bytes.\n",
|
||||
offset, remainDataSize, bytesRead);
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the readed content to the output file
|
||||
int64_t bytesWrite = fwrite(buffer.data(), 1, remainDataSize, outputFile);
|
||||
if (bytesWrite != remainDataSize)
|
||||
{
|
||||
printf("Error writing remain data offset %lld, expected %lld bytes, wrote %lld bytes.\n",
|
||||
offset, remainDataSize, bytesWrite);
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Inserts random data segments at random positions within an input file and writes the result to an output file.
|
||||
*
|
||||
* @param inputFileName The path to the input file to be processed.
|
||||
* @param outputFileName The path to the output file to write the modified content.
|
||||
* @param minSize The minimum size (in bytes) of each random data segment to insert.
|
||||
* @param maxSize The maximum size (in bytes) of each random data segment to insert.
|
||||
* @param counts The number of random data segments to insert.
|
||||
* @return true if the operation succeeds; false otherwise.
|
||||
*
|
||||
* The function performs the following steps:
|
||||
* 1. Validates input parameters and file names.
|
||||
* 2. Opens the input file for reading and the output file for writing in binary mode.
|
||||
* 3. Determines random insertion offsets within the input file.
|
||||
* 4. For each offset, copies data from the input file up to the offset, then inserts a randomly sized segment filled with random data.
|
||||
* 5. After all insertions, copies any remaining data from the input file to the output file.
|
||||
* 6. Returns true on success, or false if any error occurs during processing.
|
||||
*/
|
||||
bool insertRandomDataInFile(const char* inputFileName, const char* outputFileName, int64_t minSize, int64_t maxSize, int32_t counts);
|
||||
@@ -0,0 +1,146 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_mode_rand_modify.h"
|
||||
#include "sgu_utils.h"
|
||||
|
||||
|
||||
bool randomModifyFile(const char* inputFileName, const char* outputFileName, int64_t minSize, int64_t maxSize, int32_t counts)
|
||||
{
|
||||
if (!inputFileName || inputFileName[0] == 0 || !outputFileName || outputFileName[0] == 0)
|
||||
{
|
||||
printf("Invalid input or output file name.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minSize == 0 || maxSize == 0 || minSize > maxSize)
|
||||
{
|
||||
printf("Invalid size parameters.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if input and output file names are the same
|
||||
if (compareTwoFileName(inputFileName, outputFileName) == 0)
|
||||
{
|
||||
printf("Input and output file names cannot be the same.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE* inputFile = nullptr;
|
||||
FILE* outputFile = nullptr;
|
||||
|
||||
// Open the input file in binary mode
|
||||
fopen_s(&inputFile, inputFileName, "rb");
|
||||
if (!inputFile)
|
||||
{
|
||||
printf("Error opening input file:%s\n", inputFileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open the output file in binary mode
|
||||
fopen_s(&outputFile, outputFileName, "wb");
|
||||
if (!outputFile)
|
||||
{
|
||||
printf("Error opening output file:%s", outputFileName);
|
||||
fclose(inputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the size of the input file
|
||||
fseek(inputFile, 0, SEEK_END);
|
||||
int64_t inputFileSize = (int64_t)ftell(inputFile);
|
||||
fseek(inputFile, 0, SEEK_SET);
|
||||
|
||||
if(maxSize*counts>inputFileSize)
|
||||
{
|
||||
printf("Error: The specified size range(%d*%lld) and count exceed the input file size[%lld].\n",
|
||||
counts, maxSize, inputFileSize);
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::pair< int64_t, int64_t>> segmentArray;
|
||||
generateRandomSegment(inputFileSize, minSize, maxSize, counts, segmentArray);
|
||||
|
||||
printf("Input file size: %lld bytes\n", inputFileSize);
|
||||
printf("Modify %d segments, size range: %lld ~ %lld bytes\n", counts, minSize, maxSize);
|
||||
int64_t totalSize = 0;
|
||||
for (auto& p : segmentArray)
|
||||
{
|
||||
totalSize += p.second;
|
||||
printf("Modify segment: offset=%lld~%lld, size=%lld\n", p.first, p.first+p.second, p.second);
|
||||
}
|
||||
printf("Total modified size: %lld bytes\n", totalSize);
|
||||
|
||||
//Temp memory
|
||||
std::vector<uint8_t> buffer;
|
||||
|
||||
int64_t offset = 0;
|
||||
|
||||
//read and modify each segment
|
||||
for(const auto& segment : segmentArray)
|
||||
{
|
||||
int64_t segmentOffset = segment.first;
|
||||
int64_t segmentSize = segment.second;
|
||||
int64_t dataSize = segmentOffset - offset;
|
||||
// Read file content before segment
|
||||
buffer.resize(dataSize);
|
||||
fseek(inputFile, (long)offset, SEEK_SET);
|
||||
int64_t bytesRead = fread(buffer.data(), 1, dataSize, inputFile);
|
||||
if (bytesRead != dataSize)
|
||||
{
|
||||
printf("Error reading segment before offset %lld, expected %lld bytes, got %lld bytes.\n",
|
||||
offset, dataSize, bytesRead);
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
//write to output file
|
||||
int64_t bytesWrite = fwrite(buffer.data(), 1, dataSize, outputFile);
|
||||
if (bytesWrite != dataSize)
|
||||
{
|
||||
printf("Error writing segment before offset %lld, expected %lld bytes, wrote %lld bytes.\n",
|
||||
offset, dataSize, bytesWrite);
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
// create random segment (fill with random data)
|
||||
buffer.resize(segmentSize);
|
||||
fillRandomData(buffer.data(), segmentSize);
|
||||
|
||||
// Write the modified segment to the output file
|
||||
if (segmentSize != fwrite(buffer.data(), 1, segmentSize, outputFile))
|
||||
{
|
||||
printf("Error writing modified segment at offset %lld, size %lld bytes.\n",
|
||||
segmentOffset, segmentSize);
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
offset = segmentOffset + segmentSize;
|
||||
}
|
||||
|
||||
//read and write the remaining part of the file
|
||||
buffer.resize(inputFileSize - offset);
|
||||
fseek(inputFile, (long)offset, SEEK_SET);
|
||||
size_t bytesRead = fread(buffer.data(), 1, inputFileSize - offset, inputFile);
|
||||
if (bytesRead != inputFileSize - offset)
|
||||
{
|
||||
printf("Error reading segment before offset %zu, expected %zu bytes, got %zu bytes.\n",
|
||||
offset, inputFileSize - offset, bytesRead);
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
//write to output file
|
||||
fwrite(buffer.data(), 1, inputFileSize - offset, outputFile);
|
||||
|
||||
// Close both files
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Modifies an input file by replacing multiple randomly selected segments with random data,
|
||||
* and writes the result to an output file.
|
||||
*
|
||||
* @param inputFileName The path to the input file to be modified.
|
||||
* @param outputFileName The path to the output file to write the modified content.
|
||||
* @param minSize The minimum size (in bytes) of each random segment to be replaced.
|
||||
* @param maxSize The maximum size (in bytes) of each random segment to be replaced.
|
||||
* @param counts The number of random segments to modify.
|
||||
* @return true if the operation succeeds; false otherwise.
|
||||
*
|
||||
* The function performs the following steps:
|
||||
* 1. Validates input parameters and file names.
|
||||
* 2. Ensures the total size of all segments does not exceed the input file size.
|
||||
* 3. Randomly selects 'counts' segments within the input file, each with a size between minSize and maxSize.
|
||||
* 4. Copies unmodified data from the input file to the output file, except for the selected segments.
|
||||
* 5. For each selected segment, replaces its content with random data in the output file.
|
||||
* 6. Writes the remaining data after the last segment.
|
||||
* 7. Returns true on success, or false if any error occurs.
|
||||
*/
|
||||
bool randomModifyFile(const char* inputFileName, const char* outputFileName, int64_t minSize, int64_t maxSize, int32_t counts);
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_mode_rand_remove.h"
|
||||
#include "sgu_utils.h"
|
||||
|
||||
bool randRemovePartOfFile(const char* inputFileName, const char* outputFileName, int64_t minSize, int64_t maxSize, int32_t counts)
|
||||
{
|
||||
if (!inputFileName || inputFileName[0] == 0 || !outputFileName || outputFileName[0] == 0)
|
||||
{
|
||||
printf("Invalid input or output file name.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minSize == 0 || maxSize == 0 || minSize > maxSize)
|
||||
{
|
||||
printf("Invalid size parameters.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if input and output file names are the same
|
||||
if (compareTwoFileName(inputFileName, outputFileName) == 0)
|
||||
{
|
||||
printf("Input and output file names cannot be the same.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open the input file in binary mode
|
||||
FILE* inputFile;
|
||||
fopen_s(&inputFile, inputFileName, "rb");
|
||||
if (!inputFile)
|
||||
{
|
||||
printf("Error opening input file:%s\n", inputFileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open the output file in binary mode
|
||||
FILE* outputFile;
|
||||
fopen_s(&outputFile, outputFileName, "wb");
|
||||
if (!outputFile)
|
||||
{
|
||||
printf("Error opening output file:%s", outputFileName);
|
||||
fclose(inputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the size of the input file
|
||||
fseek(inputFile, 0, SEEK_END);
|
||||
int64_t inputFileSize = (int64_t)ftell(inputFile);
|
||||
fseek(inputFile, 0, SEEK_SET);
|
||||
|
||||
if (maxSize * counts > inputFileSize)
|
||||
{
|
||||
printf("Error: The specified size range(%d*%lld) and count exceed the input file size[%lld].\n",
|
||||
counts, maxSize, inputFileSize);
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::pair< int64_t, int64_t>> segmentArray;
|
||||
generateRandomSegment(inputFileSize, minSize, maxSize, counts, segmentArray);
|
||||
|
||||
int64_t totalSize = 0;
|
||||
printf("Input file size: %lld bytes\n", inputFileSize);
|
||||
printf("Remove %d segments, size range: %lld ~ %lld bytes\n", counts, minSize, maxSize);
|
||||
for (auto& p : segmentArray)
|
||||
{
|
||||
totalSize += p.second;
|
||||
printf("Remove segment: offset=%lld~%lld, size=%lld\n", p.first, p.first + p.second, p.second);
|
||||
}
|
||||
printf("Total size to remove: %lld bytes\n", totalSize);
|
||||
|
||||
//Temp memory
|
||||
std::vector<uint8_t> buffer;
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
//read and modify each segment
|
||||
for (const auto& segment : segmentArray)
|
||||
{
|
||||
size_t segmentOffset = segment.first;
|
||||
size_t segmentSize = segment.second;
|
||||
// Read file content before segment
|
||||
buffer.resize(segmentOffset - offset);
|
||||
fseek(inputFile, (long)offset, SEEK_SET);
|
||||
size_t bytesRead = fread(buffer.data(), 1, segmentOffset - offset, inputFile);
|
||||
if (bytesRead != segmentOffset - offset)
|
||||
{
|
||||
printf("Error reading segment before offset %zu, expected %zu bytes, got %zu bytes.\n",
|
||||
offset, segmentOffset - offset, bytesRead);
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
//write to output file
|
||||
fwrite(buffer.data(), 1, segmentOffset - offset, outputFile);
|
||||
|
||||
offset = segmentOffset + segmentSize;
|
||||
}
|
||||
|
||||
//read and write the remaining part of the file
|
||||
buffer.resize(inputFileSize - offset);
|
||||
fseek(inputFile, (long)offset, SEEK_SET);
|
||||
size_t bytesRead = fread(buffer.data(), 1, inputFileSize - offset, inputFile);
|
||||
if (bytesRead != inputFileSize - offset)
|
||||
{
|
||||
printf("Error reading segment before offset %zu, expected %zu bytes, got %zu bytes.\n",
|
||||
offset, inputFileSize - offset, bytesRead);
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
return false;
|
||||
}
|
||||
|
||||
//write to output file
|
||||
fwrite(buffer.data(), 1, inputFileSize - offset, outputFile);
|
||||
|
||||
// Close both files
|
||||
fclose(inputFile);
|
||||
fclose(outputFile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Removes multiple randomly selected segments from an input file and writes the remaining data to an output file.
|
||||
*
|
||||
* @param inputFileName The path to the input file to be processed.
|
||||
* @param outputFileName The path to the output file to write the result.
|
||||
* @param minSize The minimum size (in bytes) of each segment to remove.
|
||||
* @param maxSize The maximum size (in bytes) of each segment to remove.
|
||||
* @param counts The number of segments to remove.
|
||||
* @return true if the operation succeeds; false otherwise.
|
||||
*
|
||||
* The function performs the following steps:
|
||||
* 1. Validates input parameters and file names.
|
||||
* 2. Opens the input file for reading and the output file for writing in binary mode.
|
||||
* 3. Ensures the total size of all segments to be removed does not exceed the input file size.
|
||||
* 4. Randomly selects 'counts' segments within the input file, each with a size between minSize and maxSize.
|
||||
* 5. Copies unmodified data from the input file to the output file, skipping the selected segments.
|
||||
* 6. Writes any remaining data after the last removed segment.
|
||||
* 7. Returns true on success, or false if any error occurs during processing.
|
||||
*/
|
||||
bool randRemovePartOfFile(const char* inputFileName, const char* outputFileName, int64_t minSize, int64_t maxSize, int32_t counts);
|
||||
@@ -0,0 +1,21 @@
|
||||
#include <Windows.h>
|
||||
#include <Shlwapi.h>
|
||||
#include <wininet.h>
|
||||
#include <strsafe.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <process.h>
|
||||
#include <memory.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
#include "libNPK.h"
|
||||
#include "libNPK_Utils.h"
|
||||
@@ -0,0 +1,109 @@
|
||||
#include "sgu_stdafx.h"
|
||||
#include "sgu_utils.h"
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
|
||||
bool generateRandomSegment(int64_t memorySize, int64_t minSize, int64_t maxSize, int32_t counts,
|
||||
std::vector<std::pair< int64_t, int64_t>>& segmentArray)
|
||||
{
|
||||
// Cannot allocate: not enough space
|
||||
if ((int64_t)counts * minSize > memorySize)
|
||||
return false;
|
||||
|
||||
// Calculate the base partition size for each segment
|
||||
int64_t basePartition = memorySize / counts;
|
||||
int64_t remain = memorySize % counts;
|
||||
|
||||
std::vector<int64_t> regionSizes(counts, minSize);
|
||||
int64_t totalAssigned = minSize * counts;
|
||||
|
||||
// First assign the minimum size to each segment, then randomly distribute the remaining space
|
||||
int64_t extraSpace = memorySize - totalAssigned;
|
||||
for (int i = 0; i < counts && extraSpace > 0; ++i)
|
||||
{
|
||||
int64_t maxExtra = std::min(extraSpace, maxSize - minSize);
|
||||
int64_t add = randomInRange(0, maxExtra);
|
||||
regionSizes[i] += add;
|
||||
extraSpace -= add;
|
||||
}
|
||||
|
||||
// Shuffle the allocation to avoid always concentrating at the front
|
||||
std::random_shuffle(regionSizes.begin(), regionSizes.end());
|
||||
|
||||
// Calculate the start position of each segment, distribute them evenly
|
||||
int64_t offset = 0;
|
||||
for (int i = 0; i < counts; ++i)
|
||||
{
|
||||
int64_t partitionStart = (memorySize * i) / counts;
|
||||
int64_t partitionEnd = (memorySize * (i + 1)) / counts;
|
||||
int64_t partitionLen = partitionEnd - partitionStart;
|
||||
|
||||
// The segment length cannot exceed the partition length
|
||||
int64_t regionLen = std::min(regionSizes[i], partitionLen);
|
||||
// Random start position within the partition
|
||||
int64_t maxStart = partitionLen - regionLen;
|
||||
int64_t startInPartition = randomInRange(0, maxStart);
|
||||
int64_t regionStart = partitionStart + startInPartition;
|
||||
|
||||
segmentArray.push_back(std::make_pair(regionStart, regionLen));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Randomly generate 'counts' points on a segment of length 'nSegmentSize'.
|
||||
void generateRandomPointsInSegment(int64_t nSegmentSize, int32_t counts, std::vector<int64_t>& points)
|
||||
{
|
||||
points.clear();
|
||||
if (nSegmentSize <= 0 || counts <= 0 || counts > nSegmentSize)
|
||||
return;
|
||||
|
||||
// Use a set to avoid duplicate points
|
||||
std::set<int64_t> uniquePoints;
|
||||
while (static_cast<int32_t>(uniquePoints.size()) < counts)
|
||||
{
|
||||
int64_t pt = randomInRange(0, nSegmentSize-1);
|
||||
uniquePoints.insert(pt);
|
||||
}
|
||||
|
||||
points.assign(uniquePoints.begin(), uniquePoints.end());
|
||||
std::sort(points.begin(), points.end());
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t randomInRange(int64_t minValue, int64_t maxValue)
|
||||
{
|
||||
if (minValue > maxValue) {
|
||||
std::swap(minValue, maxValue);
|
||||
}
|
||||
if (minValue == maxValue) {
|
||||
return minValue;
|
||||
}
|
||||
int64_t range = maxValue - minValue + 1;
|
||||
if (range <= 0) { // Overflow case
|
||||
return minValue + (static_cast<int64_t>(std::rand()) << 32 | static_cast<int64_t>(std::rand()) << 16 | static_cast<int64_t>(std::rand())) % range;
|
||||
}
|
||||
else if (range <= RAND_MAX) {
|
||||
return minValue + std::rand() % range;
|
||||
}
|
||||
else if (range <= (int64_t)RAND_MAX * RAND_MAX) {
|
||||
return minValue + (static_cast<int64_t>(std::rand()) << 16 | static_cast<int64_t>(std::rand())) % range;
|
||||
}
|
||||
else {
|
||||
return minValue + (static_cast<int64_t>(std::rand()) << 32 | static_cast<int64_t>(std::rand()) << 16 | static_cast<int64_t>(std::rand())) % range;
|
||||
}
|
||||
}
|
||||
|
||||
void fillRandomData(uint8_t* buffer, int64_t size)
|
||||
{
|
||||
if (!buffer || size <= 0) return;
|
||||
for (int64_t i = 0; i < size; ++i) {
|
||||
buffer[i] = static_cast<uint8_t>(std::rand() % 256);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
bool generateRandomSegment(int64_t memorySize, int64_t minSize, int64_t maxSize, int32_t counts,
|
||||
std::vector<std::pair< int64_t, int64_t>>& segmentArray);
|
||||
|
||||
void generateRandomPointsInSegment(int64_t nSegmentSize, int32_t counts, std::vector<int64_t>& points);
|
||||
|
||||
int64_t randomInRange(int64_t minValue, int64_t maxValue);
|
||||
|
||||
void fillRandomData(uint8_t* buffer, int64_t size);
|
||||
|
||||
Reference in New Issue
Block a user