Game client codebase including: - CharacterActionControl: Character and creature management - GlobalScript: Network, items, skills, quests, utilities - RYLClient: Main client application with GUI and event handlers - Engine: 3D rendering engine (RYLGL) - MemoryManager: Custom memory allocation - Library: Third-party dependencies (DirectX, boost, etc.) - Tools: Development utilities 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
769 lines
26 KiB
C++
769 lines
26 KiB
C++
// ManualPatchPage.cpp : 구현 파일입니다.
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "PatchMaker.h"
|
|
#include "ManualPatchPage.h"
|
|
|
|
#include "UtilityFunc.h"
|
|
#include "PMSetting.h"
|
|
#include "PMSettingConstant.h"
|
|
|
|
#include "atlenc.h"
|
|
|
|
#include <process.h>
|
|
#include <ZipArchive.h>
|
|
#include ".\manualpatchpage.h"
|
|
|
|
// CManualPatchPage 대화 상자입니다.
|
|
|
|
IMPLEMENT_DYNAMIC(CManualPatchPage, CPropertyPage)
|
|
CManualPatchPage::CManualPatchPage(CPMSetting& pmSetting, DWORD dwPatchVersion)
|
|
: CPropertyPage(CManualPatchPage::IDD)
|
|
, m_szMPFolder(_T(""))
|
|
, m_szMPSFXFile(_T(""))
|
|
, m_szMPFileName(_T(""))
|
|
, m_dwMPMinver(0)
|
|
, m_dwMPMaxver(dwPatchVersion)
|
|
, m_szPatchType(_T(""))
|
|
, m_szFileProgress(_T("0/0"))
|
|
, m_pmSettingOption(pmSetting)
|
|
, m_hManualPatchThread(0)
|
|
{
|
|
m_ProgressData.m_bStopWorkerThread = FALSE;
|
|
m_ProgressData.m_nTotalFiles = 0;
|
|
m_ProgressData.m_nAddedFiles = 0;
|
|
m_ProgressData.m_dwTotalFileSize = 0LL;
|
|
m_ProgressData.m_dwCurrentFileSize = 0LL;
|
|
}
|
|
|
|
CManualPatchPage::~CManualPatchPage()
|
|
{
|
|
|
|
}
|
|
|
|
void CManualPatchPage::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CPropertyPage::DoDataExchange(pDX);
|
|
DDX_Text(pDX, IDC_ED_MP_MANUAL_PATCH_FILE, m_szMPFolder);
|
|
DDX_Text(pDX, IDC_ED_MP_MANUAL_SFX_FILE, m_szMPSFXFile);
|
|
DDX_Text(pDX, IDC_ED_MP_PATCH_FILE_NAME, m_szMPFileName);
|
|
DDX_Text(pDX, IDC_ED_MP_MIN_VER, m_dwMPMinver);
|
|
DDX_Text(pDX, IDC_ED_MP_CUR_VER, m_dwMPMaxver);
|
|
DDX_Text(pDX, IDC_ED_MP_PATCH_TYPE, m_szPatchType);
|
|
DDX_Control(pDX, IDC_ED_MP_CONSOLE, m_edProgressLog);
|
|
DDX_Control(pDX, IDC_PRG_MP, m_TotalProgressBar);
|
|
DDX_Control(pDX, IDC_ED_MP_ERR_CONSOLE, m_edErrLog);
|
|
DDX_Text(pDX, IDC_ST_MP_PRORGRESS, m_szFileProgress);
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CManualPatchPage, CPropertyPage)
|
|
ON_BN_CLICKED(ID_MP_CREATE, OnBnClickedMpCreate)
|
|
ON_BN_CLICKED(ID_MP_CANCEL, OnBnClickedMpCancel)
|
|
ON_BN_CLICKED(IDC_BTN_MP_SELECT_FILE, OnBnClickedBtnMpSelectFile)
|
|
ON_WM_CREATE()
|
|
ON_WM_TIMER()
|
|
ON_WM_CLOSE()
|
|
ON_BN_CLICKED(IDC_CLEAR_LOG, OnBnClickedClearLog)
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
// CManualPatchPage 메시지 처리기입니다.
|
|
|
|
struct MPWorkerData
|
|
{
|
|
CManualPatchPage* m_lpPage;
|
|
|
|
CString m_szMPFolder;
|
|
CString m_szMPSFXFile;
|
|
CString m_szMPFileName;
|
|
CString m_szPatchType;
|
|
|
|
DWORD m_dwMPMaxver;
|
|
DWORD m_dwMPMinver;
|
|
};
|
|
|
|
|
|
void CManualPatchPage::UpdateManualPatchStatus(MPProgressData& progressData)
|
|
{
|
|
//! MANUAL_PATCH_LOCK
|
|
m_ProgressLock.Lock();
|
|
|
|
// 데이터를 갱신한다
|
|
progressData.m_bStopWorkerThread = m_ProgressData.m_bStopWorkerThread;
|
|
|
|
m_ProgressData.m_nTotalFiles = progressData.m_nTotalFiles;
|
|
m_ProgressData.m_nAddedFiles = progressData.m_nAddedFiles;
|
|
m_ProgressData.m_dwCurrentFileSize = progressData.m_dwCurrentFileSize;
|
|
m_ProgressData.m_dwTotalFileSize = progressData.m_dwTotalFileSize;
|
|
|
|
m_ProgressData.m_ProgressList.AddTail(&progressData.m_ProgressList);
|
|
m_ProgressData.m_ErrorList.AddTail(&progressData.m_ErrorList);
|
|
m_ProgressData.m_TotalProgressList.AddTail((&progressData.m_TotalProgressList));
|
|
|
|
//! MANUAL_PATCH_UNLOCK
|
|
m_ProgressLock.Unlock();
|
|
|
|
progressData.m_ProgressList.RemoveAll();
|
|
progressData.m_ErrorList.RemoveAll();
|
|
}
|
|
|
|
template<class FnProcess>
|
|
bool EnumerateAllLeafFiles(FnProcess fnProcess, LPCTSTR lpRootPath)
|
|
{
|
|
CString szTemp;
|
|
CList<CString> dirStack;
|
|
CFileFind fileFind;
|
|
|
|
szTemp.SetString(lpRootPath);
|
|
szTemp.Trim();
|
|
|
|
int nLength = szTemp.GetLength();
|
|
|
|
// 뒤에 \\*.* 혹은 *.*를 붙여 준다.
|
|
szTemp.Append(
|
|
(1 < nLength && _T('\\') != szTemp.GetString()[nLength - 1])
|
|
? _T("\\*.*") : _T("*.*"));
|
|
|
|
dirStack.AddTail(szTemp);
|
|
|
|
BOOL bWorking = FALSE;
|
|
while(!dirStack.IsEmpty())
|
|
{
|
|
szTemp.SetString(dirStack.RemoveTail());
|
|
if (bWorking = fileFind.FindFile(szTemp))
|
|
{
|
|
while(bWorking)
|
|
{
|
|
bWorking = fileFind.FindNextFile();
|
|
if (!fileFind.IsDots())
|
|
{
|
|
szTemp.SetString(fileFind.GetFilePath());
|
|
if (fileFind.IsDirectory())
|
|
{
|
|
// 폴더인 경우
|
|
szTemp.Append(_T("\\*.*"));
|
|
dirStack.AddTail(szTemp);
|
|
}
|
|
// vssver.scc는 포함 안 함
|
|
else if(0 != szTemp.CompareNoCase(_T("vssver.scc")) && !fnProcess(fileFind))
|
|
{
|
|
// 작업을 취소하였음
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
class CGetTotalFileNumAndSize
|
|
{
|
|
public:
|
|
|
|
CGetTotalFileNumAndSize(ULONGLONG& dwTotalFileSize, int& nTotalFileNum)
|
|
: m_dwTotalFileSize(dwTotalFileSize)
|
|
, m_nTotalFileNum(nTotalFileNum)
|
|
{
|
|
|
|
}
|
|
|
|
bool operator () (const CFileFind& fileFind)
|
|
{
|
|
m_dwTotalFileSize += fileFind.GetLength();
|
|
++m_nTotalFileNum;
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
|
|
ULONGLONG& m_dwTotalFileSize;
|
|
int& m_nTotalFileNum;
|
|
};
|
|
|
|
class CCompressFiles
|
|
{
|
|
public:
|
|
|
|
CCompressFiles(
|
|
CManualPatchPage& mpPage,
|
|
MPProgressData& progressData,
|
|
CZipArchive& zipArchive,
|
|
CString& szErrorString)
|
|
: m_MPPage(mpPage)
|
|
, m_ProgressData(progressData)
|
|
, m_ZipArchive(zipArchive)
|
|
, m_szErrorString(szErrorString)
|
|
{
|
|
|
|
}
|
|
|
|
bool operator () (const CFileFind& fileFind)
|
|
{
|
|
// 파일을 압축해서 추가한다.
|
|
m_szFileName = fileFind.GetFilePath();
|
|
|
|
// 파일 속성을 기본 속성으로 바꿈
|
|
SetFileAttributes(m_szFileName, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_ARCHIVE);
|
|
if (m_ZipArchive.AddNewFile(m_szFileName, Z_BEST_COMPRESSION, false))
|
|
{
|
|
// 데이터를 갱신한다.
|
|
ULONGLONG dwFileSize = fileFind.GetLength();
|
|
m_ProgressData.m_dwCurrentFileSize += dwFileSize;
|
|
++m_ProgressData.m_nAddedFiles;
|
|
|
|
PMUtil::SetCurrentTime(m_szErrorString);
|
|
m_szErrorString.AppendFormat(_T("파일 추가 : %s"), m_szFileName);
|
|
m_ProgressData.m_ProgressList.AddTail(m_szErrorString);
|
|
}
|
|
else
|
|
{
|
|
// 에러 로그를 남긴다.
|
|
DWORD dwError = GetLastError();
|
|
|
|
PMUtil::SetCurrentTime(m_szErrorString);
|
|
m_szErrorString.AppendFormat(_T("작업 실패 : %s : "), m_szFileName);
|
|
PMUtil::AppendErrorMessage(m_szErrorString, dwError);
|
|
|
|
m_ProgressData.m_ErrorList.AddTail(m_szErrorString);
|
|
}
|
|
|
|
// 데이터를 업데이트한다.
|
|
m_MPPage.UpdateManualPatchStatus(m_ProgressData);
|
|
return (FALSE == m_ProgressData.m_bStopWorkerThread);
|
|
}
|
|
|
|
private:
|
|
|
|
CManualPatchPage& m_MPPage;
|
|
MPProgressData& m_ProgressData;
|
|
CZipArchive& m_ZipArchive;
|
|
CString& m_szErrorString;
|
|
CString m_szFileName;
|
|
};
|
|
|
|
unsigned __stdcall CManualPatchPage::ManualPatchWorker(void* pArg)
|
|
{
|
|
MPWorkerData* lpWorkerData = reinterpret_cast<MPWorkerData*>(pArg);
|
|
CManualPatchPage* lpPage = lpWorkerData->m_lpPage;
|
|
|
|
// 데이터 정의
|
|
CZipArchive zipArchive;
|
|
|
|
CString szSFXFileName;
|
|
CString szZipFileName;
|
|
|
|
CString szErrorString;
|
|
|
|
const int MAX_ERROR_LEN = 256;
|
|
TCHAR szErrorMsg[MAX_ERROR_LEN];
|
|
|
|
MPProgressData progressData;
|
|
progressData.m_bStopWorkerThread = FALSE;
|
|
progressData.m_dwTotalFileSize = 0LL;
|
|
progressData.m_dwCurrentFileSize = 0LL;
|
|
progressData.m_nTotalFiles = 0;
|
|
progressData.m_nAddedFiles = 0;
|
|
|
|
szSFXFileName.SetString(lpWorkerData->m_szMPFileName);
|
|
szZipFileName.SetString(lpWorkerData->m_szMPFileName);
|
|
int nPos = szZipFileName.ReverseFind(_T('.'));
|
|
if (0 < nPos) { szZipFileName.Truncate(nPos); szZipFileName.Append(_T(".zip"), nPos); }
|
|
|
|
// 전체 파일 개수 및 파일 용량을 계산한다.
|
|
if (!EnumerateAllLeafFiles(CGetTotalFileNumAndSize(
|
|
progressData.m_dwTotalFileSize, progressData.m_nTotalFiles), lpWorkerData->m_szMPFolder))
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
|
|
PMUtil::SetCurrentTime(szErrorString);
|
|
if (progressData.m_bStopWorkerThread)
|
|
{
|
|
szErrorString.AppendFormat(_T("작업 실패 : 작업을 취소하였습니다"));
|
|
}
|
|
else
|
|
{
|
|
szErrorString.AppendFormat(_T("작업 실패 : 압축할 파일을 찾을 수 없습니다 : "));
|
|
PMUtil::AppendErrorMessage(szErrorString, dwError);
|
|
}
|
|
|
|
progressData.m_ErrorList.AddTail(szErrorString);
|
|
progressData.m_TotalProgressList.AddTail(szErrorString);
|
|
}
|
|
else
|
|
{
|
|
TRY
|
|
{
|
|
bool bSucceededJob = false;
|
|
|
|
zipArchive.Open(szZipFileName, CZipArchive::zipCreate);
|
|
zipArchive.SetRootPath(lpWorkerData->m_szMPFolder);
|
|
|
|
// 파일을 추가/압축한다. 에러 로그는 내부에서 남긴다.
|
|
if (!EnumerateAllLeafFiles(
|
|
CCompressFiles(*lpPage, progressData, zipArchive, szErrorString),
|
|
lpWorkerData->m_szMPFolder))
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
PMUtil::SetCurrentTime(szErrorString);
|
|
|
|
if (progressData.m_bStopWorkerThread)
|
|
{
|
|
szErrorString.AppendFormat(_T("작업 실패 : 작업을 취소하였습니다"));
|
|
}
|
|
else
|
|
{
|
|
szErrorString.AppendFormat(_T("작업 실패 : 압축할 파일을 찾을 수 없습니다 : "));
|
|
PMUtil::AppendErrorMessage(szErrorString, dwError);
|
|
}
|
|
|
|
progressData.m_ErrorList.AddTail(szErrorString);
|
|
progressData.m_TotalProgressList.AddTail(szErrorString);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
TODO : 필요한 데이터를 추리고, BASE64인코딩을 한 후 Comment에 쑤셔 박는다.
|
|
|
|
필요 데이터
|
|
1. 압축 전 용량 : 디스크 용량 체크
|
|
2. 최소버전 : 깔릴 위치에 클라이언트가 있다면, 최소버전을 체크한다.
|
|
3. 패치버전 : 깔릴 위치에 클라이언트가 있다면, 현재 버전을 체크해서 더 높은가 확인한다.
|
|
4. 기본으로 읽어올 레지스트리 위치
|
|
*/
|
|
|
|
CMemFile srcData;
|
|
srcData.Write(&progressData.m_dwTotalFileSize, sizeof(progressData.m_dwTotalFileSize));
|
|
|
|
// 패치 타입에 따라서 버전을 다시 세팅.
|
|
PMConst::AdjustVersionInfoByPatchType(lpWorkerData->m_szPatchType,
|
|
lpWorkerData->m_dwMPMinver, lpWorkerData->m_dwMPMaxver);
|
|
|
|
srcData.Write(&lpWorkerData->m_dwMPMinver, sizeof(lpWorkerData->m_dwMPMinver));
|
|
srcData.Write(&lpWorkerData->m_dwMPMaxver, sizeof(lpWorkerData->m_dwMPMaxver));
|
|
|
|
int nInstalledPathLen = 0;
|
|
int nValueNameLen = 0;
|
|
|
|
CString szInstalledPath, szValueName;
|
|
if (PMConst::GetRYLInstalledRegKey(
|
|
lpWorkerData->m_szPatchType, szInstalledPath, szValueName))
|
|
{
|
|
nInstalledPathLen = szInstalledPath.GetLength();
|
|
nValueNameLen = szValueName.GetLength();
|
|
|
|
srcData.Write(&nInstalledPathLen, sizeof(nInstalledPathLen));
|
|
srcData.Write(&nValueNameLen, sizeof(nValueNameLen));
|
|
|
|
srcData.Write(szInstalledPath.GetString(), nInstalledPathLen * sizeof(TCHAR));
|
|
srcData.Write(szValueName.GetString(), nValueNameLen * sizeof(TCHAR));
|
|
}
|
|
else
|
|
{
|
|
srcData.Write(&nInstalledPathLen, sizeof(nInstalledPathLen));
|
|
srcData.Write(&nValueNameLen, sizeof(nValueNameLen));
|
|
}
|
|
|
|
int nSrcLen = static_cast<int>(srcData.GetLength());
|
|
int nDstLen = Base64EncodeGetRequiredLength(nSrcLen);
|
|
|
|
LPSTR lpEncodedData = (LPSTR)malloc(sizeof(CHAR) * (nDstLen + 1));
|
|
if (0 != lpEncodedData)
|
|
{
|
|
BYTE* lpSrcData = srcData.Detach();
|
|
|
|
if (Base64Encode(lpSrcData, nSrcLen, lpEncodedData, &nDstLen))
|
|
{
|
|
lpEncodedData[nDstLen] = (CHAR)0;
|
|
|
|
bSucceededJob =
|
|
zipArchive.SetGlobalComment(lpEncodedData);
|
|
}
|
|
|
|
free(lpSrcData);
|
|
free(lpEncodedData);
|
|
}
|
|
}
|
|
|
|
zipArchive.Close();
|
|
|
|
if (!progressData.m_bStopWorkerThread)
|
|
{
|
|
PMUtil::SetCurrentTime(szErrorString);
|
|
szErrorString.AppendFormat(_T("수동패치 작업 : 압축을 완료하였습니다. SFX파일을 생성하는 중입니다.."));
|
|
|
|
progressData.m_ProgressList.AddTail(szErrorString);
|
|
lpPage->UpdateManualPatchStatus(progressData);
|
|
|
|
if (!bSucceededJob)
|
|
{
|
|
PMUtil::SetCurrentTime(szErrorString);
|
|
szErrorString.AppendFormat(_T("수동패치 작업 실패 : 수동패치 데이터를 Comment로 남기는 데 실패했습니다"));
|
|
progressData.m_ErrorList.AddTail(szErrorString);
|
|
}
|
|
else
|
|
{
|
|
// SFX헤더를 붙여서 파일을 하나 더 만든다.
|
|
CFile sfxFile, zipFile, resultFile;
|
|
PMUtil::SetCurrentTime(szErrorString);
|
|
|
|
if (!sfxFile.Open(lpWorkerData->m_szMPSFXFile,
|
|
CFile::modeRead | CFile::shareDenyWrite | CFile::typeBinary))
|
|
{
|
|
szErrorString.AppendFormat(_T("수동패치 작업 실패 : SFX파일 %s를 읽기 위해 여는 데 실패했습니다"),
|
|
lpWorkerData->m_szMPSFXFile);
|
|
}
|
|
else if(!zipFile.Open(szZipFileName,
|
|
CFile::modeRead | CFile::shareDenyWrite | CFile::typeBinary))
|
|
{
|
|
szErrorString.AppendFormat(
|
|
_T("수동패치 작업 실패 : ZIP파일 %s를 읽기 위해 여는 데 실패했습니다"), szZipFileName);
|
|
}
|
|
else if(!resultFile.Open(szSFXFileName,
|
|
CFile::modeCreate | CFile::modeWrite | CFile::shareDenyWrite | CFile::typeBinary))
|
|
{
|
|
szErrorString.AppendFormat(
|
|
_T("수동패치 작업 실패 : 수동패치 파일 %s를 쓰기 위해 여는 데 실패했습니다"), szSFXFileName);
|
|
}
|
|
else
|
|
{
|
|
const int MAX_FILE_BUFFER = 8192;
|
|
char szTempBuffer[MAX_FILE_BUFFER];
|
|
|
|
UINT nReadBytes = 0;
|
|
UINT nCount = 0;
|
|
|
|
progressData.m_dwTotalFileSize =
|
|
sfxFile.GetLength() + zipFile.GetLength();
|
|
|
|
progressData.m_dwCurrentFileSize = 0;
|
|
|
|
lpPage->UpdateManualPatchStatus(progressData);
|
|
|
|
// 프로그리스 바를 확실히 갱신할 수 있도록 잠시 기다려 준다.
|
|
Sleep(300);
|
|
|
|
// 수동패치 파일에 SFX실행부 붙임
|
|
while(0 < (nReadBytes = sfxFile.Read(szTempBuffer, MAX_FILE_BUFFER)))
|
|
{
|
|
resultFile.Write(szTempBuffer, nReadBytes);
|
|
progressData.m_dwCurrentFileSize += nReadBytes;
|
|
if (0 == (++nCount % 10)) { lpPage->UpdateManualPatchStatus(progressData); }
|
|
}
|
|
|
|
// 수동패치 파일에 ZIP파일 붙임
|
|
while(0 < (nReadBytes = zipFile.Read(szTempBuffer, MAX_FILE_BUFFER)))
|
|
{
|
|
resultFile.Write(szTempBuffer, nReadBytes);
|
|
progressData.m_dwCurrentFileSize += nReadBytes;
|
|
if (0 == (++nCount % 10)) { lpPage->UpdateManualPatchStatus(progressData); }
|
|
}
|
|
|
|
szErrorString.Empty();
|
|
}
|
|
|
|
if (!szErrorString.IsEmpty())
|
|
{
|
|
progressData.m_ErrorList.AddTail(szErrorString);
|
|
lpPage->UpdateManualPatchStatus(progressData);
|
|
}
|
|
}
|
|
|
|
PMUtil::SetCurrentTime(szErrorString);
|
|
szErrorString.AppendFormat(_T("수동패치 작업 완료 : %d 개 파일 중 %d개를 완료하였습니다. 작업을 %s하였습니다"),
|
|
progressData.m_nTotalFiles, progressData.m_nAddedFiles,
|
|
bSucceededJob && progressData.m_nTotalFiles == progressData.m_nAddedFiles ? _T("성공") : _T("실패"));
|
|
|
|
progressData.m_ProgressList.AddTail(szErrorString);
|
|
progressData.m_TotalProgressList.AddTail(szErrorString);
|
|
}
|
|
}
|
|
CATCH_ALL(e)
|
|
{
|
|
e->GetErrorMessage(szErrorMsg, MAX_ERROR_LEN - 1);
|
|
szErrorMsg[MAX_ERROR_LEN - 1] = 0;
|
|
|
|
PMUtil::SetCurrentTime(szErrorString);
|
|
if (e->IsKindOf(RUNTIME_CLASS(CZipException)))
|
|
{
|
|
szErrorString.AppendFormat(
|
|
_T("작업 실패 : CZipArchive 라이브러리에서 에러가 발생했습니다. %s"), szErrorMsg);
|
|
}
|
|
else if (e->IsKindOf(RUNTIME_CLASS(CFileException)))
|
|
{
|
|
szErrorString.AppendFormat(
|
|
_T("작업 실패 : 파일 관련 작업에서 에러가 발생했습니다. %s"), szErrorMsg);
|
|
}
|
|
else
|
|
{
|
|
szErrorString.AppendFormat(
|
|
_T("작업 실패 : 알 수 없는 작업에서 에러가 발생했습니다. %s"), szErrorMsg);
|
|
}
|
|
|
|
progressData.m_ErrorList.AddTail(szErrorString);
|
|
progressData.m_TotalProgressList.AddTail(szErrorString);
|
|
|
|
e->Delete();
|
|
}
|
|
END_CATCH_ALL
|
|
}
|
|
|
|
// 데이터를 업데이트한다.
|
|
lpPage->UpdateManualPatchStatus(progressData);
|
|
|
|
delete lpWorkerData;
|
|
return 0;
|
|
}
|
|
|
|
void CManualPatchPage::OnBnClickedMpCreate()
|
|
{
|
|
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
|
|
UpdateData(TRUE);
|
|
|
|
CFile file;
|
|
|
|
if (0 == m_szMPFileName.GetLength() ||
|
|
!file.Open(m_szMPFileName, CFile::modeCreate))
|
|
{
|
|
MessageBox(_T("수동패치 파일 이름이 없거나, 올바른 파일 이름이 아닙니다. 다시 입력해주세요"),
|
|
_T("올바르지 않은 파일 이름"), MB_OK | MB_ICONERROR);
|
|
}
|
|
else
|
|
{
|
|
file.Close();
|
|
DeleteFile(m_szMPFileName);
|
|
|
|
if (!theApp.IsManualPatchOperate())
|
|
{
|
|
MPWorkerData* lpWorkerData = new MPWorkerData;
|
|
if (lpWorkerData)
|
|
{
|
|
lpWorkerData->m_lpPage = this;
|
|
lpWorkerData->m_szMPFolder.SetString(m_szMPFolder);
|
|
lpWorkerData->m_szMPSFXFile.SetString(m_szMPSFXFile);
|
|
lpWorkerData->m_szMPFileName.SetString(m_szMPFileName);
|
|
lpWorkerData->m_szPatchType.SetString(m_szPatchType);
|
|
lpWorkerData->m_dwMPMaxver = m_dwMPMaxver;
|
|
lpWorkerData->m_dwMPMinver = m_dwMPMinver;
|
|
|
|
InitMPWorkerData();
|
|
|
|
unsigned int nThreadID = 0;
|
|
m_hManualPatchThread = reinterpret_cast<HANDLE>(
|
|
_beginthreadex(0, 0, ManualPatchWorker, lpWorkerData, 0, &nThreadID));
|
|
|
|
if (0 == m_hManualPatchThread)
|
|
{
|
|
MessageBox(_T("수동패치 스레드를 생성할 수 없습니다"),
|
|
_T("생성 에러"), MB_OK | MB_ICONERROR);
|
|
}
|
|
else
|
|
{
|
|
static UINT nManualPatchDlgItems[] =
|
|
{
|
|
ID_MP_CREATE,
|
|
IDC_ED_MP_PATCH_FILE_NAME,
|
|
IDC_BTN_MP_SELECT_FILE
|
|
};
|
|
|
|
PMUtil::EnableDlgItems(*this, nManualPatchDlgItems,
|
|
sizeof(nManualPatchDlgItems)/sizeof(UINT), false);
|
|
|
|
CWnd* lpWnd = GetDlgItem(ID_MP_CANCEL);
|
|
if (lpWnd) { lpWnd->EnableWindow(TRUE); }
|
|
|
|
// 프로그리스 바 출력을 위한 타이머를 돌린다.
|
|
SetTimer(PMConst::MP_PROGRESS_TIMER_ID,
|
|
PMConst::PROGRESS_TIMER_UPDATE_TIME, 0);
|
|
|
|
// 수동패치가 돌고 있다고 설정한다.
|
|
theApp.SetManualPatchOperate(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CManualPatchPage::OnBnClickedMpCancel()
|
|
{
|
|
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
|
|
|
|
if (theApp.IsManualPatchOperate())
|
|
{
|
|
// 이미 스레드가 작동하고 있다면, 스레드를 취소하도록 변수를 세팅한다.
|
|
StopWorker();
|
|
}
|
|
}
|
|
|
|
void CManualPatchPage::OnBnClickedBtnMpSelectFile()
|
|
{
|
|
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
|
|
PMUtil::BrowseForNewFile(GetSafeHwnd(),
|
|
_T("수동패치 파일 이름을 선택해주세요"),
|
|
_T("Manual Patch File\0*.exe"), _T("exe"), m_szMPFileName);
|
|
|
|
UpdateData(FALSE);
|
|
}
|
|
|
|
BOOL CManualPatchPage::OnSetActive()
|
|
{
|
|
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
|
|
|
|
// 데이터를 셋업에서 읽어옵니다.
|
|
|
|
if (!theApp.IsManualPatchOperate())
|
|
{
|
|
// 스레드가 동작중이 아닌 경우에만 데이터를 갱신합니다.
|
|
m_pmSettingOption.GetSettingData(PMConst::PatchType, m_szPatchType);
|
|
m_pmSettingOption.GetSettingData(PMConst::MP_Folder, m_szMPFolder);
|
|
m_pmSettingOption.GetSettingData(PMConst::MP_SFXFile, m_szMPSFXFile);
|
|
|
|
CString szValue;
|
|
m_pmSettingOption.GetSettingData(PMConst::MP_MinVer, szValue);
|
|
m_dwMPMinver = atol(szValue);
|
|
|
|
UpdateData(FALSE);
|
|
}
|
|
|
|
return CPropertyPage::OnSetActive();
|
|
}
|
|
|
|
BOOL CManualPatchPage::OnInitDialog()
|
|
{
|
|
CPropertyPage::OnInitDialog();
|
|
|
|
// TODO: 여기에 추가 초기화 작업을 추가합니다.
|
|
|
|
// Cancel버튼은 처음에는 Disable한다.
|
|
CWnd* lpWnd = GetDlgItem(ID_MP_CANCEL);
|
|
if (lpWnd) { lpWnd->EnableWindow(FALSE); }
|
|
|
|
m_edProgressLog.SetLimitText(UINT_MAX);
|
|
m_edErrLog.SetLimitText(UINT_MAX);
|
|
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
|
// 예외: OCX 속성 페이지는 FALSE를 반환해야 합니다.
|
|
}
|
|
|
|
|
|
|
|
afx_msg void CManualPatchPage::OnTimer(UINT_PTR nIDEvent)
|
|
{
|
|
if (nIDEvent == PMConst::MP_PROGRESS_TIMER_ID)
|
|
{
|
|
int nLowerRange = 0;
|
|
int nUpperRange = 0;
|
|
int nCurrentPos = m_TotalProgressBar.GetPos();
|
|
m_TotalProgressBar.GetRange(nLowerRange, nUpperRange);
|
|
|
|
//! MANUAL_PATCH_LOCK
|
|
m_ProgressLock.Lock();
|
|
|
|
if (m_ProgressData.m_dwTotalFileSize != nUpperRange)
|
|
{
|
|
// 전체 Range가 변경되면 갱신한다 - 2.1기가 이상을 수동패치 할 일은 없다;;
|
|
m_TotalProgressBar.SetRange32(0,
|
|
static_cast<int>(m_ProgressData.m_dwTotalFileSize));
|
|
}
|
|
|
|
if (m_ProgressData.m_dwCurrentFileSize != nCurrentPos)
|
|
{
|
|
// 파일 위치가 변경되면 갱신한다.
|
|
m_TotalProgressBar.SetPos(
|
|
static_cast<int>(m_ProgressData.m_dwCurrentFileSize));
|
|
}
|
|
|
|
m_szFileProgress.Format(_T("%d/%d"),
|
|
m_ProgressData.m_nAddedFiles, m_ProgressData.m_nTotalFiles);
|
|
|
|
// 로그를 출력한다.
|
|
POSITION pos = m_ProgressData.m_ProgressList.GetHeadPosition();
|
|
while(0 != pos) { m_edProgressLog.AddLine(m_ProgressData.m_ProgressList.GetNext(pos)); }
|
|
|
|
pos = m_ProgressData.m_TotalProgressList.GetHeadPosition();
|
|
while(0 != pos) { theApp.AddTotalProgressLog(m_ProgressData.m_TotalProgressList.GetNext(pos)); }
|
|
|
|
pos = m_ProgressData.m_ErrorList.GetHeadPosition();
|
|
while(0 != pos) { m_edErrLog.AddLine(m_ProgressData.m_ErrorList.GetNext(pos)); }
|
|
|
|
m_ProgressData.m_ProgressList.RemoveAll();
|
|
m_ProgressData.m_TotalProgressList.RemoveAll();
|
|
m_ProgressData.m_ErrorList.RemoveAll();
|
|
|
|
//! MANUAL_PATCH_UNLOCK
|
|
m_ProgressLock.Unlock();
|
|
|
|
UpdateData(FALSE);
|
|
|
|
if (WAIT_OBJECT_0 == WaitForSingleObject(m_hManualPatchThread, 0))
|
|
{
|
|
// 스레드가 종료되었다. 타이머를 제거하고, 다시 수동패치 옵션을 Enable한다.
|
|
StopWorker();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CManualPatchPage::InitMPWorkerData()
|
|
{
|
|
m_hManualPatchThread = 0;
|
|
|
|
m_ProgressData.m_bStopWorkerThread = FALSE;
|
|
m_ProgressData.m_nTotalFiles = 0;
|
|
m_ProgressData.m_nAddedFiles = 0;
|
|
m_ProgressData.m_ProgressList.RemoveAll();
|
|
m_ProgressData.m_ErrorList.RemoveAll();
|
|
}
|
|
|
|
void CManualPatchPage::StopWorker()
|
|
{
|
|
if (0 != m_hManualPatchThread)
|
|
{
|
|
m_ProgressLock.Lock(); //! MANUAL_PATCH_LOCK
|
|
m_ProgressData.m_bStopWorkerThread = TRUE;
|
|
m_ProgressLock.Unlock(); //! MANUAL_PATCH_UNLOCK
|
|
|
|
WaitForSingleObject(m_hManualPatchThread, INFINITE);
|
|
CloseHandle(m_hManualPatchThread);
|
|
|
|
KillTimer(PMConst::MP_PROGRESS_TIMER_ID);
|
|
InitMPWorkerData();
|
|
|
|
static UINT nManualPatchDlgItems[] =
|
|
{
|
|
ID_MP_CREATE,
|
|
IDC_ED_MP_PATCH_FILE_NAME,
|
|
IDC_BTN_MP_SELECT_FILE
|
|
};
|
|
|
|
PMUtil::EnableDlgItems(*this, nManualPatchDlgItems,
|
|
sizeof(nManualPatchDlgItems)/sizeof(UINT), true);
|
|
|
|
CWnd* lpWnd = GetDlgItem(ID_MP_CANCEL);
|
|
if (lpWnd) { lpWnd->EnableWindow(FALSE); }
|
|
|
|
theApp.SetManualPatchOperate(false);
|
|
}
|
|
}
|
|
|
|
void CManualPatchPage::OnClose()
|
|
{
|
|
// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
|
|
StopWorker();
|
|
CPropertyPage::OnClose();
|
|
}
|
|
|
|
void CManualPatchPage::OnBnClickedClearLog()
|
|
{
|
|
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
|
|
m_edProgressLog.SetWindowText(_T(""));
|
|
m_edErrLog.SetWindowText(_T(""));
|
|
}
|