mirror of
https://github.com/isledecomp/LEGOIslandRebuilder.git
synced 2024-11-27 09:35:41 -05:00
further work
This commit is contained in:
parent
2506395dcf
commit
63dd568e64
5 changed files with 248 additions and 65 deletions
|
@ -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
115
src/launcher.cpp
Normal 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
20
src/launcher.h
Normal 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
|
147
src/window.cpp
147
src/window.cpp
|
@ -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()
|
||||||
|
|
25
src/window.h
25
src/window.h
|
@ -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()
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue