FH2.exe Source
From Forgotten Hope Wiki
FH2.exe is a workaround for a serious bug in the Battlefield 2 engine. The sourcecode is provided here for the curious/paranoid.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <assert.h>
#include <stdio.h>
#define DEFPATH(who) char who[MAX_PATH] = { '\0' }
static FILE *g_debug = NULL;
/* ----- Helpers ----- */
static void Panic(const char *what)
{
DEFPATH(tmpbuf); DEFPATH(errbuf);
int err = GetLastError();
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, err, 0, errbuf, MAX_PATH-1, 0);
_snprintf(tmpbuf, MAX_PATH, "%s: %s", what, errbuf);
MessageBox(0, tmpbuf, "Failed.", MB_ICONEXCLAMATION | MB_OK);
ExitProcess(err);
}
static void Info(const char *what)
{
MessageBox(0, what, "Info", MB_ICONINFORMATION | MB_OK);
}
/* ----- Actually useful stuff. ----- */
typedef struct {
enum { WHERE_NONE = 0, WHERE_MY_DOCUMENTS, WHERE_BF2 } where;
const char *suffix;
} pathspec;
typedef struct {
enum { MOVE_NONE = 0, MOVE_DIRECTORY, MOVE_FILE } how;
#define flag_backup 0x1
#define flag_force 0x2
int flags;
pathspec from;
pathspec to;
const char *why;
} movement;
static movement targets[] = {
{ MOVE_DIRECTORY, 0, { WHERE_MY_DOCUMENTS, "Battlefield 2/cache"}, { WHERE_MY_DOCUMENTS, "Battlefield 2/cache-vanilla" }, "move aside vanilla shader cache" },
{ MOVE_DIRECTORY, 0, { WHERE_MY_DOCUMENTS, "Battlefield 2/cache-fh2"},{ WHERE_MY_DOCUMENTS, "Battlefield 2/cache" }, "move in fh2 cache" },
{ MOVE_DIRECTORY, 0, { WHERE_MY_DOCUMENTS, "Battlefield 2/mods/bf2"}, { WHERE_MY_DOCUMENTS, "Battlefield 2/mods/bf2.tmp" }, "move aside mods/bf2 shader cache" },
{ MOVE_DIRECTORY, 0, { WHERE_MY_DOCUMENTS, "Battlefield 2/mods/fh2"}, { WHERE_MY_DOCUMENTS, "Battlefield 2/mods/bf2" }, "move in mods/fh2 shader cache" },
{ MOVE_FILE, 0, { WHERE_BF2, "mods/bf2/shaders_client.zip"}, { WHERE_BF2, "mods/bf2/shaders_client.zip-vanilla" }, "move aside vanilla shaders" },
{ MOVE_FILE, flag_backup, { WHERE_BF2, "mods/fh2/shaders_client.zip"}, { WHERE_BF2, "mods/bf2/shaders_client.zip" }, "move in fh2 shaders" },
{ MOVE_FILE, 0, { WHERE_BF2, "mods/fh2/movies/warning.bik"}, { WHERE_BF2, "mods/fh2/movies/warning.bik-disabled" }, "move aside warning video" },
{ MOVE_NONE, }
};
static void GetMyDocs(char *dest)
{
if (SUCCEEDED(SHGetFolderPath(NULL,
CSIDL_PERSONAL|CSIDL_FLAG_CREATE,
NULL,
0,
dest))) {
return;
} else {
Panic("SHGetFolderPath failed");
}
}
static HANDLE g_mutex = NULL;
static BOOL IsFirstInstance(void)
{
g_mutex = CreateMutex(NULL, FALSE, "Global\\forgotten hope 2");
if (g_mutex != NULL && GetLastError() == ERROR_ALREADY_EXISTS)
{
CloseHandle(g_mutex);
g_mutex = NULL;
return FALSE;
}
return TRUE;
}
static void ExecMovement(const movement *m, BOOL reverse)
{
int e;
DEFPATH(p_from);
DEFPATH(p_to);
DEFPATH(p_from_backup);
if (m->from.where == WHERE_MY_DOCUMENTS)
GetMyDocs(p_from);
PathAppend(p_from, m->from.suffix);
if (m->to.where == WHERE_MY_DOCUMENTS)
GetMyDocs(p_to);
PathAppend(p_to, m->to.suffix);
memcpy(p_from_backup, p_from, MAX_PATH);
strcat(p_from_backup, ".backup");
switch (m->how)
{
case MOVE_FILE:
if (!reverse && m->flags & flag_backup)
{
if (g_debug) fprintf(g_debug, "Taking a backup of '%s' to '%s'... ", p_from, p_from_backup);
e = CopyFile(p_from, p_from_backup, FALSE);
if (g_debug) fprintf(g_debug, "%s.\n", (e) ? "Done" : "Failed");
}
break;
case MOVE_DIRECTORY:
break;
default:
case MOVE_NONE:
return;
}
if (reverse)
{
if (g_debug) fprintf(g_debug, "Moving '%s' back to '%s'... ", p_to, p_from);
e = MoveFileEx(p_to, p_from, MOVEFILE_WRITE_THROUGH);
if (g_debug) fprintf(g_debug, "%s.\n", (e) ? "Done" : "Failed");
} else {
if (g_debug) fprintf(g_debug, "Moving '%s' to '%s'... ", p_from, p_to);
e = MoveFileEx(p_from, p_to, MOVEFILE_WRITE_THROUGH);
if (g_debug) fprintf(g_debug, "%s.\n", (e) ? "Done" : "Failed");
}
}
static void MoveIntoPlace(void)
{
const movement *m = targets;
while (m->how != MOVE_NONE)
{
if (g_debug) fprintf(g_debug, "MoveIntoPlace: executing task '%s'\n", m->why);
ExecMovement(m, FALSE);
m++;
}
}
static void ResetPlaces(void)
{
const movement *m = targets;
while (m->how != MOVE_NONE)
m++;
m--;
while (1)
{
if (g_debug) fprintf(g_debug, "MoveIntoPlace: reversing task '%s'\n", m->why);
ExecMovement(m, TRUE);
if (m == targets)
break;
m--;
}
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, char *lpCmdLine, int nCmdShow)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
DEFPATH(cmdline);
DEFPATH(md_cache_bf2);
DEFPATH(md_cache_bf2tmp);
DEFPATH(md_cache_fh2);
BOOL first_instance;
g_debug = fopen("fh2.exe.log", "a+");
if (g_debug) setvbuf(g_debug, NULL, _IONBF, 0);
if (g_debug) fprintf(g_debug, "-- fh2.exe -- starting log\n");
first_instance = IsFirstInstance();
if (g_debug) fprintf(g_debug, "IsFirstInstance? %s\n", (first_instance)?"Yes":"No");
if (first_instance)
MoveIntoPlace();
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
memset(&pi, 0, sizeof(pi));
_snprintf(cmdline, MAX_PATH, "bf2.exe +modPath mods/fh2 %s", lpCmdLine);
SetProcessWorkingSetSize((HANDLE) -1, -1, -1);
if (g_debug) fprintf(g_debug, "Execing '%s'\n", cmdline);
// Start the child process.
if (!CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
Panic("CreateProcess failed");
if (g_debug) fprintf(g_debug, "Waiting...\n");
// Wait until child process exits.
WaitForSingleObject(pi.hProcess, INFINITE);
if (g_debug) fprintf(g_debug, "bf2.exe finished. Clearing up...\n");
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
if (first_instance)
ResetPlaces();
if (g_mutex)
CloseHandle(g_mutex);
if (g_debug) fprintf(g_debug, "-- fh2.exe -- Exiting.\n");
if (g_debug)
fclose(g_debug);
return 0;
}

