Skip to content

Instantly share code, notes, and snippets.

@kizernis
Created September 2, 2018 12:18
Show Gist options
  • Save kizernis/b340545e56f0f186084a7dab93188feb to your computer and use it in GitHub Desktop.
Save kizernis/b340545e56f0f186084a7dab93188feb to your computer and use it in GitHub Desktop.
#include <windows.h>
#include <commctrl.h>
#include <shlobj.h>
#include "resource.h"
#include "md5.h"
#ifndef _DEBUG
#pragma comment(linker, "/entry:_WinMain /nodefaultlib /subsystem:windows /filealign:512 /stack:65536,65536")
#pragma comment(linker, "/merge:.data=.text /merge:.rdata=.text /section:.text,ewrx /ignore:4078")
#endif
typedef struct
{
PSTR psName;
UINT nNameLen;
PSTR psType;
UINT nTypeLen;
} LOGGED_GAMES_ARRAY;
#define LG_DATA_CHUNK_SIZE (1024 * 50)
LOGGED_GAMES_ARRAY * g_pLoggedGames;
UINT g_nLoggedGames = 0;
UINT g_nLoggedGamesMax;
PUCHAR g_pucLoggedGamesData;
PUCHAR g_pucLoggedGamesDataPtr;
UINT g_nLoggedGamesBufferSize;
BOOL g_bLogsPathChanged = FALSE;
void LoggedGames_Reset()
{
if (g_nLoggedGames)
{ g_nLoggedGames = 0; free(g_pLoggedGames); free(g_pucLoggedGamesData); }
g_nLoggedGamesMax = 100;
g_pLoggedGames = (LOGGED_GAMES_ARRAY *)malloc(sizeof(LOGGED_GAMES_ARRAY) * g_nLoggedGamesMax);
g_nLoggedGamesBufferSize = LG_DATA_CHUNK_SIZE;
g_pucLoggedGamesData = (PUCHAR)malloc(g_nLoggedGamesBufferSize);
g_pucLoggedGamesDataPtr = g_pucLoggedGamesData;
}
void LoggedGames_Add(PSTR psName, UINT nNameLen, PSTR psType, UINT nTypeLen)
{
UINT nRequiredSize, i;
g_nLoggedGames++;
if (g_nLoggedGames > g_nLoggedGamesMax)
{ g_nLoggedGamesMax += 100; g_pLoggedGames = (LOGGED_GAMES_ARRAY *)realloc(g_pLoggedGames, sizeof(LOGGED_GAMES_ARRAY) * g_nLoggedGamesMax); }
nRequiredSize = g_pucLoggedGamesDataPtr + nNameLen + 1 + nTypeLen + 1 - g_pucLoggedGamesData;
while (nRequiredSize > g_nLoggedGamesBufferSize)
{
PUCHAR pucOldAddress = g_pucLoggedGamesData;
g_nLoggedGamesBufferSize += LG_DATA_CHUNK_SIZE;
g_pucLoggedGamesData = (PUCHAR)realloc(g_pucLoggedGamesData, g_nLoggedGamesBufferSize);
if (g_pucLoggedGamesData != pucOldAddress)
{
int nDiff = g_pucLoggedGamesData - pucOldAddress;
for (i = 0; i < g_nLoggedGames - 1; i++)
{ g_pLoggedGames[i].psName += nDiff; g_pLoggedGames[i].psType += nDiff; }
g_pucLoggedGamesDataPtr += nDiff;
}
}
i = g_nLoggedGames - 1;
g_pLoggedGames[i].psName = (PSTR)g_pucLoggedGamesDataPtr; g_pLoggedGames[i].nNameLen = nNameLen;
strcpy(g_pucLoggedGamesDataPtr, psName); g_pucLoggedGamesDataPtr += nNameLen + 1;
g_pLoggedGames[i].psType = (PSTR)g_pucLoggedGamesDataPtr; g_pLoggedGames[i].nTypeLen = nTypeLen;
strcpy(g_pucLoggedGamesDataPtr, psType); g_pucLoggedGamesDataPtr += nTypeLen + 1;
}
#define GAME_NOT_FOUND 0xFFFFFFFF
UINT LoggedGames_Find(PSTR psName)
{
UINT i;
for (i = 0; i < g_nLoggedGames; i++)
{
if (0 == strcmp(psName, g_pLoggedGames[i].psName))
return i;
}
return GAME_NOT_FOUND;
}
typedef struct { UCHAR ucMD[16]; } MD_ARRAY;
MD_ARRAY * g_pHashes;
UINT g_nHashes = 0;
UINT g_nHashesMax;
UCHAR g_ucMD5Sum[16];
void Hashes_Reset()
{
if (g_nHashes)
{ g_nHashes = 0; free(g_pHashes); }
g_nHashesMax = 100;
g_pHashes = (MD_ARRAY *)malloc(16 * g_nHashesMax);
}
void Hashes_Add()
{
if (++g_nHashes > g_nHashesMax)
{ g_nHashesMax += 100; g_pHashes = (MD_ARRAY *)realloc(g_pHashes, 16 * g_nHashesMax); }
memcpy(g_pHashes[g_nHashes - 1].ucMD, g_ucMD5Sum, 16);
}
BOOL Hashes_IsStored()
{
PUCHAR p1 = g_pHashes[0].ucMD;
PUCHAR p2 = p1 + 16 * g_nHashes;
for (; p1 < p2; p1 += 16)
{
if (p1[ 0] == g_ucMD5Sum[ 0] && p1[ 1] == g_ucMD5Sum[ 1] && p1[ 2] == g_ucMD5Sum[ 2] && p1[ 3] == g_ucMD5Sum[ 3]
&& p1[ 4] == g_ucMD5Sum[ 4] && p1[ 5] == g_ucMD5Sum[ 5] && p1[ 6] == g_ucMD5Sum[ 6] && p1[ 7] == g_ucMD5Sum[ 7]
&& p1[ 8] == g_ucMD5Sum[ 8] && p1[ 9] == g_ucMD5Sum[ 9] && p1[10] == g_ucMD5Sum[10] && p1[11] == g_ucMD5Sum[11]
&& p1[12] == g_ucMD5Sum[12] && p1[13] == g_ucMD5Sum[13] && p1[14] == g_ucMD5Sum[14] && p1[15] == g_ucMD5Sum[15])
return TRUE;
}
return FALSE;
}
// TODO: use Unicode APIs with \\?\ path prefix and 32768 max path length (and 256 file or directory name length)
#define PATH_BUFFER_SIZE (MAX_PATH * 2)
#define LOG_BUFFER_SIZE (1024 * 30)
HWND g_hDlg, g_hBtnExtract, g_hLblStatus, g_hPgbProgress;
BOOL g_bProcessing;
PSTR g_psPathLogs, g_psPathReplays, g_psPathMaps;
UINT g_nPathLogsLen, g_nPathReplaysLen, g_nPathMapsLen;
BOOL g_bWarned_WA, g_bWarned_Maps;
PCSTR g_pcsAlert_WA, g_pcsAlert_Replays, g_pcsAlert_Maps;
#define STATE_NORMAL 0
#define STATE_WARNING 1
#define STATE_ALERT 2
#define NORMAL_STATUS_BGCOLOR RGB(0, 255, 115)
#define WARNING_STATUS_BGCOLOR RGB(255, 224, 4)
#define ALERT_STATUS_BGCOLOR RGB(255, 128, 0)
HBRUSH g_hNormalBrush, g_hWarningBrush, g_hAlertBrush;
UINT g_nState = STATE_NORMAL;
PCSTR g_csCurrentStatus;
int g_nMessageLength, g_nPos;
char g_sMsgTmp[256];
char g_sMsgFinal[256];
typedef void (* SEARCH_CALLBACK_PROC)(PSTR, PCSTR, UINT);
typedef unsigned char uch;
typedef unsigned short ush;
typedef unsigned long ulg;
#define SH(p) ((ush)(uch)((p)[1]) | ((ush)(uch)((p)[0]) << 8))
#define LG(p) ((ulg)(SH((p) + 2)) | ((ulg)(SH(p)) << 16))
// 8 - signature
// + 4 + 4 + 13 + 4 - IHDR
// + 4 + 4 + 40 + 4 - waLV
// + 4 + 4 + 0 + 4 - IEND
// #define MIN_PNG_FILE_SIZE 97
#define MAP_TYPE_BIT 1
#define MAP_TYPE_LEV 2
#define MAP_TYPE_PNG 3
#define TIMER_PROCESSING 0
#define TIMER_TYPING 1
#define TIMER_BLINKING 2
#ifndef _DEBUG
PVOID malloc(UINT nSize)
{
return HeapAlloc(GetProcessHeap(), 0, nSize);
}
PVOID realloc(PVOID pBuf, UINT nSize)
{
return HeapReAlloc(GetProcessHeap(), 0, pBuf, nSize);
}
void free(PVOID pBuf)
{
HeapFree(GetProcessHeap(), 0, pBuf);
}
UINT strlen(PCSTR pcsStr)
{
PCSTR p1;
for (p1 = pcsStr; *p1; ++p1);
return (p1 - pcsStr);
}
int memcmp(const void * s1, const void * s2, size_t n)
{
const unsigned char *p1 = s1, *p2 = s2;
while (n--) if (*p1 != *p2) return *p1 - *p2; else *p1++, *p2++;
return 0;
}
PSTR strstr(PCSTR pcsStr1, PCSTR pcsStr2)
{
UINT nLen = strlen(pcsStr2);
while (*pcsStr1)
if (!memcmp(pcsStr1++, pcsStr2, nLen))
return (PSTR)pcsStr1 - 1;
return NULL;
}
PVOID memcpy(PVOID pDst, const PVOID pSrc, UINT nCnt)
{
PUCHAR pucDst = pDst;
const unsigned char * pucSrc = pSrc;
while (nCnt--)
*pucDst++ = *pucSrc++;
return pDst;
}
PSTR strcpy(PSTR psDst, PCSTR pcsSrc)
{
PSTR psSave = psDst;
while (*psDst++ = *pcsSrc++);
return psSave;
}
int strcmp(PCSTR pcsStr1, PCSTR pcsStr2)
{
for(; *pcsStr1 == *pcsStr2; ++pcsStr1, ++pcsStr2)
if (*pcsStr1 == 0) return 0;
return *(const unsigned char *)pcsStr1 - *(const unsigned char *)pcsStr2;
}
PSTR strchr(PCSTR pcsStr, int nChar)
{
while (*pcsStr != (char)nChar)
if (!*pcsStr++) return NULL;
return (PSTR)pcsStr;
}
PSTR strrchr(PCSTR pcsStr, int nChar)
{
PSTR psRet = NULL;
do
{ if (*pcsStr == (char)nChar) (PCSTR)psRet = pcsStr; }
while (*pcsStr++);
return psRet;
}
#endif
void CALLBACK BlinkingTimerProc(HWND hWnd, UINT nMsg, UINT nEventID, DWORD dwTime)
{
char sTmp[256];
int nLen = GetWindowText(g_hLblStatus, sTmp, sizeof(sTmp) - 2);
if (sTmp[nLen - 1] == '_')
sTmp[nLen - 1] = '\0';
else
{ sTmp[nLen] = '_'; sTmp[nLen + 1] = '\0'; }
SetWindowText(g_hLblStatus, sTmp);
}
void CALLBACK TypingTimerProc(HWND hWnd, UINT nMsg, UINT nEventID, DWORD dwTime)
{
g_sMsgTmp[g_nPos - 1] = g_csCurrentStatus[g_nPos - 1];
g_sMsgTmp[g_nPos] = '_';
g_sMsgTmp[g_nPos + 1] = '\0';
SetWindowText(g_hLblStatus, g_sMsgTmp);
if (++g_nPos > g_nMessageLength)
KillTimer(g_hDlg, TIMER_TYPING);
}
void TypeStatusMessage(PCSTR csMessage, UINT nState)
{
g_nState = nState;
g_csCurrentStatus = csMessage;
lstrcpy(&g_sMsgTmp[1], &csMessage[1]);
g_nMessageLength = strlen(csMessage); g_nPos = 1;
SetTimer(g_hDlg, TIMER_TYPING, 20, TypingTimerProc);
}
BOOL ExtractMapToFile(HANDLE hSrcFile, DWORD dwMapDataSize)
{
UCHAR ucBuffer[1024];
DWORD i, j, dwRemainder, dwActual;
HANDLE hDstFile = CreateFile(g_psPathMaps, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDstFile == INVALID_HANDLE_VALUE)
return FALSE;
j = dwMapDataSize / sizeof(ucBuffer);
if (dwRemainder = dwMapDataSize % sizeof(ucBuffer))
{
if (!ReadFile(hSrcFile, ucBuffer, dwRemainder, &dwActual, NULL) || !dwActual) { CloseHandle(hDstFile); return FALSE; }
if (!WriteFile(hDstFile, ucBuffer, dwRemainder, &dwActual, NULL)) { CloseHandle(hDstFile); return FALSE; }
}
for (i = 0; i < j; i++)
{
if (!ReadFile(hSrcFile, ucBuffer, sizeof(ucBuffer), &dwActual, NULL) || !dwActual) { CloseHandle(hDstFile); return FALSE; }
if (!WriteFile(hDstFile, ucBuffer, sizeof(ucBuffer), &dwActual, NULL)) { CloseHandle(hDstFile); return FALSE; }
}
CloseHandle(hDstFile);
return TRUE;
}
#define MAX_IDAT_TAGS_TO_FIND 2
BOOL GetColourMapHash(HANDLE hFile) // размер карты не нужен
{
md5_context ctx;
UCHAR ucBuffer[4];
DWORD dwActual, dwSize, nIDATsFound;
BOOL bClose = FALSE;
if (hFile == INVALID_HANDLE_VALUE)
{
bClose = TRUE;
hFile = CreateFile(g_psPathMaps, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
}
if (0xFFFFFFFF == SetFilePointer(hFile, 33, NULL, FILE_CURRENT)) { if (bClose) CloseHandle(hFile); return FALSE; }
if (!ReadFile(hFile, ucBuffer, 4, &dwActual, NULL) || !dwActual) { if (bClose) CloseHandle(hFile); return FALSE; }
dwSize = LG(ucBuffer);
if (!ReadFile(hFile, ucBuffer, 4, &dwActual, NULL) || !dwActual) { if (bClose) CloseHandle(hFile); return FALSE; }
if ((ucBuffer[0] != 'w' || ucBuffer[1] != 'a' || ucBuffer[2] != 'L' || ucBuffer[3] != 'V')
&& (ucBuffer[0] != 'w' || ucBuffer[1] != '2' || ucBuffer[2] != 'l' || ucBuffer[3] != 'v'))
{ if (bClose) CloseHandle(hFile); return FALSE; }
if (0xFFFFFFFF == SetFilePointer(hFile, dwSize + 4, NULL, FILE_CURRENT)) { if (bClose) CloseHandle(hFile); return FALSE; }
md5_starts(&ctx);
nIDATsFound = 0;
for (;;)
{
if (!ReadFile(hFile, ucBuffer, 4, &dwActual, NULL) || !dwActual) { if (bClose) CloseHandle(hFile); return FALSE; }
dwSize = LG(ucBuffer);
if (!ReadFile(hFile, ucBuffer, 4, &dwActual, NULL) || !dwActual) { if (bClose) CloseHandle(hFile); return FALSE; }
if (ucBuffer[0] == 'I' && ucBuffer[1] == 'E' && ucBuffer[2] == 'N' && ucBuffer[3] == 'D')
{ if (nIDATsFound) break; else { if (bClose) CloseHandle(hFile); return FALSE; }}
if (ucBuffer[0] == 'I' && ucBuffer[1] == 'D' && ucBuffer[2] == 'A' && ucBuffer[3] == 'T')
{
if (0xFFFFFFFF == SetFilePointer(hFile, dwSize, NULL, FILE_CURRENT)) { if (bClose) CloseHandle(hFile); return FALSE; }
if (!ReadFile(hFile, ucBuffer, 4, &dwActual, NULL) || !dwActual) { if (bClose) CloseHandle(hFile); return FALSE; }
md5_update(&ctx, ucBuffer, sizeof(ucBuffer));
if (++nIDATsFound == MAX_IDAT_TAGS_TO_FIND)
break;
}
else
if (0xFFFFFFFF == SetFilePointer(hFile, dwSize + 4, NULL, FILE_CURRENT)) { if (bClose) CloseHandle(hFile); return FALSE; }
}
if (bClose) CloseHandle(hFile);
md5_finish(&ctx, g_ucMD5Sum);
return TRUE;
}
BOOL GetMonochromeMapHash(HANDLE hFile, DWORD dwMapDataSize)
{
md5_context ctx;
UCHAR ucBuffer[1024];
DWORD i, j, dwRemainder, dwActual;
BOOL bClose = FALSE;
if (hFile == INVALID_HANDLE_VALUE)
{
bClose = TRUE;
hFile = CreateFile(g_psPathMaps, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
}
md5_starts(&ctx);
if (!dwMapDataSize)
dwMapDataSize = GetFileSize(hFile, NULL);
j = dwMapDataSize / sizeof(ucBuffer);
if (dwRemainder = dwMapDataSize % sizeof(ucBuffer))
{
if (!ReadFile(hFile, ucBuffer, dwRemainder, &dwActual, NULL) || !dwActual) { if (bClose) CloseHandle(hFile); return FALSE; }
md5_update(&ctx, ucBuffer, dwActual);
}
for (i = 0; i < j; i++)
{
if (!ReadFile(hFile, ucBuffer, sizeof(ucBuffer), &dwActual, NULL) || !dwActual) { if (bClose) CloseHandle(hFile); return FALSE; }
md5_update(&ctx, ucBuffer, dwActual);
}
if (bClose) CloseHandle(hFile);
md5_finish(&ctx, g_ucMD5Sum);
return TRUE;
}
void FileSearch(PSTR psPathBuffer, PSTR psDirOffset, PCSTR * ppcsExtensions, UINT nExtensionsCount, SEARCH_CALLBACK_PROC pfnCallbackProc)
{
HANDLE hSearch;
WIN32_FIND_DATA wfd;
UINT i, nLen;
PSTR p1, p2;
p1 = psDirOffset;
p1[0] = '*'; p1[1] = '.'; p1[2] = '*'; p1[3] = '\0';
hSearch = FindFirstFile(psPathBuffer, &wfd);
if (hSearch == INVALID_HANDLE_VALUE)
return;
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
&& wfd.cFileName[0] == '.' && wfd.cFileName[1] == '\0')
{
FindNextFile(hSearch, &wfd);
if (!FindNextFile(hSearch, &wfd))
{ FindClose(hSearch); return; }
}
do
{
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
nLen = strlen(wfd.cFileName);
memcpy(p1, wfd.cFileName, nLen);
p1[nLen] = '\\'; p1[nLen + 1] = '\0';
FileSearch(psPathBuffer, p1 + nLen + 1, ppcsExtensions, nExtensionsCount, pfnCallbackProc);
}
else
{
p2 = strrchr(wfd.cFileName, '.');
if (!p2) p2 = ""; else p2++;
for (i = 0; i < nExtensionsCount; i++)
if (lstrcmpi(p2, ppcsExtensions[i]) == 0)
{ pfnCallbackProc(p1, wfd.cFileName, i); break; }
}
}
while (FindNextFile(hSearch, &wfd));
FindClose(hSearch);
}
UINT g_nFilesFound;
void CountingFileSearchCallback(PSTR psDir, PCSTR pcsFileName, UINT nExtensionIndex)
{
g_nFilesFound++;
}
void HashExistingMaps__FSCallback(PSTR psDir, PCSTR pcsFileName, UINT nExtensionIndex)
{
SendMessage(g_hPgbProgress, PBM_STEPIT, 0, 0);
strcpy(psDir, pcsFileName);
if (MAP_TYPE_PNG == nExtensionIndex + 1)
{ if (!GetColourMapHash(INVALID_HANDLE_VALUE)) return; }
else
{ if (!GetMonochromeMapHash(INVALID_HANDLE_VALUE, 0)) return; }
if (!Hashes_IsStored())
Hashes_Add();
}
void HashExistingMaps()
{
PCSTR ppcsExtensions[] = { "bit", "lev", "png" };
PSTR p1 = g_psPathMaps + g_nPathMapsLen;
Hashes_Reset();
g_nFilesFound = 0;
FileSearch(g_psPathMaps, p1, ppcsExtensions, 3, CountingFileSearchCallback);
if (!g_nFilesFound)
return;
SendMessage(g_hPgbProgress, PBM_SETRANGE, 0, MAKELPARAM(0, g_nFilesFound));
FileSearch(g_psPathMaps, p1, ppcsExtensions, 3, HashExistingMaps__FSCallback);
SendMessage(g_hPgbProgress, PBM_SETPOS, 0, 0);
}
DWORD g_dwLogBufSize;
PSTR g_psLogBuffer;
void GuessGameTypes__FSCallback(PSTR psDir, PCSTR pcsFileName, UINT nExtensionIndex)
{
HANDLE hFile;
DWORD dwFileSize, dwActual;
PSTR p1, p2;
PSTR psGameFileName, psGameType, psGameAuthor;
UINT nGameFileNameLen;
SendMessage(g_hPgbProgress, PBM_STEPIT, 0, 0);
if (strstr(pcsFileName, "DirectIP - Direct IP")
|| strstr(pcsFileName, "DirectIP - - Direct IP"))
return;
strcpy(psDir, pcsFileName);
hFile = CreateFile(g_psPathLogs, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
dwFileSize = GetFileSize(hFile, NULL);
if (dwFileSize < 30)
{ CloseHandle(hFile); return; }
if (dwFileSize + 1 > g_dwLogBufSize)
{ g_dwLogBufSize = dwFileSize + 1; g_psLogBuffer = (PSTR)realloc(g_psLogBuffer, g_dwLogBufSize); }
if (!ReadFile(hFile, g_psLogBuffer, dwFileSize, &dwActual, NULL) || !dwActual) { CloseHandle(hFile); return; }
CloseHandle(hFile);
g_psLogBuffer[dwActual] = '\0';
if (!(p1 = strstr(g_psLogBuffer, "recorded as file \"User\\Games\\")))
return;
psGameFileName = p1 + 29;
p1 = strchr(psGameFileName + 32, '"'); *p1 = '\0';
nGameFileNameLen = p1 - psGameFileName;
psGameType = strstr(psDir + 23, " - ") + 3;
if (!(p1 = strchr(psGameType, '+')))
p1 = strrchr(psGameType, '.');
else
p1--;
*p1 = '\0';
psGameAuthor = strrchr(psGameType, ' ') + 1;
*(psGameAuthor - 3) = '\0';
if (strcmp(psGameAuthor, "HostingBuddy") == 0)
{
if (p1 = strstr(psGameType, "#039"))
psGameType = p1 + (*(p1 + 4) == 's' ? 6 : 5); // #039s_ #039_
else
{
if (p1 = strstr(psGameType, "_for_"))
{
while (p2 = strstr(p1 + 5, "_for_")) p1 = p2;
*p1 = '\0';
}
}
}
else
{ if (*psGameType == 'Я' || *psGameType == '_') psGameType++; }
LoggedGames_Add(psGameFileName, nGameFileNameLen, psGameType, strlen(psGameType));
}
void GuessGameTypes()
{
PCSTR ppcsExtensions[] = { "log" };
PSTR p1;
if (!g_nPathLogsLen)
return;
LoggedGames_Reset();
p1 = g_psPathLogs + g_nPathLogsLen;
g_nFilesFound = 0;
FileSearch(g_psPathLogs, p1, ppcsExtensions, 1, CountingFileSearchCallback);
if (!g_nFilesFound)
return;
SendMessage(g_hPgbProgress, PBM_SETRANGE, 0, MAKELPARAM(0, g_nFilesFound));
g_dwLogBufSize = LOG_BUFFER_SIZE;
g_psLogBuffer = (PSTR)malloc(LOG_BUFFER_SIZE);
FileSearch(g_psPathLogs, p1, ppcsExtensions, 1, GuessGameTypes__FSCallback);
free(g_psLogBuffer);
SendMessage(g_hPgbProgress, PBM_SETPOS, 0, 0);
}
UINT g_nDuplicates, g_nMapsExtracted;
void ExtractMaps__FSCallback(PSTR psDir, PCSTR pcsFileName, UINT nExtensionIndex)
{
HANDLE hFile;
PSTR p1, p2;
int nLen, i;
unsigned char ucTmp[4 * 3];
DWORD dwMapSize, dwMapType, dwActual, dwPointer;
SendMessage(g_hPgbProgress, PBM_STEPIT, 0, 0);
strcpy(psDir, pcsFileName);
hFile = CreateFile(g_psPathReplays, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (!ReadFile(hFile, ucTmp, sizeof(ucTmp), &dwActual, NULL)
|| dwActual != sizeof(ucTmp)
|| ucTmp[0] != 'W'
|| ucTmp[1] != 'A')
{ CloseHandle(hFile); return; }
dwMapSize = *((DWORD *)(ucTmp + 4)) - 4;
dwMapType = *((DWORD *)(ucTmp + 8));
if (dwMapType != MAP_TYPE_BIT && dwMapType != MAP_TYPE_LEV && dwMapType != MAP_TYPE_PNG)
{ CloseHandle(hFile); return; }
dwPointer = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
if (dwMapType == MAP_TYPE_PNG)
{ if (!GetColourMapHash(hFile)) { CloseHandle(hFile); return; } }
else
{ if (!GetMonochromeMapHash(hFile, dwMapSize)) { CloseHandle(hFile); return; } }
SetFilePointer(hFile, dwPointer, NULL, FILE_BEGIN);
if (Hashes_IsStored())
{ g_nDuplicates++; CloseHandle(hFile); return; }
Hashes_Add();
i = LoggedGames_Find(psDir);
p2 = g_psPathMaps + g_nPathMapsLen;
if (i != GAME_NOT_FOUND)
{
if (p1 = strchr(psDir, '['))
{
nLen = p1 - psDir + 1;
memcpy(p2, psDir, nLen);
p2 += nLen;
strcpy(p2, g_pLoggedGames[i].psType);
p2 += g_pLoggedGames[i].nTypeLen;
p1 = strchr(p1 + 1, ']');
nLen = strrchr(p1 + 1, '.') - p1 + 1;
memcpy(p2, p1, nLen);
p2 += nLen;
}
else
{
// how can this happen?
nLen = strrchr(psDir, '.') - psDir; memcpy(p2, psDir, nLen); p2 += nLen;
p2[0] = ' '; p2[1] = '['; p2 += 2;
strcpy(p2, g_pLoggedGames[i].psType);
nLen = g_pLoggedGames[i].nTypeLen; p2[nLen] = ']'; p2[nLen + 1] = '.'; p2 += nLen + 2;
}
}
else
{
nLen = strrchr(psDir, '.') - psDir + 1;
memcpy(p2, psDir, nLen);
p2 += nLen;
}
switch (dwMapType)
{
case MAP_TYPE_BIT: strcpy(p2, "bit"); break;
case MAP_TYPE_LEV: strcpy(p2, "lev"); break;
default: strcpy(p2, "png");
}
if (ExtractMapToFile(hFile, dwMapSize))
g_nMapsExtracted++;
CloseHandle(hFile);
}
void ExtractMaps()
{
PCSTR ppcsExtensions[] = { "WAgame" };
PSTR p1 = g_psPathReplays + g_nPathReplaysLen;
g_nFilesFound = 0; g_nDuplicates = 0; g_nMapsExtracted = 0;
FileSearch(g_psPathReplays, p1, ppcsExtensions, 1, CountingFileSearchCallback);
if (!g_nFilesFound)
return;
SendMessage(g_hPgbProgress, PBM_SETRANGE, 0, MAKELPARAM(0, g_nFilesFound));
FileSearch(g_psPathReplays, p1, ppcsExtensions, 1, ExtractMaps__FSCallback);
SendMessage(g_hPgbProgress, PBM_SETPOS, 0, 0);
}
DWORD WINAPI ProcessingThreadProc(PVOID pDummy)
{
PSTR p1;
UINT nNotExportable;
TypeStatusMessage("Hashing existing maps...", STATE_NORMAL);
HashExistingMaps();
if (!g_nLoggedGames || g_bLogsPathChanged)
{
TypeStatusMessage("Guessing game types (reading \"User\\Logs\")...", STATE_NORMAL);
GuessGameTypes();
g_bLogsPathChanged = FALSE;
}
TypeStatusMessage("Extracting maps...", STATE_NORMAL);
ExtractMaps();
p1 = g_sMsgFinal;
p1 += wsprintf(p1, "%d maps extracted", g_nMapsExtracted);
if (g_nDuplicates)
{ p1 += wsprintf(p1, " (%d duplicates skipped", g_nDuplicates); }
if (nNotExportable = g_nFilesFound - g_nDuplicates - g_nMapsExtracted)
{ p1 += wsprintf(p1, "%s%d not exportable maps).", g_nDuplicates ? ", " : " (", nNotExportable); }
else
strcpy(p1, g_nDuplicates ? ")." : ".");
TypeStatusMessage(g_sMsgFinal, STATE_NORMAL);
g_bProcessing = FALSE;
return 0;
}
int CALLBACK BrowseCallbackProc(HWND hWnd, UINT nMsg, LPARAM lParam, LPARAM lpData)
{
char sDir[MAX_PATH];
switch(nMsg)
{
case BFFM_INITIALIZED:
if (GetDlgItemText(g_hDlg, (int)lpData, sDir, sizeof(sDir)))
SendMessage(hWnd, BFFM_SETSELECTION, TRUE, (LPARAM)sDir);
break;
}
return 0;
}
// CoInitialize() call is needed
void ChooseFolder(int nControlId, PCSTR pcsTitle)
{
BROWSEINFO bi = { 0 };
char sPath[MAX_PATH];
LPITEMIDLIST p_iil;
LPMALLOC pMalloc;
if (NOERROR != SHGetMalloc(&pMalloc))
return;
bi.hwndOwner = g_hDlg;
bi.lpszTitle = pcsTitle;
bi.ulFlags = BIF_RETURNONLYFSDIRS;
/*
if (nControlId == ID_EDT_MAPS)
bi.ulFlags |= 0x0040;
*/
bi.lpfn = BrowseCallbackProc;
bi.lParam = (LPARAM)nControlId;
if (p_iil = SHBrowseForFolder(&bi))
{
if (SHGetPathFromIDList(p_iil, sPath))
{
if (sPath[1] == ':' && sPath[0] >= 'A' && sPath[0] <= 'Z')
sPath[0] += 32;
SetDlgItemText(g_hDlg, nControlId, sPath);
}
pMalloc->lpVtbl->Free(pMalloc, p_iil);
}
pMalloc->lpVtbl->Release(pMalloc);
}
BOOL DirectoryExists(PCSTR pcsPath)
{
DWORD dwAttrib = GetFileAttributes(pcsPath);
return (dwAttrib != 0xFFFFFFFF && dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
}
BOOL ValidatePath(PSTR psPath, UINT nPathLen)
{
UINT nFirstBSPos, i, nStartFrom;
UINT * pnFoundBSPos;
UINT nFoundBSCount;
if (nPathLen <= 3)
return FALSE;
nFirstBSPos = (psPath[0] == '\\' && psPath[2] != '\\') ? 1 : 2;
pnFoundBSPos = (UINT *)malloc((nPathLen - 3) * sizeof(UINT));
nFoundBSCount = 0;
for (i = nFirstBSPos + 2; i < nPathLen; i++)
{
if (psPath[i] == '\\')
{
if (psPath[i - 1] == '.')
{ free(pnFoundBSPos); return FALSE; }
else
pnFoundBSPos[nFoundBSCount++] = i;
}
}
nStartFrom = (nFirstBSPos == 1 || psPath[0] == '\\') ? 1 : 0;
if (nStartFrom == 1 && nFoundBSCount < 2)
{ free(pnFoundBSPos); return FALSE; }
if (nFoundBSCount)
{
i = nFoundBSCount - 1;
for (;;)
{
psPath[pnFoundBSPos[i]] = '\0';
if (DirectoryExists(psPath))
{ psPath[pnFoundBSPos[i++]] = '\\'; break; }
if (i == nStartFrom)
break;
i--;
}
}
if (nStartFrom == 1 && i == nStartFrom)
{ free(pnFoundBSPos); return FALSE; }
if (!nFoundBSCount)
{
if (!CreateDirectory(psPath, NULL))
{ free(pnFoundBSPos); return FALSE; }
}
else
for(;;)
{
if (!CreateDirectory(psPath, NULL))
{ free(pnFoundBSPos); return FALSE; }
if (i == nFoundBSCount)
break;
psPath[pnFoundBSPos[i++]] = '\\';
}
free(pnFoundBSPos);
return TRUE;
}
#define PATH_GOOD 0
#define PATH_NOT_EXISTING 1
#define PATH_INCORRECT 2
UINT CheckEnteredPath(int nControlId)
{
char sTmpPath[MAX_PATH];
PSTR psPath;
PSTR psFilePart;
UINT * pnPathLen;
UINT nFirstBSPos, i;
switch (nControlId)
{
case ID_EDT_WA : psPath = g_psPathLogs; pnPathLen = &g_nPathLogsLen; break;
case ID_EDT_REPLAYS : psPath = g_psPathReplays; pnPathLen = &g_nPathReplaysLen; break;
default : psPath = g_psPathMaps; pnPathLen = &g_nPathMapsLen;
}
if (!GetDlgItemText(g_hDlg, nControlId, sTmpPath, sizeof(sTmpPath)))
{ *pnPathLen = 0; return PATH_INCORRECT; }
*pnPathLen = GetFullPathName(sTmpPath, PATH_BUFFER_SIZE, psPath, &psFilePart);
if (*pnPathLen > PATH_BUFFER_SIZE || *pnPathLen < 3)
return PATH_INCORRECT;
if (psPath[*pnPathLen - 1] == '\\' && *pnPathLen != 3)
{ (*pnPathLen)--; psPath[*pnPathLen] = '\0';}
if (psPath[*pnPathLen - 1] == '.')
return PATH_INCORRECT;
if (psPath[1] == ':' && psPath[0] >= 'A' && psPath[0] <= 'Z')
psPath[0] += 32;
nFirstBSPos = (psPath[0] == '\\' && psPath[2] != '\\') ? 1 : 2;
for (i = nFirstBSPos + 1; i < *pnPathLen; i++)
{
if (psPath[i] == ':' || psPath[i] == '*' || psPath[i] == '?'
|| psPath[i] == '"' || psPath[i] == '<' || psPath[i] == '>' || psPath[i] == '|')
return PATH_INCORRECT;
}
if (!DirectoryExists(psPath))
return PATH_NOT_EXISTING;
SetDlgItemText(g_hDlg, nControlId, psPath);
return PATH_GOOD;
}
BOOL PathsAreGood()
{
if (!g_nLoggedGames || g_bLogsPathChanged)
{
if (g_pcsAlert_WA)
{ TypeStatusMessage(g_pcsAlert_WA, STATE_ALERT); return FALSE; }
if (g_bWarned_WA)
{ g_bWarned_WA = FALSE; SetDlgItemText(g_hDlg, ID_EDT_WA, ""); g_nPathLogsLen = 0; }
else
{
if (PATH_GOOD != CheckEnteredPath(ID_EDT_WA))
{ if (g_nPathLogsLen) { g_pcsAlert_WA = "Incorrect path to WA! Leave the field empty if WA is not installed."; TypeStatusMessage(g_pcsAlert_WA, STATE_ALERT); return FALSE; } }
else
{
strcpy(g_psPathLogs + g_nPathLogsLen, "\\User\\Logs");
g_nPathLogsLen += 10;
if (!DirectoryExists(g_psPathLogs))
{
g_bWarned_WA = TRUE;
TypeStatusMessage("\"User\\Logs\" not found. You can continue, but game types won't be defined.", STATE_WARNING);
return FALSE;
}
}
if (g_nPathLogsLen)
{ g_psPathLogs[g_nPathLogsLen++] = '\\'; g_psPathLogs[g_nPathLogsLen] = '\0'; }
}
}
if (g_pcsAlert_Replays)
{ TypeStatusMessage(g_pcsAlert_Replays, STATE_ALERT); return FALSE; }
if (PATH_GOOD != CheckEnteredPath(ID_EDT_REPLAYS))
{ g_pcsAlert_Replays = "Incorrect replays (saved games) path!"; TypeStatusMessage(g_pcsAlert_Replays, STATE_ALERT); return FALSE; }
g_psPathReplays[g_nPathReplaysLen++] = '\\'; g_psPathReplays[g_nPathReplaysLen] = '\0';
if (g_pcsAlert_Maps)
{ TypeStatusMessage(g_pcsAlert_Maps, STATE_ALERT); return FALSE; }
if (g_bWarned_Maps)
{
g_bWarned_Maps = FALSE;
if (!ValidatePath(g_psPathMaps, g_nPathMapsLen))
{ g_pcsAlert_Maps = "Can't create directory for maps. Please specify a valid path."; TypeStatusMessage(g_pcsAlert_Maps, STATE_ALERT); return FALSE; }
}
else
{
UINT nResult = CheckEnteredPath(ID_EDT_MAPS);
if (nResult == PATH_INCORRECT)
{ g_pcsAlert_Maps = "Incorrect maps (saved levels) path!"; TypeStatusMessage(g_pcsAlert_Maps, STATE_ALERT); return FALSE; }
else if (nResult == PATH_NOT_EXISTING)
{
SetDlgItemText(g_hDlg, ID_EDT_MAPS, g_psPathMaps);
g_bWarned_Maps = TRUE; TypeStatusMessage("Specified maps directory does not exist. Push the button again to create it.", STATE_WARNING); return FALSE;
}
}
g_psPathMaps[g_nPathMapsLen++] = '\\'; g_psPathMaps[g_nPathMapsLen] = '\0';
return TRUE;
}
BOOL CALLBACK DialogProc(HWND hDlg, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
switch (nMsg)
{
case WM_INITDIALOG:
{
HKEY hKey;
g_hDlg = hDlg;
SendMessage(hDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICO_MAIN)));
g_hNormalBrush = CreateSolidBrush(NORMAL_STATUS_BGCOLOR);
g_hWarningBrush = CreateSolidBrush(WARNING_STATUS_BGCOLOR);
g_hAlertBrush = CreateSolidBrush(ALERT_STATUS_BGCOLOR);
g_hBtnExtract = GetDlgItem(hDlg, IDOK);
g_hLblStatus = GetDlgItem(hDlg, ID_LBL_STATUS);
g_hPgbProgress = GetDlgItem(hDlg, ID_PGB_PROGRESS);
SendMessage(g_hPgbProgress, PBM_SETSTEP, 1, 0);
SetTimer(hDlg, TIMER_BLINKING, 500, BlinkingTimerProc);
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Team17SoftwareLTD\\WormsArmageddon", 0, KEY_QUERY_VALUE, &hKey))
{
char sPath[MAX_PATH];
DWORD dwBufSize = sizeof(sPath);
LONG nResult = RegQueryValueEx(hKey, "PATH", NULL, NULL, (PUCHAR)sPath, &dwBufSize);
RegCloseKey(hKey);
if (nResult == ERROR_SUCCESS && dwBufSize != 0 && dwBufSize <= MAX_PATH - 1 - 17)
{
PSTR psOffset = sPath + dwBufSize - 1;
if (*(psOffset - 1) == '\\') *(--psOffset) = '\0';
SetDlgItemText(hDlg, ID_EDT_WA, sPath);
strcpy(psOffset, "\\User\\Games");
SetDlgItemText(hDlg, ID_EDT_REPLAYS, sPath);
strcpy(psOffset + 6, "SavedLevels");
SetDlgItemText(hDlg, ID_EDT_MAPS, sPath);
TypeStatusMessage("Specify the paths and click on that button ----->", STATE_NORMAL);
break;
}
}
TypeStatusMessage("Specify the paths. Leave WA path field empty if you don't have WA installed.", STATE_NORMAL);
}
break;
case WM_CTLCOLORSTATIC:
if ((HWND)lParam == g_hLblStatus)
{
switch (g_nState)
{
case STATE_WARNING : SetBkColor((HDC)wParam, WARNING_STATUS_BGCOLOR); return (BOOL)g_hWarningBrush;
case STATE_ALERT: SetBkColor((HDC)wParam, ALERT_STATUS_BGCOLOR); return (BOOL)g_hAlertBrush;
default: SetBkColor((HDC)wParam, NORMAL_STATUS_BGCOLOR); return (BOOL)g_hNormalBrush;
}
}
else
return FALSE;
case WM_TIMER:
if (!g_bProcessing)
{
KillTimer(hDlg, 0);
EnableWindow(g_hBtnExtract, TRUE);
}
break;
case WM_COMMAND:
if (HIWORD(wParam) == EN_CHANGE)
{
switch (LOWORD(wParam))
{
case ID_EDT_WA:
if (g_bWarned_WA) g_bWarned_WA = FALSE;
if (g_pcsAlert_WA) g_pcsAlert_WA = NULL;
if (!g_bLogsPathChanged) g_bLogsPathChanged = TRUE;
return FALSE;
case ID_EDT_REPLAYS:
if (g_pcsAlert_Replays) g_pcsAlert_Replays = NULL;
return FALSE;
case ID_EDT_MAPS:
if (g_bWarned_Maps) g_bWarned_Maps = FALSE;
if (g_pcsAlert_Maps) g_pcsAlert_Maps = NULL;
return FALSE;
}
}
switch (LOWORD(wParam))
{
case IDOK:
if (!g_bProcessing && PathsAreGood())
{
DWORD dwThreadId;
g_bProcessing = TRUE;
EnableWindow(g_hBtnExtract, FALSE);
SetTimer(hDlg, TIMER_PROCESSING, 25, NULL);
CreateThread(NULL, 0, ProcessingThreadProc, NULL, 0, &dwThreadId);
return FALSE;
}
return FALSE;
case ID_BTN_WA:
ChooseFolder(ID_EDT_WA, "Select Worms Armageddon root folder:");
return FALSE;
case ID_BTN_REPLAYS:
ChooseFolder(ID_EDT_REPLAYS, "Select the saved games (replays) folder:");
return FALSE;
case ID_BTN_MAPS:
ChooseFolder(ID_EDT_MAPS, "Select the saved levels (maps) folder:");
return FALSE;
}
break;
case WM_CLOSE:
EndDialog(hDlg, 0);
break;
default:
return FALSE;
}
return TRUE;
}
#ifdef _DEBUG
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR psCmdLine, int nShowCmd)
{
#else
void _WinMain()
{
HINSTANCE hInst = GetModuleHandle(NULL);
#endif
g_psPathLogs = (PSTR)malloc(PATH_BUFFER_SIZE * 3);
g_psPathReplays = g_psPathLogs + PATH_BUFFER_SIZE; g_psPathMaps = g_psPathLogs + PATH_BUFFER_SIZE * 2;
InitCommonControls();
CoInitialize(NULL);
DialogBox(hInst, MAKEINTRESOURCE(ID_DLG_MAIN), HWND_DESKTOP, DialogProc);
free(g_psPathLogs);
if (g_nLoggedGames)
{ free(g_pLoggedGames); free(g_pucLoggedGamesData); }
if (g_nHashes)
free(g_pHashes);
#ifdef _DEBUG
return 0;
#else
ExitProcess(0);
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment