further work

This commit is contained in:
itsmattkc 2021-09-13 03:58:22 -07:00
parent 2506395dcf
commit 63dd568e64
5 changed files with 248 additions and 65 deletions

View file

@ -10,6 +10,8 @@ add_executable(Rebuilder WIN32
res/res.rc res/res.rc
src/app.cpp src/app.cpp
src/app.h src/app.h
src/launcher.cpp
src/launcher.h
src/window.cpp src/window.cpp
src/window.h src/window.h
) )
@ -19,3 +21,5 @@ if (BUILD_UNICODE)
target_compile_definitions(Rebuilder PRIVATE UNICODE _UNICODE) target_compile_definitions(Rebuilder PRIVATE UNICODE _UNICODE)
target_link_options(Rebuilder PRIVATE /entry:wWinMainCRTStartup) target_link_options(Rebuilder PRIVATE /entry:wWinMainCRTStartup)
endif() endif()
target_link_libraries(Rebuilder PRIVATE shlwapi.lib)

115
src/launcher.cpp Normal file
View file

@ -0,0 +1,115 @@
#include "launcher.h"
#include <COMMDLG.H>
#include <SHLWAPI.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);
}
}
delete [] filename;
return ret;
}
LPTSTR Launcher::FindInstallation(HWND parent)
{
// Search for LEGO Island disk path
DWORD value_sz;
TCHAR *isle_diskpath = NULL;
// On install, LEGO Island records its installation directory in the registry
HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Mindscape\\LEGO Island"), 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
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);
// 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;
}
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];
}
// 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[0] = '\0';
fn.nMaxFile = MAX_PATH;
fn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
fn.lpstrTitle = _T("Where is LEGO Island installed?");
fn.lpstrFilter = _T("ISLE.EXE\0ISLE.EXE\0");
if (!GetOpenFileName(&fn)) {
// If they cancelled the dialog, break out of the loop
delete [] isle_diskpath;
isle_diskpath = NULL;
break;
}
}
return isle_diskpath;
}
BOOL Launcher::Patch(HANDLE process)
{
UINT32 width = 320;
UINT32 height = 240;
if (!WriteProcessMemory(process, (LPVOID)0x00410048, &width, sizeof(width), NULL)) {
return FALSE;
}
if (!WriteProcessMemory(process, (LPVOID)0x0041004C, &height, sizeof(height), NULL)) {
return FALSE;
}
return TRUE;
}

20
src/launcher.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef LAUNCHER_H
#define LAUNCHER_H
#include <AFXWIN.H>
class Launcher
{
public:
Launcher();
static HANDLE Launch(HWND parent);
private:
static LPTSTR FindInstallation(HWND parent);
static BOOL Patch(HANDLE process);
};
#endif // LAUNCHER_H

View file

