initial commit

This commit is contained in:
2025-09-01 20:47:58 +08:00
commit 12c51b5ddb
843 changed files with 475321 additions and 0 deletions
+61
View File
@@ -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
)
+1046
View File
File diff suppressed because it is too large Load Diff
+650
View File
@@ -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);
}
+152
View File
@@ -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);
};
+60
View File
@@ -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
+3
View File
@@ -0,0 +1,3 @@
#pragma once
bool downloadFileFromCOS(const char* cosFilePath, const char* localFilePath);
+431
View File
@@ -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;
}
+151
View File
@@ -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;
}
+21
View File
@@ -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);
+32
View File
@@ -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;
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
bool downloadFile(const char* szDownloadUrl, const char* szLocalFile);
+59
View File
@@ -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;
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
bool generateRandomFile(size_t minSize, size_t maxSize, const char* outputFilename);
+87
View File
@@ -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;
}
+25
View File
@@ -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);
+128
View File
@@ -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;
}
+20
View File
@@ -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);
+62
View File
@@ -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;
}
+18
View File
@@ -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);
+85
View File
@@ -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;
}
+21
View File
@@ -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);
+142
View File
@@ -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;
}
+21
View File
@@ -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);
+146
View File
@@ -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;
}
+24
View File
@@ -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);
+123
View File
@@ -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;
}
+22
View File
@@ -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);
+21
View File
@@ -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"
+109
View File
@@ -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);
}
}
+12
View File
@@ -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);