mirror of
https://github.com/isledecomp/LEGOIslandRebuilder.git
synced 2024-11-23 15:48:03 -05:00
work on new hooking system
This commit is contained in:
parent
63dd568e64
commit
18be36348f
18 changed files with 848 additions and 96 deletions
|
@ -3,23 +3,48 @@ cmake_minimum_required(VERSION 3.5)
|
|||
project(Rebuilder LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_MFC_FLAG 2)
|
||||
add_compile_definitions(_AFXDLL)
|
||||
|
||||
option(BUILD_UNICODE "Build with Unicode support" ON)
|
||||
|
||||
#
|
||||
# Build our code injected DLL
|
||||
#
|
||||
add_library(Rebld SHARED
|
||||
lib/dllmain.cpp
|
||||
lib/hooks.cpp
|
||||
lib/hooks.h
|
||||
lib/mmpassthru.cpp
|
||||
lib/util.cpp
|
||||
lib/util.h
|
||||
lib/worker.cpp
|
||||
lib/worker.h
|
||||
)
|
||||
target_link_libraries(Rebld PRIVATE winmm.lib)
|
||||
|
||||
#
|
||||
# Build launcher/configuration executable
|
||||
#
|
||||
add_executable(Rebuilder WIN32
|
||||
res/res.rc
|
||||
res/resource.h
|
||||
src/app.cpp
|
||||
src/app.h
|
||||
src/clinkstatic.cpp
|
||||
src/clinkstatic.h
|
||||
src/launcher.cpp
|
||||
src/launcher.h
|
||||
src/window.cpp
|
||||
src/window.h
|
||||
)
|
||||
|
||||
target_compile_definitions(Rebuilder PRIVATE _AFXDLL)
|
||||
if (BUILD_UNICODE)
|
||||
target_compile_definitions(Rebuilder PRIVATE UNICODE _UNICODE)
|
||||
target_compile_definitions(Rebld PRIVATE UNICODE _UNICODE)
|
||||
target_link_options(Rebuilder PRIVATE /entry:wWinMainCRTStartup)
|
||||
endif()
|
||||
|
||||
target_link_libraries(Rebuilder PRIVATE shlwapi.lib)
|
||||
|
||||
# Ensure DLL is compiled before resource is built into executable
|
||||
set_source_files_properties(res/res.rc PROPERTIES OBJECT_DEPENDS Rebld)
|
||||
|
|
27
lib/dllmain.cpp
Normal file
27
lib/dllmain.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include <IOSTREAM>
|
||||
#include <TCHAR.H>
|
||||
#include <WINDOWS.H>
|
||||
|
||||
#include "worker.h"
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
||||
{
|
||||
switch (ul_reason_for_call) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
// Allocate console
|
||||
AllocConsole();
|
||||
|
||||
// Direct stdin/stderr/stdout to console
|
||||
_tfreopen(TEXT("CONIN$"), TEXT("r"), stdin);
|
||||
_tfreopen(TEXT("CONOUT$"), TEXT("w"), stderr);
|
||||
_tfreopen(TEXT("CONOUT$"), TEXT("w"), stdout);
|
||||
|
||||
// Print success line
|
||||
printf("Injected successfully\n");
|
||||
|
||||
Patch();
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
115
lib/hooks.cpp
Normal file
115
lib/hooks.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
#include "hooks.h"
|
||||
|
||||
#include <STDIO.H>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
void InterceptOutputDebugStringA(LPCSTR s)
|
||||
{
|
||||
printf("%s\n", s);
|
||||
MessageBoxA(0,s,"LEGO Island sez",0);
|
||||
}
|
||||
|
||||
HWND WINAPI InterceptCreateWindowExA(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)
|
||||
{
|
||||
HWND window = CreateWindowExA(dwExStyle, lpClassName, "LEGO Island: Rebuilt", dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
HWND WINAPI InterceptFindWindowA(LPCSTR lpClassName, LPCSTR lpWindowName)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ForceDDPixelFormatTo16(LPDDPIXELFORMAT lpDDPixelFormat)
|
||||
{
|
||||
if (lpDDPixelFormat->dwRGBBitCount == 32) {
|
||||
lpDDPixelFormat->dwRGBBitCount = 16;
|
||||
lpDDPixelFormat->dwRBitMask = 0xF800;
|
||||
lpDDPixelFormat->dwGBitMask = 0x07E0;
|
||||
lpDDPixelFormat->dwBBitMask = 0x001F;
|
||||
}
|
||||
}
|
||||
|
||||
void ForceDDSurfaceDescTo16(LPDDSURFACEDESC lpDDSurfaceDesc)
|
||||
{
|
||||
DDPIXELFORMAT &pixfmt = lpDDSurfaceDesc->ddpfPixelFormat;
|
||||
if (pixfmt.dwRGBBitCount == 32) {
|
||||
// LEGO Island has no support for 32-bit surfaces, so we tell it we're on a 16-bit surface
|
||||
lpDDSurfaceDesc->lPitch /= 2;
|
||||
ForceDDPixelFormatTo16(&pixfmt);
|
||||
}
|
||||
}
|
||||
|
||||
typedef HRESULT (WINAPI *ddSurfaceGetDescFunction)(LPDIRECTDRAWSURFACE lpDDSurface, LPDDSURFACEDESC lpDDSurfaceDesc);
|
||||
ddSurfaceGetDescFunction originalDDSurfaceGetDescFunction = NULL;
|
||||
HRESULT WINAPI InterceptSurfaceGetDesc(LPDIRECTDRAWSURFACE lpDDSurface, LPDDSURFACEDESC lpDDSurfaceDesc)
|
||||
{
|
||||
HRESULT res = originalDDSurfaceGetDescFunction(lpDDSurface, lpDDSurfaceDesc);
|
||||
|
||||
if (res == DD_OK) {
|
||||
ForceDDSurfaceDescTo16(lpDDSurfaceDesc);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef HRESULT (WINAPI *ddSurfaceGetPixelFormatFunction)(LPDIRECTDRAWSURFACE lpDDSurface, LPDDPIXELFORMAT lpDDPixelFormat);
|
||||
ddSurfaceGetPixelFormatFunction originalDDSurfaceGetPixelFunction = NULL;
|
||||
HRESULT WINAPI InterceptSurfaceGetPixelFormatFunction(LPDIRECTDRAWSURFACE lpDDSurface, LPDDPIXELFORMAT lpDDPixelFormat)
|
||||
{
|
||||
HRESULT res = originalDDSurfaceGetPixelFunction(lpDDSurface, lpDDPixelFormat);
|
||||
|
||||
if (res == DD_OK) {
|
||||
//ForceDDPixelFormatTo16(lpDDPixelFormat);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef HRESULT (WINAPI *ddCreateSurfaceFunction)(LPDIRECTDRAW lpDD, LPDDSURFACEDESC lpDDSurfaceDesc, LPDIRECTDRAWSURFACE *lplpDDSurface, IUnknown *unknown);
|
||||
ddCreateSurfaceFunction originalCreateSurfaceFunction = NULL;
|
||||
HRESULT WINAPI InterceptCreateSurface(LPDIRECTDRAW lpDD, LPDDSURFACEDESC lpDDSurfaceDesc, LPDIRECTDRAWSURFACE *lplpDDSurface, IUnknown *unknown)
|
||||
{
|
||||
//ForceDDSurfaceDescTo16(lpDDSurfaceDesc);
|
||||
|
||||
HRESULT res = originalCreateSurfaceFunction(lpDD, lpDDSurfaceDesc, lplpDDSurface, unknown);
|
||||
|
||||
if (res == DD_OK) {
|
||||
if (!originalDDSurfaceGetPixelFunction) {
|
||||
originalDDSurfaceGetPixelFunction = (ddSurfaceGetPixelFormatFunction)OverwriteVirtualTable(*lplpDDSurface, 0x15, (LPVOID)InterceptSurfaceGetPixelFormatFunction);
|
||||
}
|
||||
|
||||
if (!originalDDSurfaceGetDescFunction) {
|
||||
//originalDDSurfaceGetDescFunction = (ddSurfaceGetDescFunction)OverwriteVirtualTable(*lplpDDSurface, 0x16, (LPVOID)InterceptSurfaceGetDesc);
|
||||
originalDDSurfaceGetDescFunction = (ddSurfaceGetDescFunction)OverwriteVirtualTable(*lplpDDSurface, 0x16, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef HRESULT (WINAPI *ddGetDisplayModeFunction)(LPDIRECTDRAW lpDD, LPDDSURFACEDESC lpDDSurfaceDesc);
|
||||
ddGetDisplayModeFunction originalGetDisplayMode = NULL;
|
||||
HRESULT WINAPI InterceptGetDisplayMode(LPDIRECTDRAW lpDD, LPDDSURFACEDESC lpDDSurfaceDesc)
|
||||
{
|
||||
HRESULT res = originalGetDisplayMode(lpDD, lpDDSurfaceDesc);
|
||||
|
||||
//ForceDDSurfaceDescTo16(lpDDSurfaceDesc);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
ddCreateFunction ddCreateOriginal = NULL;
|
||||
HRESULT WINAPI InterceptDirectDrawCreate(GUID *lpGUID, LPDIRECTDRAW *lplpDD, IUnknown *pUnkOuter)
|
||||
{
|
||||
HRESULT res = ddCreateOriginal(lpGUID, lplpDD, pUnkOuter);
|
||||
|
||||
if (res == DD_OK && !originalGetDisplayMode) {
|
||||
originalGetDisplayMode = (ddGetDisplayModeFunction)OverwriteVirtualTable(*lplpDD, 0xC, (LPVOID)InterceptGetDisplayMode);
|
||||
originalCreateSurfaceFunction = (ddCreateSurfaceFunction)OverwriteVirtualTable(*lplpDD, 0x6, (LPVOID)InterceptCreateSurface);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
32
lib/hooks.h
Normal file
32
lib/hooks.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
#ifndef HOOKS_H
|
||||
#define HOOKS_H
|
||||
|
||||
#include <DDRAW.H>
|
||||
#include <WINDOWS.H>
|
||||
|
||||
void InterceptOutputDebugStringA(LPCSTR s);
|
||||
|
||||
HWND WINAPI InterceptCreateWindowExA(
|
||||
DWORD dwExStyle,
|
||||
LPCSTR lpClassName,
|
||||
LPCSTR lpWindowName,
|
||||
DWORD dwStyle,
|
||||
int X,
|
||||
int Y,
|
||||
int nWidth,
|
||||
int nHeight,
|
||||
HWND hWndParent,
|
||||
HMENU hMenu,
|
||||
HINSTANCE hInstance,
|
||||
LPVOID lpParam
|
||||
);
|
||||
|
||||
HWND WINAPI InterceptFindWindowA(LPCSTR lpClassName, LPCSTR lpWindowName);
|
||||
|
||||
typedef HRESULT (WINAPI *ddCreateFunction)(GUID *lpGUID, LPDIRECTDRAW *lplpDD, IUnknown *pUnkOuterS);
|
||||
extern ddCreateFunction ddCreateOriginal;
|
||||
HRESULT WINAPI InterceptDirectDrawCreate(GUID *lpGUID, LPDIRECTDRAW *lplpDD, IUnknown *pUnkOuter);
|
||||
|
||||
HRESULT WINAPI InterceptSurfaceGetDesc(LPDIRECTDRAWSURFACE lpDDSurface, LPDDSURFACEDESC lpDDSurfaceDesc);
|
||||
|
||||
#endif // HOOKS_H
|
6
lib/mmpassthru.cpp
Normal file
6
lib/mmpassthru.cpp
Normal file
|
@ -0,0 +1,6 @@
|
|||
#include <WINDOWS.H>
|
||||
|
||||
extern "C" __declspec(dllexport) DWORD rbldGetTime()
|
||||
{
|
||||
return timeGetTime();
|
||||
}
|
139
lib/util.cpp
Normal file
139
lib/util.cpp
Normal file
|
@ -0,0 +1,139 @@
|
|||
#include "util.h"
|
||||
|
||||
#include <STDIO.H>
|
||||
|
||||
BOOL WriteMemory(LPVOID destination, LPVOID source, size_t length, LPVOID oldData)
|
||||
{
|
||||
DWORD oldProtec;
|
||||
|
||||
if (VirtualProtect(destination, length, PAGE_EXECUTE_READWRITE, &oldProtec)) {
|
||||
// Read data out if requested
|
||||
if (oldData) {
|
||||
memcpy(oldData, destination, length);
|
||||
}
|
||||
|
||||
// Write data if provided
|
||||
if (source) {
|
||||
memcpy(destination, source, length);
|
||||
}
|
||||
|
||||
// Restore original protection
|
||||
VirtualProtect(destination, length, oldProtec, &oldProtec);
|
||||
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
SIZE_T SearchReplacePattern(LPVOID imageBase, LPCVOID search, LPCVOID replace, SIZE_T count, BOOL only_once)
|
||||
{
|
||||
SIZE_T instances = 0;
|
||||
|
||||
HANDLE process = GetCurrentProcess();
|
||||
|
||||
MEMORY_BASIC_INFORMATION mbi = {0};
|
||||
|
||||
// Loop through memory pages
|
||||
UINT_PTR addr = (UINT_PTR)imageBase;
|
||||
while ((!instances || !only_once) && VirtualQueryEx(process, (LPVOID)addr, &mbi, sizeof(mbi)) && mbi.AllocationBase == imageBase) {
|
||||
if (mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS) {
|
||||
DWORD oldProtec;
|
||||
|
||||
// Try to gain access to this memory page
|
||||
if (VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &oldProtec)) {
|
||||
// Loop through every byte to find the pattern
|
||||
SIZE_T maxOffset = mbi.RegionSize - count;
|
||||
for (SIZE_T i=0; i<maxOffset; i++) {
|
||||
LPVOID offset = (LPVOID)((UINT_PTR)(mbi.BaseAddress)+i);
|
||||
|
||||
if (!memcmp(offset, search, count)) {
|
||||
// Found pattern, overwrite it
|
||||
memcpy(offset, replace, count);
|
||||
instances++;
|
||||
|
||||
if (only_once) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore original permissions
|
||||
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, oldProtec, &oldProtec);
|
||||
}
|
||||
}
|
||||
|
||||
addr += mbi.RegionSize;
|
||||
}
|
||||
|
||||
return instances;
|
||||
}
|
||||
|
||||
LPVOID OverwriteImport(LPVOID imageBase, LPCSTR overrideFunction, LPVOID override)
|
||||
{
|
||||
// Get DOS header
|
||||
PIMAGE_DOS_HEADER dosHeaders = (PIMAGE_DOS_HEADER)imageBase;
|
||||
|
||||
// Retrieve NT header
|
||||
PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((UINT_PTR)imageBase + dosHeaders->e_lfanew);
|
||||
|
||||
// Retrieve imports directory
|
||||
IMAGE_DATA_DIRECTORY importsDirectory = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
|
||||
// Retrieve import descriptor
|
||||
PIMAGE_IMPORT_DESCRIPTOR importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((UINT_PTR)imageBase + importsDirectory.VirtualAddress);
|
||||
|
||||
while (importDescriptor->Name != NULL) {
|
||||
LPCSTR libraryName = (LPCSTR)importDescriptor->Name + (UINT_PTR)imageBase;
|
||||
HMODULE library = LoadLibraryA(libraryName);
|
||||
|
||||
if (library) {
|
||||
PIMAGE_THUNK_DATA originalFirstThunk = (PIMAGE_THUNK_DATA)((UINT_PTR)imageBase + importDescriptor->OriginalFirstThunk);
|
||||
PIMAGE_THUNK_DATA firstThunk = (PIMAGE_THUNK_DATA)((UINT_PTR)imageBase + importDescriptor->FirstThunk);
|
||||
|
||||
while (originalFirstThunk->u1.AddressOfData != NULL) {
|
||||
PIMAGE_IMPORT_BY_NAME functionName = (PIMAGE_IMPORT_BY_NAME)((UINT_PTR)imageBase + (UINT_PTR)originalFirstThunk->u1.AddressOfData);
|
||||
|
||||
if (!strcmp((const char*)functionName->Name, overrideFunction)) {
|
||||
LPVOID originalFunction = firstThunk->u1.Function;
|
||||
firstThunk->u1.Function = (PDWORD)override;
|
||||
|
||||
// Return original function and end loop here
|
||||
printf("Hooked %s\n", overrideFunction);
|
||||
return originalFunction;
|
||||
}
|
||||
++originalFirstThunk;
|
||||
++firstThunk;
|
||||
}
|
||||
}
|
||||
|
||||
importDescriptor++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LPVOID OverwriteVirtualTable(LPVOID object, SIZE_T methodIndex, LPVOID overrideFunction)
|
||||
{
|
||||
LPVOID *vtable = ((LPVOID**)(object))[0];
|
||||
LPVOID &functionAddress = vtable[methodIndex];
|
||||
LPVOID originalFunction = functionAddress;
|
||||
|
||||
if (overrideFunction) {
|
||||
functionAddress = overrideFunction;
|
||||
printf("Hooked vtable %p+%lu\n", vtable, methodIndex);
|
||||
}
|
||||
|
||||
return originalFunction;
|
||||
}
|
||||
|
||||
BOOL OverwriteCall(LPVOID destination, LPVOID localCall)
|
||||
{
|
||||
char callInst[5];
|
||||
|
||||
callInst[0] = '\xE8';
|
||||
|
||||
*(DWORD*)(&callInst[1]) = (DWORD)localCall - ((DWORD)destination + 5);
|
||||
|
||||
return WriteMemory(destination, callInst, 5);
|
||||
}
|
16
lib/util.h
Normal file
16
lib/util.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <WINDOWS.H>
|
||||
|
||||
BOOL WriteMemory(LPVOID destination, LPVOID source, size_t length, LPVOID oldData = NULL);
|
||||
|
||||
BOOL OverwriteCall(LPVOID destination, LPVOID localCall);
|
||||
|
||||
SIZE_T SearchReplacePattern(LPVOID imageBase, LPCVOID search, LPCVOID replace, SIZE_T count, BOOL only_once = FALSE);
|
||||
|
||||
LPVOID OverwriteImport(LPVOID imageBase, LPCSTR overrideFunction, LPVOID override);
|
||||
|
||||
LPVOID OverwriteVirtualTable(LPVOID object, SIZE_T methodIndex, LPVOID overrideFunction);
|
||||
|
||||
#endif // UTIL_H
|
45
lib/worker.cpp
Normal file
45
lib/worker.cpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#include "worker.h"
|
||||
|
||||
#include <VECTOR>
|
||||
|
||||
#include "hooks.h"
|
||||
#include "util.h"
|
||||
|
||||
DWORD WINAPI Patch()
|
||||
{
|
||||
MessageBoxA(0,"Connect debugger now",0,0);
|
||||
|
||||
// Hook import address table
|
||||
LPVOID exeBase = GetModuleHandle(TEXT("ISLE.EXE"));
|
||||
LPVOID dllBase = GetModuleHandle(TEXT("LEGO1.DLL"));
|
||||
|
||||
// Redirect various imports
|
||||
OverwriteImport(exeBase, "CreateWindowExA", (LPVOID)InterceptCreateWindowExA);
|
||||
OverwriteImport(exeBase, "FindWindowA", (LPVOID)InterceptFindWindowA);
|
||||
OverwriteImport(dllBase, "OutputDebugStringA", (LPVOID)InterceptOutputDebugStringA);
|
||||
ddCreateOriginal = (ddCreateFunction)OverwriteImport(dllBase, "DirectDrawCreate", (LPVOID)InterceptDirectDrawCreate);
|
||||
|
||||
// Stay active when defocused
|
||||
SearchReplacePattern(exeBase, "\x89\x58\x70", "\x90\x90\x90", 3);
|
||||
SearchReplacePattern(dllBase, "\xC7\x44\x24\x24\xE0\x00\x00\x00", "\xC7\x44\x24\x24\xE0\x80\x00\x00", 8);
|
||||
SearchReplacePattern(dllBase, "\xC7\x44\x24\x24\xB0\x00\x00\x00", "\xC7\x44\x24\x24\xB0\x80\x00\x00", 8);
|
||||
SearchReplacePattern(dllBase, "\xC7\x45\xCC\x11\x00\x00\x00", "\xC7\x45\xCC\x11\x80\x00\x00", 7);
|
||||
SearchReplacePattern(dllBase, "\xC7\x45\xCC\xE0\x00\x00\x00", "\xC7\x45\xCC\xE0\x80\x00\x00", 7);
|
||||
|
||||
// DDRAW GetSurfaceDesc Override
|
||||
//OverwriteCall((LPVOID) ((UINT_PTR)dllBase+0xBA7D5), (LPVOID)InterceptSurfaceGetDesc);
|
||||
|
||||
// Window size hack
|
||||
/*SearchReplacePattern(exeBase,
|
||||
"\x80\x02\x00\x00\xE0\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x80\x02\x00\x00\xE0\x01\x00\x00",
|
||||
"\x40\x01\x00\x00\xE0\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x80\x02\x00\x00\xE0\x01\x00\x00", 24);*/
|
||||
|
||||
/*char debug[5];
|
||||
debug[0] = 0xE8;
|
||||
*(UINT_PTR*)(&debug[1]) = (UINT_PTR)InterceptOutputDebugStringA;
|
||||
if (SIZE_T replacements = SearchReplacePattern(legoBase, "\xE8\x34\x08\x00\x00", debug, 5)) {
|
||||
printf("Hooked some other debug function %lu times", replacements);
|
||||
}*/
|
||||
|
||||
return 0;
|
||||
}
|
8
lib/worker.h
Normal file
8
lib/worker.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef WORKER_H
|
||||
#define WORKER_H
|
||||
|
||||
#include <WINDOWS.H>
|
||||
|
||||
DWORD WINAPI Patch();
|
||||
|
||||
#endif // WORKER_H
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="com.itsmattkc.Rebuilder" type="win32" />
|
||||
<description>LEGO Island modding tool and launcher.</description>
|
||||
<dependency>
|
||||
|
@ -7,4 +7,17 @@
|
|||
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*" />
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<asmv3:application>
|
||||
<asmv3:windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false">
|
||||
</requestedExecutionLevel>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
||||
|
|
21
res/res.rc
21
res/res.rc
|
@ -1,2 +1,21 @@
|
|||
#include "resource.h"
|
||||
|
||||
// Application icon
|
||||
IDI_ICON1 ICON DISCARDABLE "mama.ico"
|
||||
1 24 "res.manifest"
|
||||
|
||||
// Manifest to enable visual styles
|
||||
1 RC_MANIFEST "res.manifest"
|
||||
|
||||
// String table
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_TITLE "LEGO Island Rebuilder"
|
||||
IDS_SUBTITLE "by MattKC (itsmattkc.com)"
|
||||
IDS_PATCHES "Patches"
|
||||
IDS_MUSIC "Music"
|
||||
IDS_RUN "Run"
|
||||
IDS_KILL "Kill"
|
||||
END
|
||||
|
||||
// Include worker DLL in executable for better portability
|
||||
WORKER_DLL RCDATA "rebld.dll"
|
||||
|
|
15
res/resource.h
Normal file
15
res/resource.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#ifndef RESOURCE_H
|
||||
#define RESOURCE_H
|
||||
|
||||
#define IDS_TITLE 0
|
||||
#define IDS_SUBTITLE 1
|
||||
#define IDS_PATCHES 2
|
||||
#define IDS_MUSIC 3
|
||||
#define IDS_RUN 4
|
||||
#define IDS_KILL 5
|
||||
|
||||
#define RC_MANIFEST 24
|
||||
|
||||
#define WORKER_DLL 300
|
||||
|
||||
#endif // RESOURCE_H
|
55
src/clinkstatic.cpp
Normal file
55
src/clinkstatic.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#include "clinkstatic.h"
|
||||
|
||||
#define super CStatic
|
||||
|
||||
#ifndef IDC_HAND
|
||||
#define IDC_HAND MAKEINTRESOURCE(32649)
|
||||
#endif
|
||||
|
||||
CLinkStatic::CLinkStatic()
|
||||
{
|
||||
// We want a pointing hand cursor and different Windows versions provide this differently
|
||||
OSVERSIONINFO info;
|
||||
ZeroMemory(&info, sizeof(info));
|
||||
info.dwOSVersionInfoSize = sizeof(info);
|
||||
GetVersionEx(&info);
|
||||
|
||||
if (info.dwMajorVersion <= 4) {
|
||||
// Windows 9x didn't have a pointing hand cursor so we'll pull it from winhlp32
|
||||
CString strWndDir;
|
||||
GetWindowsDirectory(strWndDir.GetBuffer(MAX_PATH), MAX_PATH);
|
||||
strWndDir.ReleaseBuffer();
|
||||
|
||||
strWndDir += _T("\\winhlp32.exe");
|
||||
HMODULE hModule = LoadLibrary(strWndDir);
|
||||
if (hModule) {
|
||||
HCURSOR hHandCursor = ::LoadCursor(hModule, MAKEINTRESOURCE(106));
|
||||
if (hHandCursor)
|
||||
m_hPointHand = CopyCursor(hHandCursor);
|
||||
}
|
||||
FreeLibrary(hModule);
|
||||
} else {
|
||||
// With NT5+, we can use IDC_HAND
|
||||
m_hPointHand = LoadCursor(NULL, IDC_HAND);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CLinkStatic::OnSetCursor(CWnd *pWnd, UINT nHitTest, UINT message)
|
||||
{
|
||||
|
||||
::SetCursor(m_hPointHand);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
HBRUSH CLinkStatic::CtlColor(CDC *pDC, UINT nCtlColor)
|
||||
{
|
||||
pDC->SetTextColor(RGB(0, 0, 240));
|
||||
pDC->SetBkMode(TRANSPARENT);
|
||||
|
||||
return (HBRUSH) GetStockObject(NULL_BRUSH);
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(CLinkStatic, super)
|
||||
ON_WM_SETCURSOR()
|
||||
ON_WM_CTLCOLOR_REFLECT()
|
||||
END_MESSAGE_MAP()
|
22
src/clinkstatic.h
Normal file
22
src/clinkstatic.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef CLINKSTATIC_H
|
||||
#define CLINKSTATIC_H
|
||||
|
||||
#include <AFXWIN.H>
|
||||
|
||||
class CLinkStatic : public CStatic
|
||||
{
|
||||
public:
|
||||
CLinkStatic();
|
||||
|
||||
afx_msg BOOL OnSetCursor(CWnd *pWnd, UINT nHitTest, UINT message);
|
||||
|
||||
afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor);
|
||||
|
||||
private:
|
||||
HCURSOR m_hPointHand;
|
||||
|
||||
DECLARE_MESSAGE_MAP()
|
||||
|
||||
};
|
||||
|
||||
#endif // CLINKSTATIC_H
|
212
src/launcher.cpp
212
src/launcher.cpp
|
@ -3,50 +3,68 @@
|
|||
#include <COMMDLG.H>
|
||||
#include <SHLWAPI.H>
|
||||
|
||||
#include "../res/resource.h"
|
||||
|
||||
HANDLE Launcher::Launch(HWND parent)
|
||||
{
|
||||
HANDLE ret = NULL;
|
||||
|
||||
// Find the installation
|
||||
LPTSTR filename = FindInstallation(parent);
|
||||
|
||||
if (filename) {
|
||||
// If we found it, launch now
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFO si;
|
||||
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
|
||||
if (CreateProcess(NULL, filename, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) {
|
||||
ret = pi.hProcess;
|
||||
|
||||
if (!Patch(ret) && MessageBox(parent, _T("One or more patches failed. Would you like to continue?"), NULL, MB_YESNO) == IDNO) {
|
||||
// Something went wrong, so we'll terminate this process now
|
||||
TerminateProcess(ret, 1);
|
||||
ret = NULL;
|
||||
} else {
|
||||
// Otherwise resume
|
||||
ResumeThread(pi.hThread);
|
||||
}
|
||||
} else {
|
||||
TCHAR err[2048];
|
||||
_stprintf(err, _T("Failed to create process with error 0x%lx"), GetLastError());
|
||||
MessageBox(parent, err, NULL, 0);
|
||||
}
|
||||
TCHAR filename[MAX_PATH];
|
||||
if (!FindInstallation(parent, filename)) {
|
||||
// Don't show error message because presumably the user cancelled out of looking for ISLE
|
||||
return NULL;
|
||||
}
|
||||
|
||||
delete [] filename;
|
||||
// If we found it, make a copy to temp
|
||||
TCHAR copiedFile[MAX_PATH];
|
||||
TCHAR libraryFile[MAX_PATH];
|
||||
if (!CopyIsleToTemp(filename, copiedFile)) {
|
||||
MessageBox(parent, _T("Failed to copy to temp"), NULL, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
// Extract REBLD.DLL which contains our patches
|
||||
if (!ExtractLibrary(libraryFile, MAX_PATH)) {
|
||||
MessageBox(parent, _T("Failed to extract to temp"), NULL, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Patch our copied ISLE to import our DLL
|
||||
if (!PatchIsle(copiedFile)) {
|
||||
MessageBox(parent, _T("Failed to patch import"), NULL, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get ISLE directory only
|
||||
TCHAR srcDir[MAX_PATH];
|
||||
_tcscpy(srcDir, filename);
|
||||
PathRemoveFileSpec(srcDir);
|
||||
|
||||
// Start launching our copy
|
||||
PROCESS_INFORMATION pi;
|
||||
STARTUPINFO si;
|
||||
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
|
||||
if (!CreateProcess(NULL, copiedFile, NULL, NULL, FALSE, 0, NULL, srcDir, &si, &pi)) {
|
||||
TCHAR err[2048];
|
||||
_stprintf(err, _T("Failed to create process with error 0x%lx"), GetLastError());
|
||||
MessageBox(parent, err, NULL, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pi.hProcess;
|
||||
}
|
||||
|
||||
LPTSTR Launcher::FindInstallation(HWND parent)
|
||||
BOOL Launcher::FindInstallation(HWND parent, LPTSTR str)
|
||||
{
|
||||
// Search for LEGO Island disk path
|
||||
DWORD value_sz;
|
||||
|
||||
TCHAR *isle_diskpath = NULL;
|
||||
BOOL success = TRUE;
|
||||
|
||||
// Start with empty string
|
||||
str[0] = 0;
|
||||
|
||||
// On install, LEGO Island records its installation directory in the registry
|
||||
HKEY hKey;
|
||||
|
@ -54,33 +72,23 @@ LPTSTR Launcher::FindInstallation(HWND parent)
|
|||
LONG ret = RegQueryValueEx(hKey, _T("diskpath"), NULL, NULL, NULL, &value_sz);
|
||||
if (ret == ERROR_SUCCESS) {
|
||||
// Get value from registry
|
||||
TCHAR *reg_val = new TCHAR[value_sz];
|
||||
RegQueryValueEx(hKey, _T("diskpath"), NULL, NULL, (BYTE*)reg_val, &value_sz);
|
||||
RegQueryValueEx(hKey, _T("diskpath"), NULL, NULL, (BYTE*)str, &value_sz);
|
||||
|
||||
// Append ISLE.EXE to diskpath
|
||||
isle_diskpath = new TCHAR[MAX_PATH];
|
||||
wsprintf(isle_diskpath, _T("%s\\ISLE.EXE"), reg_val);
|
||||
|
||||
// Delete reg val
|
||||
delete [] reg_val;
|
||||
_tcscat(str, _T("\\ISLE.EXE"));
|
||||
}
|
||||
|
||||
RegCloseKey(hKey);
|
||||
}
|
||||
|
||||
// Validate diskpath, either we couldn't find the registry entry or it was incorrect somehow
|
||||
while (!isle_diskpath || !PathFileExists(isle_diskpath)) {
|
||||
if (!isle_diskpath) {
|
||||
// Allocate diskpath if it hasn't been allocated yet
|
||||
isle_diskpath = new TCHAR[MAX_PATH];
|
||||
}
|
||||
|
||||
while (!_tcslen(str) || !PathFileExists(str)) {
|
||||
// Ask user where LEGO Island is installed
|
||||
OPENFILENAME fn;
|
||||
ZeroMemory(&fn, sizeof(fn));
|
||||
fn.hwndOwner = parent;
|
||||
fn.lStructSize = sizeof(fn);
|
||||
fn.lpstrFile = isle_diskpath;
|
||||
fn.lpstrFile = str;
|
||||
fn.lpstrFile[0] = '\0';
|
||||
fn.nMaxFile = MAX_PATH;
|
||||
fn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
|
||||
|
@ -89,27 +97,119 @@ LPTSTR Launcher::FindInstallation(HWND parent)
|
|||
|
||||
if (!GetOpenFileName(&fn)) {
|
||||
// If they cancelled the dialog, break out of the loop
|
||||
delete [] isle_diskpath;
|
||||
isle_diskpath = NULL;
|
||||
success = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return isle_diskpath;
|
||||
return success;
|
||||
}
|
||||
|
||||
BOOL Launcher::Patch(HANDLE process)
|
||||
BOOL Launcher::ExtractLibrary(LPTSTR str, SIZE_T len)
|
||||
{
|
||||
UINT32 width = 320;
|
||||
UINT32 height = 240;
|
||||
|
||||
if (!WriteProcessMemory(process, (LPVOID)0x00410048, &width, sizeof(width), NULL)) {
|
||||
// Find temporary location to store DLL
|
||||
if (!GetTempPath(len, str)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!WriteProcessMemory(process, (LPVOID)0x0041004C, &height, sizeof(height), NULL)) {
|
||||
_tcscat(str, _T("REBLD.DLL"));
|
||||
|
||||
// Open file
|
||||
HANDLE file = CreateFile(str, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (!file) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
// Extract DLL which should have been compiled in as a resource
|
||||
HRSRC res = FindResource(AfxGetInstanceHandle(), MAKEINTRESOURCE(WORKER_DLL), RT_RCDATA);
|
||||
if (!res) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
HGLOBAL res_resource = LoadResource(NULL, res);
|
||||
if (!res_resource) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
LPVOID res_data = LockResource(res_resource);
|
||||
DWORD res_sz = SizeofResource(NULL, res);
|
||||
|
||||
DWORD writtenBytes;
|
||||
BOOL success = WriteFile(file, res_data, res_sz, &writtenBytes, NULL);
|
||||
|
||||
UnlockResource(res_resource);
|
||||
|
||||
FreeResource(res_resource);
|
||||
|
||||
CloseHandle(file);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
BOOL Launcher::CopyIsleToTemp(LPCTSTR src, LPTSTR dst)
|
||||
{
|
||||
BOOL success = FALSE;
|
||||
|
||||
TCHAR tempDir[MAX_PATH];
|
||||
if (GetTempPath(MAX_PATH, tempDir)) {
|
||||
_tcscpy(dst, tempDir);
|
||||
_tcscat(dst, _T("ISLE.EXE"));
|
||||
if (CopyFile(src, dst, FALSE)) {
|
||||
// Force our copy to load our DLL
|
||||
success = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
BOOL Launcher::PatchIsle(LPCTSTR filename)
|
||||
{
|
||||
BOOL success = false;
|
||||
|
||||
if (HANDLE file = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)) {
|
||||
// Force ISLE to load our DLL. This system will work on all platforms including Windows 95.
|
||||
if (ReplacePatternInFile(file, "timeGetTime\0WINMM", "rbldGetTime\0REBLD", 17)) {
|
||||
success = true;
|
||||
}
|
||||
|
||||
CloseHandle(file);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
BOOL Launcher::ReplacePatternInFile(HANDLE file, const char *pattern, const char *replace, LONG sz)
|
||||
{
|
||||
BOOL success = FALSE;
|
||||
|
||||
SetFilePointer(file, 0, 0, FILE_BEGIN);
|
||||
|
||||
char *data = new char[sz];
|
||||
|
||||
// Find pattern in the file
|
||||
DWORD nbBytesRead;
|
||||
do {
|
||||
ReadFile(file, data, sz, &nbBytesRead, NULL);
|
||||
SetFilePointer(file, -sz + 1, 0, FILE_CURRENT);
|
||||
} while ((LONG)nbBytesRead == sz && memcmp(data, pattern, sz));
|
||||
|
||||
if ((LONG)nbBytesRead == sz) {
|
||||
// Must have found pattern
|
||||
SetFilePointer(file, -1, 0, FILE_CURRENT);
|
||||
|
||||
// Overwrite with replace
|
||||
DWORD nbBytesWritten; // We don't use this value, but Windows 95 will fail without it
|
||||
if (WriteFile(file, replace, sz, &nbBytesWritten, NULL)) {
|
||||
success = TRUE;
|
||||
} else {
|
||||
char buf[200];
|
||||
sprintf(buf, "Failed to write to file with error: %lx", GetLastError());
|
||||
MessageBoxA(0, buf, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
delete [] data;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
|
|
@ -11,9 +11,15 @@ public:
|
|||
static HANDLE Launch(HWND parent);
|
||||
|
||||
private:
|
||||
static LPTSTR FindInstallation(HWND parent);
|
||||
static BOOL FindInstallation(HWND parent, LPTSTR isle_diskpath);
|
||||
|
||||
static BOOL Patch(HANDLE process);
|
||||
static BOOL ExtractLibrary(LPTSTR str, SIZE_T len);
|
||||
|
||||
static BOOL CopyIsleToTemp(LPCTSTR src, LPTSTR dst);
|
||||
|
||||
static BOOL PatchIsle(LPCTSTR filename);
|
||||
|
||||
static BOOL ReplacePatternInFile(HANDLE file, const char *pattern, const char *replace, LONG sz);
|
||||
|
||||
};
|
||||
|
||||
|
|
154
src/window.cpp
154
src/window.cpp
|
@ -1,60 +1,84 @@
|
|||
#include "window.h"
|
||||
|
||||
#include <WINDOWS.H>
|
||||
|
||||
#include "launcher.h"
|
||||
#include "../res/resource.h"
|
||||
|
||||
#define super CWnd
|
||||
|
||||
static const int WM_CHILD_CLOSED = WM_USER + 1;
|
||||
|
||||
CString GetResourceString(UINT id)
|
||||
{
|
||||
CString cstr;
|
||||
cstr.LoadString(id);
|
||||
return cstr;
|
||||
}
|
||||
|
||||
CRebuilderWindow::CRebuilderWindow()
|
||||
{
|
||||
// Declare default width/height
|
||||
static const UINT defaultWindowWidth = 420;
|
||||
static const UINT defaultWindowHeight = 420;
|
||||
|
||||
// Register custom window class
|
||||
LPCTSTR wndclass = AfxRegisterWndClass(CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW,
|
||||
LoadCursor(NULL, IDC_ARROW),
|
||||
(HBRUSH) (COLOR_WINDOW),
|
||||
(HBRUSH) COLOR_WINDOW,
|
||||
LoadIcon(AfxGetInstanceHandle(), _T("IDI_ICON1")));
|
||||
|
||||
// Create form
|
||||
CreateEx(WS_EX_OVERLAPPEDWINDOW, wndclass, _T("LEGO Island Rebuilder"), WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL);
|
||||
CreateEx(WS_EX_OVERLAPPEDWINDOW, wndclass, GetResourceString(IDS_TITLE), WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL);
|
||||
ModifyStyleEx(WS_EX_CLIENTEDGE, 0, 0);
|
||||
|
||||
// Create title
|
||||
m_cTopLevelTitle.Create(_T("LEGO Island Rebuilder"), WS_CHILD | WS_VISIBLE | SS_CENTER, CRect(), this);
|
||||
m_cTopLevelTitle.Create(GetResourceString(IDS_TITLE), WS_CHILD | WS_VISIBLE | SS_CENTER, CRect(), this);
|
||||
|
||||
// Create subtitle
|
||||
m_cTopLevelSubtitle.Create(_T("by MattKC (itsmattkc.com)"), WS_CHILD | WS_VISIBLE | SS_CENTER, CRect(), this);
|
||||
m_cTopLevelSubtitle.Create(GetResourceString(IDS_SUBTITLE), WS_CHILD | WS_VISIBLE | SS_CENTER | SS_NOTIFY, CRect(), this, ID_SUBTITLE);
|
||||
|
||||
// Create tab control
|
||||
m_cTabCtrl.Create(WS_CHILD | WS_VISIBLE, CRect(), this, ID_TABCTRL);
|
||||
|
||||
// Initialize common TCITEM
|
||||
TCITEM tabItem;
|
||||
ZeroMemory(&tabItem, sizeof(tabItem));
|
||||
tabItem.mask |= TCIF_TEXT;
|
||||
tabItem.pszText = new TCHAR[100];
|
||||
|
||||
// Add "patches" tab
|
||||
TCITEM patchesItem;
|
||||
ZeroMemory(&patchesItem, sizeof(patchesItem));
|
||||
patchesItem.pszText = _T("Patches");
|
||||
patchesItem.mask |= TCIF_TEXT;
|
||||
m_cTabCtrl.InsertItem(0, &patchesItem);
|
||||
_tcscpy(tabItem.pszText, GetResourceString(IDS_PATCHES));
|
||||
m_cTabCtrl.InsertItem(TAB_PATCHES, &tabItem);
|
||||
|
||||
// Add "music" tab
|
||||
TCITEM musicItem;
|
||||
ZeroMemory(&musicItem, sizeof(musicItem));
|
||||
musicItem.pszText = _T("Music");
|
||||
musicItem.mask |= TCIF_TEXT;
|
||||
m_cTabCtrl.InsertItem(1, &musicItem);
|
||||
_tcscpy(tabItem.pszText, GetResourceString(IDS_MUSIC));
|
||||
m_cTabCtrl.InsertItem(TAB_MUSIC, &tabItem);
|
||||
|
||||
delete [] tabItem.pszText;
|
||||
|
||||
// Create property grid (placeholder right now)
|
||||
m_cPatchGrid.Create(_T("Property Grid"), WS_CHILD | WS_VISIBLE, CRect(), &m_cTabCtrl);
|
||||
m_cMusicTable.Create(_T("Music Tab"), WS_CHILD, CRect(), &m_cTabCtrl);
|
||||
|
||||
// Create run button
|
||||
m_cRunBtn.Create(_T("Run"), WS_CHILD | WS_VISIBLE, CRect(), this, ID_RUN);
|
||||
m_cRunBtn.Create(GetResourceString(IDS_RUN), WS_CHILD | WS_VISIBLE, CRect(), this, ID_RUN);
|
||||
|
||||
// Create run button
|
||||
m_cKillBtn.Create(_T("Kill"), WS_CHILD, CRect(), this, ID_KILL);
|
||||
|
||||
// Call this after all UI objects are created because this will call LayoutObjects
|
||||
SetWindowPos(NULL, 0, 0, defaultWindowWidth, defaultWindowHeight, 0);
|
||||
CenterWindow(NULL);
|
||||
m_cKillBtn.Create(GetResourceString(IDS_KILL), WS_CHILD, CRect(), this, ID_KILL);
|
||||
|
||||
// Set fonts
|
||||
SetGUIFonts();
|
||||
|
||||
// Call this after all UI objects are created because this will call LayoutObjects
|
||||
static const UINT defaultWindowWidth = 32;
|
||||
static const UINT defaultWindowHeight = 32;
|
||||
SetWindowPos(NULL, 0, 0, m_nFontHeight * defaultWindowWidth, m_nFontHeight * defaultWindowHeight, 0);
|
||||
CenterWindow(NULL);
|
||||
}
|
||||
|
||||
DWORD WINAPI WaitForProcessToClose(HANDLE hProcess)
|
||||
{
|
||||
WaitForSingleObject(hProcess, INFINITE);
|
||||
AfxGetMainWnd()->PostMessage(WM_CHILD_CLOSED, (WPARAM)hProcess);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CRebuilderWindow::OnRunClick()
|
||||
|
@ -63,8 +87,11 @@ void CRebuilderWindow::OnRunClick()
|
|||
|
||||
if (proc) {
|
||||
m_lProcesses.push_back(proc);
|
||||
m_cRunBtn.ShowWindow(SW_HIDE);
|
||||
m_cKillBtn.ShowWindow(SW_SHOWNORMAL);
|
||||
SwitchButtonMode(TRUE);
|
||||
|
||||
// Register callback when process exits
|
||||
DWORD threadId; // We don't use this, but Windows 95 will fail without it
|
||||
CloseHandle(CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WaitForProcessToClose, proc, 0, &threadId));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,8 +102,12 @@ void CRebuilderWindow::OnKillClick()
|
|||
}
|
||||
m_lProcesses.clear();
|
||||
|
||||
m_cKillBtn.ShowWindow(SW_HIDE);
|
||||
m_cRunBtn.ShowWindow(SW_SHOWNORMAL);
|
||||
SwitchButtonMode(FALSE);
|
||||
}
|
||||
|
||||
void CRebuilderWindow::OnSubtitleClick()
|
||||
{
|
||||
ShellExecute(NULL, _T("open"), _T("http://itsmattkc.com/"), NULL, NULL, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
void CRebuilderWindow::OnSize(UINT type, int width, int height)
|
||||
|
@ -92,7 +123,7 @@ void CRebuilderWindow::OnSize(UINT type, int width, int height)
|
|||
topComponentEnd += m_nFontHeight;
|
||||
|
||||
// Bottom components
|
||||
const int btnHeight = 25;
|
||||
const int btnHeight = m_nFontHeight*18/10;
|
||||
int bottomComponentStart = height - btnHeight - padding;
|
||||
int bottomComponentWidth = width - dblPadding;
|
||||
m_cRunBtn.SetWindowPos(NULL, padding, bottomComponentStart, bottomComponentWidth, btnHeight, 0);
|
||||
|
@ -101,15 +132,30 @@ void CRebuilderWindow::OnSize(UINT type, int width, int height)
|
|||
// Center components
|
||||
int centerHeight = bottomComponentStart - topComponentEnd;
|
||||
m_cTabCtrl.SetWindowPos(NULL, padding, topComponentEnd + padding, bottomComponentWidth, centerHeight - dblPadding, 0);
|
||||
|
||||
// Tabs
|
||||
RECT tabClientRect;
|
||||
m_cTabCtrl.GetClientRect(&tabClientRect);
|
||||
m_cTabCtrl.AdjustRect(FALSE, &tabClientRect);
|
||||
m_cPatchGrid.SetWindowPos(NULL, tabClientRect.left, tabClientRect.top, tabClientRect.right - tabClientRect.left, tabClientRect.bottom - tabClientRect.top, 0);
|
||||
m_cMusicTable.SetWindowPos(NULL, tabClientRect.left, tabClientRect.top, tabClientRect.right - tabClientRect.left, tabClientRect.bottom - tabClientRect.top, 0);
|
||||
}
|
||||
|
||||
void CRebuilderWindow::OnGetMinMaxInfo(MINMAXINFO *info)
|
||||
{
|
||||
static const LONG minimumWindowWidth = 160;
|
||||
static const LONG minimumWindowHeight = 160;
|
||||
static const LONG minimumWindowWidth = 12;
|
||||
static const LONG minimumWindowHeight = 12;
|
||||
|
||||
info->ptMinTrackSize.x = minimumWindowWidth;
|
||||
info->ptMinTrackSize.y = minimumWindowHeight;
|
||||
info->ptMinTrackSize.x = m_nFontHeight * minimumWindowWidth;
|
||||
info->ptMinTrackSize.y = m_nFontHeight * minimumWindowHeight;
|
||||
}
|
||||
|
||||
void CRebuilderWindow::OnTabSelChange(NMHDR *pNMHDR, LRESULT *pResult)
|
||||
{
|
||||
int tab = m_cTabCtrl.GetCurSel();
|
||||
|
||||
m_cPatchGrid.ShowWindow((tab == TAB_PATCHES) ? SW_SHOWNORMAL : SW_HIDE);
|
||||
m_cMusicTable.ShowWindow((tab == TAB_MUSIC) ? SW_SHOWNORMAL : SW_HIDE);
|
||||
}
|
||||
|
||||
BOOL CRebuilderWindow::SetFont(HWND child, LPARAM font)
|
||||
|
@ -118,6 +164,28 @@ BOOL CRebuilderWindow::SetFont(HWND child, LPARAM font)
|
|||
return true;
|
||||
}
|
||||
|
||||
LRESULT CRebuilderWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (uMsg == WM_CHILD_CLOSED) {
|
||||
HANDLE hProcess = (HANDLE)wParam;
|
||||
|
||||
for (std::vector<HANDLE>::iterator it=m_lProcesses.begin(); it!=m_lProcesses.end(); it++) {
|
||||
if (*it == hProcess) {
|
||||
m_lProcesses.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_lProcesses.empty()) {
|
||||
SwitchButtonMode(FALSE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
return super::WindowProc(uMsg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
void CRebuilderWindow::SetGUIFonts()
|
||||
{
|
||||
// Retrieve default GUI font
|
||||
|
@ -139,6 +207,13 @@ void CRebuilderWindow::SetGUIFonts()
|
|||
SetFont(m_cRunBtn.GetSafeHwnd(), (LPARAM)bold);
|
||||
SetFont(m_cKillBtn.GetSafeHwnd(), (LPARAM)bold);
|
||||
|
||||
// Create link variant for subtitle
|
||||
lf.lfWeight = FW_NORMAL;
|
||||
lf.lfUnderline = TRUE;
|
||||
|
||||
HFONT link = CreateFontIndirect(&lf);
|
||||
SetFont(m_cTopLevelSubtitle.GetSafeHwnd(), (LPARAM)link);
|
||||
|
||||
// While here, get height of font for layout purposes
|
||||
HDC hDC = ::GetDC(NULL);
|
||||
HGDIOBJ hFontOld = SelectObject(hDC, defaultFont);
|
||||
|
@ -149,9 +224,22 @@ void CRebuilderWindow::SetGUIFonts()
|
|||
::ReleaseDC(NULL, hDC);
|
||||
}
|
||||
|
||||
void CRebuilderWindow::SwitchButtonMode(BOOL running)
|
||||
{
|
||||
if (running) {
|
||||
m_cRunBtn.ShowWindow(SW_HIDE);
|
||||
m_cKillBtn.ShowWindow(SW_SHOWNORMAL);
|
||||
} else {
|
||||
m_cKillBtn.ShowWindow(SW_HIDE);
|
||||
m_cRunBtn.ShowWindow(SW_SHOWNORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_MESSAGE_MAP(CRebuilderWindow, super)
|
||||
ON_WM_SIZE()
|
||||
ON_WM_GETMINMAXINFO()
|
||||
ON_BN_CLICKED(ID_RUN, OnRunClick)
|
||||
ON_BN_CLICKED(ID_KILL, OnKillClick)
|
||||
ON_BN_CLICKED(ID_SUBTITLE, OnSubtitleClick)
|
||||
ON_NOTIFY(TCN_SELCHANGE, ID_TABCTRL, OnTabSelChange)
|
||||
END_MESSAGE_MAP()
|
||||
|
|
25
src/window.h
25
src/window.h
|
@ -5,6 +5,8 @@
|
|||
#include <AFXWIN.H>
|
||||
#include <VECTOR>
|
||||
|
||||
#include "clinkstatic.h"
|
||||
|
||||
class CRebuilderWindow : public CFrameWnd
|
||||
{
|
||||
public:
|
||||
|
@ -14,31 +16,50 @@ public:
|
|||
|
||||
afx_msg void OnKillClick();
|
||||
|
||||
afx_msg void OnSubtitleClick();
|
||||
|
||||
afx_msg void OnSize(UINT type, int width, int height);
|
||||
|
||||
afx_msg void OnGetMinMaxInfo(MINMAXINFO *info);
|
||||
|
||||
afx_msg void OnTabSelChange(NMHDR* pNMHDR, LRESULT* pResult);
|
||||
|
||||
static BOOL CALLBACK SetFont(HWND child, LPARAM font);
|
||||
|
||||
virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||||
|
||||
private:
|
||||
void SetGUIFonts();
|
||||
|
||||
void SwitchButtonMode(BOOL running);
|
||||
|
||||
enum {
|
||||
ID_RUN = 1000,
|
||||
ID_KILL,
|
||||
ID_TABCTRL
|
||||
ID_TABCTRL,
|
||||
ID_SUBTITLE,
|
||||
ID_PATCHGRID,
|
||||
ID_COUNT
|
||||
};
|
||||
|
||||
enum Tab {
|
||||
TAB_PATCHES,
|
||||
TAB_MUSIC
|
||||
};
|
||||
|
||||
UINT m_nFontHeight;
|
||||
|
||||
CStatic m_cTopLevelTitle;
|
||||
CStatic m_cTopLevelSubtitle;
|
||||
CLinkStatic m_cTopLevelSubtitle;
|
||||
|
||||
CTabCtrl m_cTabCtrl;
|
||||
|
||||
CButton m_cRunBtn;
|
||||
CButton m_cKillBtn;
|
||||
|
||||
CStatic m_cPatchGrid;
|
||||
CStatic m_cMusicTable;
|
||||
|
||||
std::vector<HANDLE> m_lProcesses;
|
||||
|
||||
DECLARE_MESSAGE_MAP()
|
||||
|
|
Loading…
Reference in a new issue