@ -1,6 +1,8 @@
#include "window.h" #include "window.h"
#define super CFrameWnd #include "launcher.h"
#define super CWnd
CRebuilderWindow::CRebuilderWindow() CRebuilderWindow::CRebuilderWindow()
{ {
@ -8,45 +10,24 @@ CRebuilderWindow::CRebuilderWindow()
static const UINT defaultWindowWidth = 420; static const UINT defaultWindowWidth = 420;
static const UINT defaultWindowHeight = 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),
LoadIcon(AfxGetInstanceHandle(), _T("IDI_ICON1")));
// Create form // Create form
Create(NULL, _T("LEGO Island Rebuilder")); CreateEx(WS_EX_OVERLAPPEDWINDOW, wndclass, _T("LEGO Island Rebuilder"), WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, NULL, NULL);
ModifyStyleEx(WS_EX_CLIENTEDGE, 0, 0);
// Set window icon to application icon
TCHAR filename[MAX_PATH];
GetModuleFileName(NULL, filename, MAX_PATH);
WORD index;
HICON icon = ExtractAssociatedIcon(AfxGetInstanceHandle(), filename, &index);
SetIcon(icon, TRUE);
// Get default Win32 dialog font
m_fDialogFont.CreateStockObject(DEFAULT_GUI_FONT);
// Create bolded variant of the above
LOGFONT lf;
m_fDialogFont.GetLogFont(&lf);
lf.lfWeight = FW_BOLD;
m_fBoldDialogFont.CreateFontIndirect(&lf);
// Get information about font height for layout
HDC hDC = ::GetDC(NULL);
HGDIOBJ hFontOld = SelectObject(hDC, m_fDialogFont.GetSafeHandle());
TEXTMETRIC tm;
GetTextMetrics(hDC, &tm);
m_nFontHeight = tm.tmHeight + tm.tmExternalLeading;
SelectObject(hDC, hFontOld);
::ReleaseDC(NULL, hDC);
// Create title // Create title
m_cTopLevelTitle.Create(_T("LEGO Island Rebuilder"), WS_CHILD | WS_VISIBLE | SS_CENTER, CRect(), this); m_cTopLevelTitle.Create(_T("LEGO Island Rebuilder"), WS_CHILD | WS_VISIBLE | SS_CENTER, CRect(), this);
m_cTopLevelTitle.SetFont(&m_fBoldDialogFont);
// Create subtitle // Create subtitle
m_cTopLevelSubtitle.Create(_T("by MattKC (itsmattkc.com)"), WS_CHILD | WS_VISIBLE | SS_CENTER, CRect(), this); m_cTopLevelSubtitle.Create(_T("by MattKC (itsmattkc.com)"), WS_CHILD | WS_VISIBLE | SS_CENTER, CRect(), this);
m_cTopLevelSubtitle.SetFont(&m_fDialogFont);
// Create tab control // Create tab control
m_cTabCtrl.Create(WS_CHILD | WS_VISIBLE, CRect(), this, IDI_TABCTRL); m_cTabCtrl.Create(WS_CHILD | WS_VISIBLE, CRect(), this, ID_TABCTRL);
m_cTabCtrl.SetFont(&m_fDialogFont);
// Add "patches" tab // Add "patches" tab
TCITEM patchesItem; TCITEM patchesItem;
@ -63,46 +44,42 @@ CRebuilderWindow::CRebuilderWindow()
m_cTabCtrl.InsertItem(1, &musicItem); m_cTabCtrl.InsertItem(1, &musicItem);
// Create run button // Create run button
m_cRunBtn.Create(_T("Run"), WS_CHILD | WS_VISIBLE, CRect(), this, IDI_RUN); m_cRunBtn.Create(_T("Run"), WS_CHILD | WS_VISIBLE, CRect(), this, ID_RUN);
m_cRunBtn.SetFont(&m_fBoldDialogFont);
// 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 // Call this after all UI objects are created because this will call LayoutObjects
SetWindowPos(NULL, 0, 0, defaultWindowWidth, defaultWindowHeight, 0); SetWindowPos(NULL, 0, 0, defaultWindowWidth, defaultWindowHeight, 0);
CenterWindow(NULL); CenterWindow(NULL);
// Set background color to Win32 window color // Set fonts
::SetClassLong(GetSafeHwnd(), GCL_HBRBACKGROUND, COLOR_WINDOW); SetGUIFonts();
} }
LRESULT CRebuilderWindow::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) void CRebuilderWindow::OnRunClick()
{ {
switch (message) { HANDLE proc = Launcher::Launch(this->GetSafeHwnd());
case WM_SIZE:
if (proc) {
m_lProcesses.push_back(proc);
m_cRunBtn.ShowWindow(SW_HIDE);
m_cKillBtn.ShowWindow(SW_SHOWNORMAL);
}
}
void CRebuilderWindow::OnKillClick()
{ {
UINT width = LOWORD(lParam); for (std::vector<HANDLE>::iterator it=m_lProcesses.begin(); it!=m_lProcesses.end(); it++) {
UINT height = HIWORD(lParam); TerminateProcess(*it, 0);
LayoutObjects(width, height);
break;
} }
case WM_GETMINMAXINFO: m_lProcesses.clear();
{
static const LONG minimumWindowWidth = 160;
static const LONG minimumWindowHeight = 160;
MINMAXINFO *minmaxInfo = (MINMAXINFO*)lParam; m_cKillBtn.ShowWindow(SW_HIDE);
m_cRunBtn.ShowWindow(SW_SHOWNORMAL);
minmaxInfo->ptMinTrackSize.x = minimumWindowWidth;
minmaxInfo->ptMinTrackSize.y = minimumWindowHeight;
return 0;
}
} }
return super::WindowProc(message, wParam, lParam); void CRebuilderWindow::OnSize(UINT type, int width, int height)
}
void CRebuilderWindow::LayoutObjects(UINT width, UINT height)
{ {
const int padding = m_nFontHeight/2; const int padding = m_nFontHeight/2;
const int dblPadding = padding * 2; const int dblPadding = padding * 2;
@ -119,8 +96,62 @@ void CRebuilderWindow::LayoutObjects(UINT width, UINT height)
int bottomComponentStart = height - btnHeight - padding; int bottomComponentStart = height - btnHeight - padding;
int bottomComponentWidth = width - dblPadding; int bottomComponentWidth = width - dblPadding;
m_cRunBtn.SetWindowPos(NULL, padding, bottomComponentStart, bottomComponentWidth, btnHeight, 0); m_cRunBtn.SetWindowPos(NULL, padding, bottomComponentStart, bottomComponentWidth, btnHeight, 0);
m_cKillBtn.SetWindowPos(NULL, padding, bottomComponentStart, bottomComponentWidth, btnHeight, 0);
// Center components // Center components
int centerHeight = bottomComponentStart - topComponentEnd; int centerHeight = bottomComponentStart - topComponentEnd;
m_cTabCtrl.SetWindowPos(NULL, padding, topComponentEnd + padding, bottomComponentWidth, centerHeight - dblPadding, 0); m_cTabCtrl.SetWindowPos(NULL, padding, topComponentEnd + padding, bottomComponentWidth, centerHeight - dblPadding, 0);
} }
void CRebuilderWindow::OnGetMinMaxInfo(MINMAXINFO *info)
{
static const LONG minimumWindowWidth = 160;
static const LONG minimumWindowHeight = 160;
info->ptMinTrackSize.x = minimumWindowWidth;
info->ptMinTrackSize.y = minimumWindowHeight;
}
BOOL CRebuilderWindow::SetFont(HWND child, LPARAM font)
{
::SendMessage(child, WM_SETFONT, font, true);
return true;
}
void CRebuilderWindow::SetGUIFonts()
{
// Retrieve default GUI font
HFONT defaultFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
// Set on all UI objects
EnumChildWindows(this->GetSafeHwnd(), (WNDENUMPROC)SetFont, (LPARAM)defaultFont);
// Get LOGFONT to create bold variant
LOGFONT lf;
GetObject(defaultFont, sizeof(lf), &lf);
lf.lfWeight = FW_BOLD;
// Create font from LOGFONT
HFONT bold = CreateFontIndirect(&lf);
// Set bold variant on relevant objects
SetFont(m_cTopLevelTitle.GetSafeHwnd(), (LPARAM)bold);
SetFont(m_cRunBtn.GetSafeHwnd(), (LPARAM)bold);
SetFont(m_cKillBtn.GetSafeHwnd(), (LPARAM)bold);
// While here, get height of font for layout purposes
HDC hDC = ::GetDC(NULL);
HGDIOBJ hFontOld = SelectObject(hDC, defaultFont);
TEXTMETRIC tm;
GetTextMetrics(hDC, &tm);
m_nFontHeight = tm.tmHeight + tm.tmExternalLeading;
SelectObject(hDC, hFontOld);
::ReleaseDC(NULL, hDC);
}
BEGIN_MESSAGE_MAP(CRebuilderWindow, super)
ON_WM_SIZE()
ON_WM_GETMINMAXINFO()
ON_BN_CLICKED(ID_RUN, OnRunClick)
ON_BN_CLICKED(ID_KILL, OnKillClick)
END_MESSAGE_MAP()

View file

@ -3,24 +3,32 @@
#include <AFXCMN.H> #include <AFXCMN.H>
#include <AFXWIN.H> #include <AFXWIN.H>
#include <VECTOR>
class CRebuilderWindow : public CFrameWnd class CRebuilderWindow : public CFrameWnd
{ {
public: public:
CRebuilderWindow(); CRebuilderWindow();
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); afx_msg void OnRunClick();
afx_msg void OnKillClick();
afx_msg void OnSize(UINT type, int width, int height);
afx_msg void OnGetMinMaxInfo(MINMAXINFO *info);
static BOOL CALLBACK SetFont(HWND child, LPARAM font);
private: private:
void LayoutObjects(UINT width, UINT height); void SetGUIFonts();
enum { enum {
IDI_RUN, ID_RUN = 1000,
IDI_TABCTRL ID_KILL,
ID_TABCTRL
}; };
CFont m_fDialogFont;
CFont m_fBoldDialogFont;
UINT m_nFontHeight; UINT m_nFontHeight;
CStatic m_cTopLevelTitle; CStatic m_cTopLevelTitle;
@ -29,6 +37,11 @@ private:
CTabCtrl m_cTabCtrl; CTabCtrl m_cTabCtrl;
CButton m_cRunBtn; CButton m_cRunBtn;
CButton m_cKillBtn;
std::vector<HANDLE> m_lProcesses;
DECLARE_MESSAGE_MAP()
}; };