mirror of
https://github.com/isledecomp/LEGOIslandRebuilder.git
synced 2024-11-23 15:48:03 -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
|
||||
src/app.cpp
|
||||
src/app.h
|
||||
src/launcher.cpp
|
||||
src/launcher.h
|
||||
src/window.cpp
|
||||
src/window.h
|
||||
)
|
||||
|
@ -19,3 +21,5 @@ if (BUILD_UNICODE)
|
|||
target_compile_definitions(Rebuilder PRIVATE UNICODE _UNICODE)
|
||||
target_link_options(Rebuilder PRIVATE /entry:wWinMainCRTStartup)
|
||||
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"
|
||||
|
||||
#define super CFrameWnd
|
||||
#include "launcher.h"
|
||||
|
||||
#define super CWnd
|
||||
|
||||
CRebuilderWindow::CRebuilderWindow()
|
||||
{
|
||||
|
@ -8,45 +10,24 @@ CRebuilderWindow::CRebuilderWindow()
|
|||
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),
|
||||
LoadIcon(AfxGetInstanceHandle(), _T("IDI_ICON1")));
|
||||
|
||||
// Create form
|
||||
Create(NULL, _T("LEGO Island Rebuilder"));
|
||||
|
||||
// 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);
|
||||
CreateEx(WS_EX_OVERLAPPEDWINDOW, wndclass, _T("LEGO Island Rebuilder"), 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.SetFont(&m_fBoldDialogFont);
|
||||
|
||||
// Create subtitle
|
||||
m_cTopLevelSubtitle.Create(_T("by MattKC (itsmattkc.com)"), WS_CHILD | WS_VISIBLE | SS_CENTER, CRect(), this);
|
||||
m_cTopLevelSubtitle.SetFont(&m_fDialogFont);
|
||||
|
||||
// Create tab control
|
||||
m_cTabCtrl.Create(WS_CHILD | WS_VISIBLE, CRect(), this, IDI_TABCTRL);
|
||||
m_cTabCtrl.SetFont(&m_fDialogFont);
|
||||
m_cTabCtrl.Create(WS_CHILD | WS_VISIBLE, CRect(), this, ID_TABCTRL);
|
||||
|
||||
// Add "patches" tab
|
||||
TCITEM patchesItem;
|
||||
|
@ -63,46 +44,42 @@ CRebuilderWindow::CRebuilderWindow()
|
|||
m_cTabCtrl.InsertItem(1, &musicItem);
|
||||
|
||||
// Create run button
|
||||
m_cRunBtn.Create(_T("Run"), WS_CHILD | WS_VISIBLE, CRect(), this, IDI_RUN);
|
||||
m_cRunBtn.SetFont(&m_fBoldDialogFont);
|
||||
m_cRunBtn.Create(_T("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);
|
||||
|
||||
// Set background color to Win32 window color
|
||||
::SetClassLong(GetSafeHwnd(), GCL_HBRBACKGROUND, COLOR_WINDOW);
|
||||
// Set fonts
|
||||
SetGUIFonts();
|
||||
}
|
||||
|
||||
LRESULT CRebuilderWindow::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
|
||||
void CRebuilderWindow::OnRunClick()
|
||||
{
|
||||
switch (message) {
|
||||
case WM_SIZE:
|
||||
HANDLE proc = Launcher::Launch(this->GetSafeHwnd());
|
||||
|
||||
if (proc) {
|
||||
m_lProcesses.push_back(proc);
|
||||
m_cRunBtn.ShowWindow(SW_HIDE);
|
||||
m_cKillBtn.ShowWindow(SW_SHOWNORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
void CRebuilderWindow::OnKillClick()
|
||||
{
|
||||
UINT width = LOWORD(lParam);
|
||||
UINT height = HIWORD(lParam);
|
||||
|
||||
LayoutObjects(width, height);
|
||||
break;
|
||||
for (std::vector<HANDLE>::iterator it=m_lProcesses.begin(); it!=m_lProcesses.end(); it++) {
|
||||
TerminateProcess(*it, 0);
|
||||
}
|
||||
case WM_GETMINMAXINFO:
|
||||
{
|
||||
static const LONG minimumWindowWidth = 160;
|
||||
static const LONG minimumWindowHeight = 160;
|
||||
m_lProcesses.clear();
|
||||
|
||||
MINMAXINFO *minmaxInfo = (MINMAXINFO*)lParam;
|
||||
|
||||
minmaxInfo->ptMinTrackSize.x = minimumWindowWidth;
|
||||
minmaxInfo->ptMinTrackSize.y = minimumWindowHeight;
|
||||
|
||||
return 0;
|
||||
}
|
||||
m_cKillBtn.ShowWindow(SW_HIDE);
|
||||
m_cRunBtn.ShowWindow(SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
return super::WindowProc(message, wParam, lParam);
|
||||
}
|
||||
|
||||
void CRebuilderWindow::LayoutObjects(UINT width, UINT height)
|
||||
void CRebuilderWindow::OnSize(UINT type, int width, int height)
|
||||
{
|
||||
const int padding = m_nFontHeight/2;
|
||||
const int dblPadding = padding * 2;
|
||||
|
@ -119,8 +96,62 @@ void CRebuilderWindow::LayoutObjects(UINT width, UINT height)
|
|||
int bottomComponentStart = height - btnHeight - padding;
|
||||
int bottomComponentWidth = width - dblPadding;
|
||||
m_cRunBtn.SetWindowPos(NULL, padding, bottomComponentStart, bottomComponentWidth, btnHeight, 0);
|
||||
m_cKillBtn.SetWindowPos(NULL, padding, bottomComponentStart, bottomComponentWidth, btnHeight, 0);
|
||||
|
||||
// Center components
|
||||
int centerHeight = bottomComponentStart - topComponentEnd;
|
||||
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 <AFXWIN.H>
|
||||
#include <VECTOR>
|
||||
|
||||
class CRebuilderWindow : public CFrameWnd
|
||||
{
|
||||
public:
|
||||
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:
|
||||
void LayoutObjects(UINT width, UINT height);
|
||||
void SetGUIFonts();
|
||||
|
||||
enum {
|
||||
IDI_RUN,
|
||||
IDI_TABCTRL
|
||||
ID_RUN = 1000,
|
||||
ID_KILL,
|
||||
ID_TABCTRL
|
||||
};
|
||||
|
||||
CFont m_fDialogFont;
|
||||
CFont m_fBoldDialogFont;
|
||||
UINT m_nFontHeight;
|
||||
|
||||
CStatic m_cTopLevelTitle;
|
||||
|
@ -29,6 +37,11 @@ private:
|
|||
CTabCtrl m_cTabCtrl;
|
||||
|
||||
CButton m_cRunBtn;
|
||||
CButton m_cKillBtn;
|
||||
|
||||
std::vector<HANDLE> m_lProcesses;
|
||||
|
||||
DECLARE_MESSAGE_MAP()
|
||||
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue