please fucking work
24
.gitignore
vendored
@ -14,8 +14,6 @@ ENV/
|
|||||||
VENV/
|
VENV/
|
||||||
env.bak/
|
env.bak/
|
||||||
venv.bak/
|
venv.bak/
|
||||||
/build/
|
|
||||||
/build_debug/
|
|
||||||
/legobin/
|
/legobin/
|
||||||
*.swp
|
*.swp
|
||||||
LEGO1PROGRESS.*
|
LEGO1PROGRESS.*
|
||||||
@ -25,5 +23,23 @@ tools/ghidra_scripts/import.log
|
|||||||
|
|
||||||
# By convention we put the retail binaries into ./legobin.
|
# By convention we put the retail binaries into ./legobin.
|
||||||
# These entries are kept for now since that convention has not always been around.
|
# These entries are kept for now since that convention has not always been around.
|
||||||
ISLE.EXE
|
build/*.EXE
|
||||||
LEGO1.DLL
|
build/B_TMA.DLL
|
||||||
|
build/X10_TMA.DLL
|
||||||
|
build/*.pdb
|
||||||
|
build/*.lib
|
||||||
|
build/*.exp
|
||||||
|
setupPaths.bat
|
||||||
|
build/CMakeFiles/
|
||||||
|
build/SAV/
|
||||||
|
|
||||||
|
build/CMakeCache.txt
|
||||||
|
build/Makefile
|
||||||
|
build/cmake_install.cmake
|
||||||
|
build/tma_sources.txt
|
||||||
|
|
||||||
|
dgVoodooSetup.exe
|
||||||
|
build/Lego/
|
||||||
|
!build/Lego/Scripts
|
||||||
|
build/Lego/Scripts/*
|
||||||
|
!build/Lego/Scripts/NOCD.SI
|
||||||
|
|||||||
@ -58,6 +58,7 @@ endfunction()
|
|||||||
message(STATUS "MSVC for decompilation: ${MSVC_FOR_DECOMP}")
|
message(STATUS "MSVC for decompilation: ${MSVC_FOR_DECOMP}")
|
||||||
|
|
||||||
option(ISLE_WERROR "Treat warnings as errors" OFF)
|
option(ISLE_WERROR "Treat warnings as errors" OFF)
|
||||||
|
<<<<<<< Updated upstream
|
||||||
option(ISLE_BUILD_APP "Build ISLE.EXE application" ON)
|
option(ISLE_BUILD_APP "Build ISLE.EXE application" ON)
|
||||||
cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "NOT MINGW" OFF)
|
cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIG.EXE application" ON "NOT MINGW" OFF)
|
||||||
option(ISLE_USE_SMARTHEAP "Build LEGO1.DLL with SmartHeap" ${MSVC_FOR_DECOMP})
|
option(ISLE_USE_SMARTHEAP "Build LEGO1.DLL with SmartHeap" ${MSVC_FOR_DECOMP})
|
||||||
@ -66,6 +67,16 @@ option(ISLE_DECOMP_ASSERT "Assert struct size" ${MSVC_FOR_DECOMP})
|
|||||||
cmake_dependent_option(ISLE_USE_DX5_LIBS "Build with internal DirectX 5 SDK Libraries" ON ISLE_USE_DX5 OFF)
|
cmake_dependent_option(ISLE_USE_DX5_LIBS "Build with internal DirectX 5 SDK Libraries" ON ISLE_USE_DX5 OFF)
|
||||||
option(ISLE_BUILD_LEGO1 "Build LEGO1.DLL library" ON)
|
option(ISLE_BUILD_LEGO1 "Build LEGO1.DLL library" ON)
|
||||||
option(ISLE_BUILD_BETA10 "Build BETA10.DLL library" OFF)
|
option(ISLE_BUILD_BETA10 "Build BETA10.DLL library" OFF)
|
||||||
|
=======
|
||||||
|
option(ISLE_BUILD_APP "Build TMA_LAUNCHER.EXE application" ON)
|
||||||
|
cmake_dependent_option(ISLE_BUILD_CONFIG "Build CONFIGURE.EXE application" ON "NOT MINGW" OFF)
|
||||||
|
option(ISLE_USE_SMARTHEAP "Build B_TMA.DLL with SmartHeap" ${MSVC_FOR_DECOMP})
|
||||||
|
option(ISLE_USE_DX5 "Build with internal DirectX 5 SDK" ON)
|
||||||
|
option(ISLE_DECOMP_ASSERT "Assert struct size" ${MSVC_FOR_DECOMP})
|
||||||
|
cmake_dependent_option(ISLE_USE_DX5_LIBS "Build with internal DirectX 5 SDK Libraries" ON ISLE_USE_DX5 OFF)
|
||||||
|
option(ISLE_BUILD_LEGO1 "Build B_TMA.DLL library" ON)
|
||||||
|
option(ISLE_BUILD_BETA10 "Build X10_TMA.DLL library" OFF)
|
||||||
|
>>>>>>> Stashed changes
|
||||||
|
|
||||||
if(NOT (ISLE_BUILD_LEGO1 OR ISLE_BUILD_BETA10))
|
if(NOT (ISLE_BUILD_LEGO1 OR ISLE_BUILD_BETA10))
|
||||||
message(FATAL_ERROR "ISLE_BUILD_LEGO1 AND ISLE_BUILD_BETA10 cannot be both disabled")
|
message(FATAL_ERROR "ISLE_BUILD_LEGO1 AND ISLE_BUILD_BETA10 cannot be both disabled")
|
||||||
@ -321,9 +332,17 @@ function(add_lego_libraries NAME)
|
|||||||
LEGO1/lego/legoomni/src/paths/legoanimactor.cpp
|
LEGO1/lego/legoomni/src/paths/legoanimactor.cpp
|
||||||
LEGO1/lego/legoomni/src/entity/legoworld.cpp
|
LEGO1/lego/legoomni/src/entity/legoworld.cpp
|
||||||
LEGO1/lego/legoomni/src/build/legocarbuild.cpp
|
LEGO1/lego/legoomni/src/build/legocarbuild.cpp
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
LEGO1/lego/legoomni/src/actors/ambulance.cpp
|
||||||
LEGO1/lego/legoomni/src/common/legoplantmanager.cpp
|
LEGO1/lego/legoomni/src/common/legoplantmanager.cpp
|
||||||
LEGO1/lego/legoomni/src/actors/bumpbouy.cpp
|
LEGO1/lego/legoomni/src/actors/bumpbouy.cpp
|
||||||
LEGO1/modeldb/modeldb.cpp
|
LEGO1/modeldb/modeldb.cpp
|
||||||
|
LEGO1/lego/legoomni/src/actors/pizza.cpp
|
||||||
|
=======
|
||||||
|
LEGO1/lego/legoomni/src/common/legoplantmanager.cpp
|
||||||
|
LEGO1/lego/legoomni/src/actors/bumpbouy.cpp
|
||||||
|
LEGO1/modeldb/modeldb.cpp
|
||||||
|
>>>>>>> Stashed changes
|
||||||
LEGO1/lego/legoomni/src/actors/racecar.cpp
|
LEGO1/lego/legoomni/src/actors/racecar.cpp
|
||||||
LEGO1/lego/legoomni/src/control/legocontrolmanager.cpp
|
LEGO1/lego/legoomni/src/control/legocontrolmanager.cpp
|
||||||
LEGO1/lego/legoomni/src/audio/legosoundmanager.cpp
|
LEGO1/lego/legoomni/src/audio/legosoundmanager.cpp
|
||||||
@ -334,8 +353,11 @@ function(add_lego_libraries NAME)
|
|||||||
LEGO1/lego/legoomni/src/paths/legopathactor.cpp
|
LEGO1/lego/legoomni/src/paths/legopathactor.cpp
|
||||||
LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp
|
LEGO1/lego/legoomni/src/common/legobuildingmanager.cpp
|
||||||
LEGO1/lego/legoomni/src/worlds/isle.cpp
|
LEGO1/lego/legoomni/src/worlds/isle.cpp
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
LEGO1/lego/legoomni/src/actors/ambulance.cpp
|
LEGO1/lego/legoomni/src/actors/ambulance.cpp
|
||||||
LEGO1/lego/legoomni/src/actors/pizza.cpp
|
LEGO1/lego/legoomni/src/actors/pizza.cpp
|
||||||
|
>>>>>>> Stashed changes
|
||||||
LEGO1/lego/legoomni/src/actors/motorcycle.cpp
|
LEGO1/lego/legoomni/src/actors/motorcycle.cpp
|
||||||
LEGO1/lego/legoomni/src/actors/act3ammo.cpp
|
LEGO1/lego/legoomni/src/actors/act3ammo.cpp
|
||||||
LEGO1/lego/legoomni/src/audio/legocachesoundmanager.cpp
|
LEGO1/lego/legoomni/src/audio/legocachesoundmanager.cpp
|
||||||
@ -468,7 +490,11 @@ endif()
|
|||||||
if(ISLE_BUILD_LEGO1)
|
if(ISLE_BUILD_LEGO1)
|
||||||
add_lego_libraries(lego1
|
add_lego_libraries(lego1
|
||||||
LINK_LIBRARIES ${lego1_link_libraries}
|
LINK_LIBRARIES ${lego1_link_libraries}
|
||||||
|
<<<<<<< Updated upstream
|
||||||
DLL_OUTPUT_NAME "LEGO1"
|
DLL_OUTPUT_NAME "LEGO1"
|
||||||
|
=======
|
||||||
|
DLL_OUTPUT_NAME "B_TMA"
|
||||||
|
>>>>>>> Stashed changes
|
||||||
DLL_PREFIX ""
|
DLL_PREFIX ""
|
||||||
DLL_SUFFIX ".DLL"
|
DLL_SUFFIX ".DLL"
|
||||||
OUT_TARGETS lego1_targets
|
OUT_TARGETS lego1_targets
|
||||||
@ -479,7 +505,11 @@ endif()
|
|||||||
if(ISLE_BUILD_BETA10)
|
if(ISLE_BUILD_BETA10)
|
||||||
add_lego_libraries(beta10
|
add_lego_libraries(beta10
|
||||||
SUFFIX "-beta10"
|
SUFFIX "-beta10"
|
||||||
|
<<<<<<< Updated upstream
|
||||||
DLL_OUTPUT_NAME "BETA10"
|
DLL_OUTPUT_NAME "BETA10"
|
||||||
|
=======
|
||||||
|
DLL_OUTPUT_NAME "X10_TMA"
|
||||||
|
>>>>>>> Stashed changes
|
||||||
DLL_PREFIX ""
|
DLL_PREFIX ""
|
||||||
DLL_SUFFIX ".DLL"
|
DLL_SUFFIX ".DLL"
|
||||||
OUT_TARGETS beta10_targets
|
OUT_TARGETS beta10_targets
|
||||||
@ -513,7 +543,11 @@ if (ISLE_BUILD_APP)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Make sure filenames are ALL CAPS
|
# Make sure filenames are ALL CAPS
|
||||||
|
<<<<<<< Updated upstream
|
||||||
set_property(TARGET isle PROPERTY OUTPUT_NAME ISLE)
|
set_property(TARGET isle PROPERTY OUTPUT_NAME ISLE)
|
||||||
|
=======
|
||||||
|
set_property(TARGET isle PROPERTY OUTPUT_NAME TMA_LAUNCHER)
|
||||||
|
>>>>>>> Stashed changes
|
||||||
set_property(TARGET isle PROPERTY SUFFIX ".EXE")
|
set_property(TARGET isle PROPERTY SUFFIX ".EXE")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -537,7 +571,11 @@ if (ISLE_BUILD_CONFIG)
|
|||||||
endif()
|
endif()
|
||||||
target_compile_definitions(config PRIVATE DIRECT3D_VERSION=0x500)
|
target_compile_definitions(config PRIVATE DIRECT3D_VERSION=0x500)
|
||||||
target_link_libraries(config PRIVATE ddraw dxguid)
|
target_link_libraries(config PRIVATE ddraw dxguid)
|
||||||
|
<<<<<<< Updated upstream
|
||||||
set_property(TARGET config PROPERTY OUTPUT_NAME "CONFIG")
|
set_property(TARGET config PROPERTY OUTPUT_NAME "CONFIG")
|
||||||
|
=======
|
||||||
|
set_property(TARGET config PROPERTY OUTPUT_NAME "CONFIGURE")
|
||||||
|
>>>>>>> Stashed changes
|
||||||
set_property(TARGET config PROPERTY SUFFIX ".EXE")
|
set_property(TARGET config PROPERTY SUFFIX ".EXE")
|
||||||
set_property(TARGET config PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded$<$<CONFIG:Debug>:Debug>DLL)
|
set_property(TARGET config PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded$<$<CONFIG:Debug>:Debug>DLL)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@ -32,6 +32,7 @@ ON_COMMAND(IDC_RAD_PALETTE_256, OnRadiobuttonPalette256)
|
|||||||
ON_COMMAND(IDC_CHK_3D_VIDEO_MEMORY, OnCheckbox3DVideoMemory)
|
ON_COMMAND(IDC_CHK_3D_VIDEO_MEMORY, OnCheckbox3DVideoMemory)
|
||||||
ON_WM_DESTROY() // FIXME: CONFIG.EXE calls Default
|
ON_WM_DESTROY() // FIXME: CONFIG.EXE calls Default
|
||||||
ON_COMMAND(IDABORT, OnButtonCancel)
|
ON_COMMAND(IDABORT, OnButtonCancel)
|
||||||
|
ON_COMMAND(IDAPPLY, OnApply)
|
||||||
ON_COMMAND(IDC_CHK_3DSOUND, OnCheckbox3DSound)
|
ON_COMMAND(IDC_CHK_3DSOUND, OnCheckbox3DSound)
|
||||||
ON_COMMAND(IDC_RAD_MODEL_QUALITY_LOW, OnRadiobuttonModelLowQuality)
|
ON_COMMAND(IDC_RAD_MODEL_QUALITY_LOW, OnRadiobuttonModelLowQuality)
|
||||||
ON_COMMAND(IDC_RAD_MODEL_QUALITY_HIGH, OnRadiobuttonModelHighQuality)
|
ON_COMMAND(IDC_RAD_MODEL_QUALITY_HIGH, OnRadiobuttonModelHighQuality)
|
||||||
@ -41,6 +42,7 @@ ON_COMMAND(IDC_CHK_JOYSTICK, OnCheckboxJoystick)
|
|||||||
ON_COMMAND(IDC_BTN_ADVANCED, OnButtonAdvanced)
|
ON_COMMAND(IDC_BTN_ADVANCED, OnButtonAdvanced)
|
||||||
ON_COMMAND(IDC_CHK_DRAW_CURSOR, OnCheckboxDrawCursor)
|
ON_COMMAND(IDC_CHK_DRAW_CURSOR, OnCheckboxDrawCursor)
|
||||||
ON_COMMAND(IDC_CHK_MUSIC, OnCheckboxMusic)
|
ON_COMMAND(IDC_CHK_MUSIC, OnCheckboxMusic)
|
||||||
|
ON_COMMAND(IDC_CHK_FULLSCREEN, OnCheckboxFullscreen)
|
||||||
END_MESSAGE_MAP()
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
// FUNCTION: CONFIG 0x00403e80
|
// FUNCTION: CONFIG 0x00403e80
|
||||||
@ -168,6 +170,14 @@ void CMainDialog::OnButtonCancel()
|
|||||||
OnCancel();
|
OnCancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CMainDialog::OnApply()
|
||||||
|
{
|
||||||
|
if (m_modified) {
|
||||||
|
currentConfigApp->WriteRegisterSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// FUNCTION: CONFIG 0x00404360
|
// FUNCTION: CONFIG 0x00404360
|
||||||
void CMainDialog::UpdateInterface()
|
void CMainDialog::UpdateInterface()
|
||||||
{
|
{
|
||||||
@ -218,6 +228,7 @@ void CMainDialog::UpdateInterface()
|
|||||||
);
|
);
|
||||||
CheckDlgButton(IDC_CHK_JOYSTICK, currentConfigApp->m_use_joystick);
|
CheckDlgButton(IDC_CHK_JOYSTICK, currentConfigApp->m_use_joystick);
|
||||||
CheckDlgButton(IDC_CHK_MUSIC, currentConfigApp->m_music);
|
CheckDlgButton(IDC_CHK_MUSIC, currentConfigApp->m_music);
|
||||||
|
CheckDlgButton(IDC_CHK_FULLSCREEN, currentConfigApp->m_full_screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FUNCTION: CONFIG 0x004045e0
|
// FUNCTION: CONFIG 0x004045e0
|
||||||
@ -344,3 +355,10 @@ void CMainDialog::OnCheckboxMusic()
|
|||||||
m_modified = TRUE;
|
m_modified = TRUE;
|
||||||
UpdateInterface();
|
UpdateInterface();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CMainDialog::OnCheckboxFullscreen()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_full_screen = IsDlgButtonChecked(IDC_CHK_FULLSCREEN);
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
@ -35,6 +35,7 @@ class CMainDialog : public CDialog {
|
|||||||
void OnCancel();
|
void OnCancel();
|
||||||
void OnDestroy();
|
void OnDestroy();
|
||||||
void OnButtonCancel();
|
void OnButtonCancel();
|
||||||
|
void OnApply();
|
||||||
void OnCheckbox3DSound();
|
void OnCheckbox3DSound();
|
||||||
void OnCheckbox3DVideoMemory();
|
void OnCheckbox3DVideoMemory();
|
||||||
void OnRadiobuttonPalette16bit();
|
void OnRadiobuttonPalette16bit();
|
||||||
@ -48,6 +49,7 @@ class CMainDialog : public CDialog {
|
|||||||
void OnButtonAdvanced();
|
void OnButtonAdvanced();
|
||||||
void OnCheckboxDrawCursor();
|
void OnCheckboxDrawCursor();
|
||||||
void OnCheckboxMusic();
|
void OnCheckboxMusic();
|
||||||
|
void OnCheckboxFullscreen();
|
||||||
|
|
||||||
DECLARE_MESSAGE_MAP()
|
DECLARE_MESSAGE_MAP()
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,12 +29,17 @@ CConfigApp::CConfigApp()
|
|||||||
BOOL CConfigApp::InitInstance()
|
BOOL CConfigApp::InitInstance()
|
||||||
{
|
{
|
||||||
if (!IsLegoNotRunning()) {
|
if (!IsLegoNotRunning()) {
|
||||||
|
AfxMessageBox(
|
||||||
|
"\"LEGO Island: The Modder's Arrival\" configuration requests that you exit \"LEGO Island: The Modder's Arrival\" "
|
||||||
|
"before continuing. This ensures that no unwanted behavior occurs by preventing you from changing configurations "
|
||||||
|
"while the game is running."
|
||||||
|
);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
if (!DetectDirectX5()) {
|
if (!DetectDirectX5()) {
|
||||||
AfxMessageBox(
|
AfxMessageBox(
|
||||||
"\"LEGO\xae Island\" is not detecting DirectX 5 or later. Please quit all other applications and try "
|
"\"LEGO Island: The Modder's Arrival\" configuration failed to detect an installation of DirectX 5. Please ensure any possibly"
|
||||||
"again."
|
"interfering applications have been terminated before re-launching \"LEGO Island: The Modder's Arrival\"."
|
||||||
);
|
);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@ -45,7 +50,7 @@ BOOL CConfigApp::InitInstance()
|
|||||||
#endif
|
#endif
|
||||||
CConfigCommandLineInfo cmdInfo;
|
CConfigCommandLineInfo cmdInfo;
|
||||||
ParseCommandLine(cmdInfo);
|
ParseCommandLine(cmdInfo);
|
||||||
if (_stricmp(afxCurrentAppName, "config") == 0) {
|
if (_stricmp(afxCurrentAppName, "configure") == 0) {
|
||||||
m_run_config_dialog = TRUE;
|
m_run_config_dialog = TRUE;
|
||||||
}
|
}
|
||||||
m_device_enumerator = new LegoDeviceEnumerate;
|
m_device_enumerator = new LegoDeviceEnumerate;
|
||||||
@ -90,7 +95,7 @@ BOOL CConfigApp::InitInstance()
|
|||||||
m_device = NULL;
|
m_device = NULL;
|
||||||
char password[256];
|
char password[256];
|
||||||
ReadReg("password", password, sizeof(password));
|
ReadReg("password", password, sizeof(password));
|
||||||
const char* exe = _stricmp("ogel", password) == 0 ? "isled.exe" : "isle.exe";
|
const char* exe = _stricmp("ogel", password) == 0 ? "isled.exe" : "isle_tma.exe";
|
||||||
char diskpath[1024];
|
char diskpath[1024];
|
||||||
if (ReadReg("diskpath", diskpath, sizeof(diskpath))) {
|
if (ReadReg("diskpath", diskpath, sizeof(diskpath))) {
|
||||||
_chdir(diskpath);
|
_chdir(diskpath);
|
||||||
@ -106,8 +111,17 @@ BOOL CConfigApp::InitInstance()
|
|||||||
// FUNCTION: CONFIG 0x00403100
|
// FUNCTION: CONFIG 0x00403100
|
||||||
BOOL CConfigApp::IsLegoNotRunning()
|
BOOL CConfigApp::IsLegoNotRunning()
|
||||||
{
|
{
|
||||||
|
//hwnd is legacy, hwnd2 is the handle used by TMA
|
||||||
HWND hWnd = FindWindowA("Lego Island MainNoM App", "LEGO\xae");
|
HWND hWnd = FindWindowA("Lego Island MainNoM App", "LEGO\xae");
|
||||||
if (_stricmp(afxCurrentAppName, "config") == 0 || !hWnd) {
|
HWND hWnd2 = FindWindowA("TMAOMNI PrimaryRuntimeWrapper", "LEGO Island: The Modder's Arrival");
|
||||||
|
// This only part of this code that works is checking whether the app filename is configure or not.
|
||||||
|
// Why does it even check for this.
|
||||||
|
// And the only part that should work doesn't. Why? Has it ever?
|
||||||
|
|
||||||
|
// Hello me from 30 minutes ago - this was really fucking simple to fix. Are you stupid?
|
||||||
|
|
||||||
|
//if (_stricmp(afxCurrentAppName, "configure") == 0 && hWnd && hWnd2) {
|
||||||
|
if (!hWnd && !hWnd2) {
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
if (SetForegroundWindow(hWnd)) {
|
if (SetForegroundWindow(hWnd)) {
|
||||||
@ -124,7 +138,7 @@ BOOL CConfigApp::WriteReg(const char* p_key, const char* p_value) const
|
|||||||
|
|
||||||
if (RegCreateKeyExA(
|
if (RegCreateKeyExA(
|
||||||
HKEY_LOCAL_MACHINE,
|
HKEY_LOCAL_MACHINE,
|
||||||
"SOFTWARE\\Mindscape\\LEGO Island",
|
"SOFTWARE\\ActionSoft\\LEGO Island TMA",
|
||||||
0,
|
0,
|
||||||
"string",
|
"string",
|
||||||
0,
|
0,
|
||||||
@ -153,7 +167,7 @@ BOOL CConfigApp::ReadReg(LPCSTR p_key, LPCSTR p_value, DWORD p_size) const
|
|||||||
|
|
||||||
BOOL out = FALSE;
|
BOOL out = FALSE;
|
||||||
DWORD size = p_size;
|
DWORD size = p_size;
|
||||||
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Mindscape\\LEGO Island", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\ActionSoft\\LEGO Island TMA", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
||||||
if (RegQueryValueExA(hKey, p_key, NULL, &valueType, (LPBYTE) p_value, &size) == ERROR_SUCCESS) {
|
if (RegQueryValueExA(hKey, p_key, NULL, &valueType, (LPBYTE) p_value, &size) == ERROR_SUCCESS) {
|
||||||
if (RegCloseKey(hKey) == ERROR_SUCCESS) {
|
if (RegCloseKey(hKey) == ERROR_SUCCESS) {
|
||||||
out = TRUE;
|
out = TRUE;
|
||||||
|
|||||||
BIN
CONFIG/res/CONFIG.ico
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
CONFIG/res/config.aps
Normal file
@ -1,82 +1,216 @@
|
|||||||
|
//Microsoft Developer Studio generated resource script.
|
||||||
|
//
|
||||||
|
#include "resrc1.h"
|
||||||
|
|
||||||
|
#define APSTUDIO_READONLY_SYMBOLS
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Generated from the TEXTINCLUDE 2 resource.
|
||||||
|
//
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include "afxres.h"
|
#include "afxres.h"
|
||||||
|
|
||||||
IDI_CONFIG ICON "lego.ico"
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
#undef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
|
||||||
IDB_SHARK BITMAP "shark.bmp"
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// English (U.S.) resources
|
||||||
|
|
||||||
IDD_ABOUT DIALOG 0, 0, 217, 55
|
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||||
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
#ifdef _WIN32
|
||||||
CAPTION "About Configure LEGO\xAE Island"
|
|
||||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
|
#pragma code_page(1252)
|
||||||
|
#endif //_WIN32
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Icon
|
||||||
|
//
|
||||||
|
|
||||||
|
// Icon with lowest ID value placed first to ensure application icon
|
||||||
|
// remains consistent on all systems.
|
||||||
|
IDI_CONFIG ICON DISCARDABLE "CONFIG.ico"
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Bitmap
|
||||||
|
//
|
||||||
|
|
||||||
|
IDB_SHARK BITMAP MOVEABLE PURE "shark.bmp"
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Dialog
|
||||||
|
//
|
||||||
|
|
||||||
|
IDD_ABOUT DIALOG DISCARDABLE 0, 0, 217, 55
|
||||||
|
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||||
|
CAPTION "About Configuration Application"
|
||||||
FONT 8, "MS Sans Serif"
|
FONT 8, "MS Sans Serif"
|
||||||
BEGIN
|
BEGIN
|
||||||
CONTROL IDI_CONFIG, -1, "STATIC", SS_ICON | WS_CHILD | WS_VISIBLE, 11, 17, 18, 20
|
ICON IDI_CONFIG,-1,11,17,18,20
|
||||||
CONTROL "Configure LEGO Island Version 1.0", -1, "STATIC", SS_LEFT | SS_NOPREFIX | WS_CHILD | WS_VISIBLE | WS_GROUP, 40, 10, 119, 8
|
LTEXT "Configuration settings for ",-1,40,10,119,8,SS_NOPREFIX
|
||||||
CONTROL "Copyright \xA9 1997 mindscape", -1, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 40, 25, 119, 8
|
LTEXT "LEGO Island: The Modder's Arrival",-1,40,20,119,8,
|
||||||
CONTROL "OK", IDOK, "BUTTON", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 178, 7, 32, 14
|
SS_NOPREFIX
|
||||||
|
LTEXT "Copyright <20> 2025 i guess",-1,40,30,119,8
|
||||||
|
DEFPUSHBUTTON "OK",IDOK,178,7,32,14,WS_GROUP
|
||||||
END
|
END
|
||||||
|
|
||||||
STRINGTABLE
|
IDD_MAIN DIALOGEX 0, 0, 289, 250
|
||||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION |
|
||||||
BEGIN
|
WS_SYSMENU
|
||||||
IDS_ABOUT, "&About Config..."
|
|
||||||
END
|
|
||||||
|
|
||||||
IDD_MAIN DIALOGEX 0, 0, 289, 247
|
|
||||||
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
|
|
||||||
EXSTYLE WS_EX_APPWINDOW
|
EXSTYLE WS_EX_APPWINDOW
|
||||||
CAPTION "Configure LEGO Island"
|
CAPTION "Configuration for LEGO Island: The Modder's Arrival"
|
||||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
|
||||||
FONT 8, "MS Sans Serif"
|
FONT 8, "MS Sans Serif"
|
||||||
BEGIN
|
BEGIN
|
||||||
CONTROL "OK", IDABORT, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 172, 125, 44, 14
|
PUSHBUTTON "Save && Exit",IDABORT,194,123,44,14
|
||||||
CONTROL "Cancel", IDCANCEL, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 238, 125, 44, 14
|
PUSHBUTTON "Cancel",IDCANCEL,238,123,44,14
|
||||||
CONTROL "Fast", IDC_RAD_MODEL_QUALITY_LOW, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 124, 18, 42, 10
|
CONTROL "Fast",IDC_RAD_MODEL_QUALITY_LOW,"Button",
|
||||||
CONTROL "High", IDC_RAD_MODEL_QUALITY_HIGH, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 192, 18, 69, 10
|
BS_AUTORADIOBUTTON,118,18,42,10
|
||||||
CONTROL "Fast", IDC_RAD_TEXTURE_QUALITY_LOW, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 124, 49, 43, 10
|
CONTROL "High",IDC_RAD_MODEL_QUALITY_HIGH,"Button",
|
||||||
CONTROL "High", IDC_RAD_TEXTURE_QUALITY_HIGH, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 192, 49, 69, 10
|
BS_AUTORADIOBUTTON,192,18,69,10
|
||||||
CONTROL "256 Color", IDC_RAD_PALETTE_256, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 124, 80, 50, 10
|
CONTROL "Fast",IDC_RAD_TEXTURE_QUALITY_LOW,"Button",
|
||||||
CONTROL "High Color(16 bit)", IDC_RAD_PALETTE_16BIT, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 192, 80, 76, 10
|
BS_AUTORADIOBUTTON,118,49,43,10
|
||||||
CONTROL "Use Joystick", IDC_CHK_JOYSTICK, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 192, 104, 60, 10
|
CONTROL "High",IDC_RAD_TEXTURE_QUALITY_HIGH,"Button",
|
||||||
CONTROL "", IDC_LIST_3DDEVICES, "LISTBOX", LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL, 115, 168, 161, 36 , WS_EX_TRANSPARENT
|
BS_AUTORADIOBUTTON,192,49,69,10
|
||||||
CONTROL "Flip Video Memory Pages", IDC_CHK_FLIP_VIDEO_MEM_PAGES, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, 116, 224, 96, 15
|
CONTROL "8-bit (256) Colour",IDC_RAD_PALETTE_256,"Button",
|
||||||
CONTROL "Draw 3D to Video Memory", IDC_CHK_3D_VIDEO_MEMORY, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, 116, 210, 99, 10
|
BS_AUTORADIOBUTTON,118,80,69,10
|
||||||
CONTROL "Color Palette", -1, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 107, 69, 175, 26
|
CONTROL "16-bit (65536) Colour",IDC_RAD_PALETTE_16BIT,"Button",
|
||||||
CONTROL "Direct 3D Devices", -1, "STATIC", SS_CENTER | WS_CHILD | WS_VISIBLE | WS_GROUP, 123, 158, 143, 10
|
BS_AUTORADIOBUTTON,192,80,80,10
|
||||||
CONTROL "3D Sound", IDC_CHK_3DSOUND, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, 220, 210, 45, 10
|
CONTROL "Use Joystick",IDC_CHK_JOYSTICK,"Button",BS_AUTOCHECKBOX |
|
||||||
CONTROL "Island Model Quality", -1, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 107, 7, 175, 26
|
WS_TABSTOP,219,104,54,10
|
||||||
CONTROL "Island Texture Quality", -1, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 107, 38, 175, 26
|
LISTBOX IDC_LIST_3DDEVICES,115,168,161,36,LBS_NOINTEGRALHEIGHT |
|
||||||
CONTROL "Advanced Settings", IDC_GRP_ADVANCED, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 107, 147, 175, 93
|
WS_VSCROLL,WS_EX_TRANSPARENT
|
||||||
CONTROL "Advanced", IDC_BTN_ADVANCED, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 109, 124, 44, 14
|
CONTROL "Flip Video Memory Pages",IDC_CHK_FLIP_VIDEO_MEM_PAGES,
|
||||||
CONTROL IDB_SHARK, IDC_BMP_SHARK, "STATIC", SS_BITMAP | WS_CHILD | WS_VISIBLE, 7, 7, 83, 249
|
"Button",BS_AUTOCHECKBOX,116,224,96,15
|
||||||
CONTROL "Draw Cursor", IDC_CHK_DRAW_CURSOR, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, 219, 227, 54, 10
|
CONTROL "Draw 3D to Video Memory",IDC_CHK_3D_VIDEO_MEMORY,
|
||||||
CONTROL "Music", IDC_CHK_MUSIC, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 124, 104, 35, 10
|
"Button",BS_AUTOCHECKBOX,116,210,99,10
|
||||||
|
GROUPBOX "Color Palette",-1,107,69,175,26
|
||||||
|
CTEXT "Direct 3D Devices",-1,123,158,143,10
|
||||||
|
CONTROL "3D Sound",IDC_CHK_3DSOUND,"Button",BS_AUTOCHECKBOX,220,
|
||||||
|
210,45,10
|
||||||
|
GROUPBOX "Island Model Quality",-1,107,7,175,26
|
||||||
|
GROUPBOX "Island Texture Quality",-1,107,38,175,26
|
||||||
|
GROUPBOX "Advanced Settings",IDC_GRP_ADVANCED,107,147,175,93
|
||||||
|
PUSHBUTTON "Advanced",IDC_BTN_ADVANCED,107,123,43,14,NOT WS_TABSTOP
|
||||||
|
CONTROL 135,IDC_BMP_SHARK,"Static",SS_BITMAP,7,0,83,249
|
||||||
|
CONTROL "Draw Cursor",IDC_CHK_DRAW_CURSOR,"Button",
|
||||||
|
BS_AUTOCHECKBOX,219,227,54,10
|
||||||
|
CONTROL "Music",IDC_CHK_MUSIC,"Button",BS_AUTOCHECKBOX |
|
||||||
|
WS_TABSTOP,114,104,35,10
|
||||||
|
CONTROL "Fullscreen",IDC_CHK_FULLSCREEN,"Button",BS_AUTOCHECKBOX |
|
||||||
|
WS_TABSTOP,166,104,45,10
|
||||||
|
PUSHBUTTON "Save",IDAPPLY,150,123,44,14
|
||||||
END
|
END
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _MAC
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Version
|
||||||
|
//
|
||||||
|
|
||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION 1,1,0,0
|
FILEVERSION 1,0,0,0
|
||||||
PRODUCTVERSION 1,1,0,0
|
PRODUCTVERSION 1,0,0,0
|
||||||
FILEOS 0x4
|
FILEFLAGSMASK 0x0L
|
||||||
FILETYPE 0x1
|
#ifdef _DEBUG
|
||||||
|
FILEFLAGS 0x1L
|
||||||
|
#else
|
||||||
|
FILEFLAGS 0x0L
|
||||||
|
#endif
|
||||||
|
FILEOS 0x4L
|
||||||
|
FILETYPE 0x1L
|
||||||
|
FILESUBTYPE 0x0L
|
||||||
BEGIN
|
BEGIN
|
||||||
BLOCK "StringFileInfo"
|
BLOCK "StringFileInfo"
|
||||||
BEGIN
|
BEGIN
|
||||||
BLOCK "040904b0"
|
BLOCK "040904b0"
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "Mindscape, Inc."
|
VALUE "CompanyName", "FUNNIMAN.EXE"
|
||||||
VALUE "FileDescription", "LEGOIsland & Configuration application"
|
VALUE "FileDescription", "b_TMAOmni Library Configurator"
|
||||||
VALUE "FileVersion", "1, 1, 0, 0"
|
VALUE "FileVersion", "1, 0, 0, 0"
|
||||||
VALUE "InternalName", "LEGOISLE.EXE"
|
VALUE "InternalName", "b_TMAOmni Library Configurator"
|
||||||
VALUE "LegalCopyright", "Copyright \xA9 1997"
|
VALUE "LegalCopyright", "Copyright \xA9 2025"
|
||||||
VALUE "OriginalFilename", "CONFIG.EXE"
|
VALUE "OriginalFilename", "CONFIGURE.EXE"
|
||||||
VALUE "ProductName", "LEGO Island"
|
VALUE "ProductName", "LEGO Island: The Modder's Arrival"
|
||||||
VALUE "ProductVersion", "1, 1, 0, 0"
|
VALUE "ProductVersion", "1, 0, 0, 0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "Translation", 0x0409, 0x04B0 // U.S. English, Unicode
|
VALUE "Translation", 0x409, 1200
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
|
||||||
|
#endif // !_MAC
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// TEXTINCLUDE
|
||||||
|
//
|
||||||
|
|
||||||
|
1 TEXTINCLUDE DISCARDABLE
|
||||||
|
BEGIN
|
||||||
|
"resrc1.h\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
2 TEXTINCLUDE DISCARDABLE
|
||||||
|
BEGIN
|
||||||
|
"#include ""resource.h""\r\n"
|
||||||
|
"#include ""afxres.h""\r\n"
|
||||||
|
"\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
3 TEXTINCLUDE DISCARDABLE
|
||||||
|
BEGIN
|
||||||
|
"\r\n"
|
||||||
|
"\0"
|
||||||
|
END
|
||||||
|
|
||||||
|
#endif // APSTUDIO_INVOKED
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// DESIGNINFO
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
GUIDELINES DESIGNINFO DISCARDABLE
|
||||||
|
BEGIN
|
||||||
|
IDD_MAIN, DIALOG
|
||||||
|
BEGIN
|
||||||
|
BOTTOMMARGIN, 249
|
||||||
|
END
|
||||||
|
END
|
||||||
|
#endif // APSTUDIO_INVOKED
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// String Table
|
||||||
|
//
|
||||||
|
|
||||||
|
STRINGTABLE DISCARDABLE
|
||||||
|
BEGIN
|
||||||
|
IDS_ABOUT "&About Config..."
|
||||||
|
END
|
||||||
|
|
||||||
|
#endif // English (U.S.) resources
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef APSTUDIO_INVOKED
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Generated from the TEXTINCLUDE 3 resource.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
#endif // not APSTUDIO_INVOKED
|
||||||
|
|
||||||
|
|||||||
@ -7,10 +7,12 @@
|
|||||||
|
|
||||||
#define IDC_LIST_3DDEVICES 1000
|
#define IDC_LIST_3DDEVICES 1000
|
||||||
#define IDC_CHK_FLIP_VIDEO_MEM_PAGES 1001
|
#define IDC_CHK_FLIP_VIDEO_MEM_PAGES 1001
|
||||||
|
#define IDC_CHK_FULLSCREEN 1002
|
||||||
#define IDC_CHK_3D_VIDEO_MEMORY 1003
|
#define IDC_CHK_3D_VIDEO_MEMORY 1003
|
||||||
#define IDC_RAD_PALETTE_256 1004
|
#define IDC_RAD_PALETTE_256 1004
|
||||||
#define IDC_RAD_PALETTE_16BIT 1005
|
#define IDC_RAD_PALETTE_16BIT 1005
|
||||||
#define IDC_CHK_3DSOUND 1006
|
#define IDC_CHK_3DSOUND 1006
|
||||||
|
#define IDAPPLY 1007
|
||||||
#define IDC_CHK_DRAW_CURSOR 1008
|
#define IDC_CHK_DRAW_CURSOR 1008
|
||||||
#define IDC_RAD_MODEL_QUALITY_LOW 1010
|
#define IDC_RAD_MODEL_QUALITY_LOW 1010
|
||||||
#define IDC_RAD_MODEL_QUALITY_HIGH 1011
|
#define IDC_RAD_MODEL_QUALITY_HIGH 1011
|
||||||
|
|||||||
16
CONFIG/res/resrc1.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//{{NO_DEPENDENCIES}}
|
||||||
|
// Microsoft Developer Studio generated include file.
|
||||||
|
// Used by config.rc
|
||||||
|
//
|
||||||
|
#define IDAPPLY 4
|
||||||
|
|
||||||
|
// Next default values for new objects
|
||||||
|
//
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
#define _APS_NEXT_RESOURCE_VALUE 101
|
||||||
|
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||||
|
#define _APS_NEXT_CONTROL_VALUE 1000
|
||||||
|
#define _APS_NEXT_SYMED_VALUE 101
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
19
CONFIG_orig/AboutDlg.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "AboutDlg.h"
|
||||||
|
|
||||||
|
#include "decomp.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(CDialog, 0x60)
|
||||||
|
DECOMP_SIZE_ASSERT(CAboutDialog, 0x60)
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403c20
|
||||||
|
CAboutDialog::CAboutDialog() : CDialog(IDD)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403d20
|
||||||
|
void CAboutDialog::DoDataExchange(CDataExchange* pDX)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN_MESSAGE_MAP(CAboutDialog, CDialog)
|
||||||
|
END_MESSAGE_MAP()
|
||||||
39
CONFIG_orig/AboutDlg.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#if !defined(AFX_ABOUTDLG_H)
|
||||||
|
#define AFX_ABOUTDLG_H
|
||||||
|
|
||||||
|
#include "StdAfx.h"
|
||||||
|
#include "compat.h"
|
||||||
|
#include "res/resource.h"
|
||||||
|
|
||||||
|
// VTABLE: CONFIG 0x00406308
|
||||||
|
// SIZE 0x60
|
||||||
|
class CAboutDialog : public CDialog {
|
||||||
|
public:
|
||||||
|
CAboutDialog();
|
||||||
|
enum {
|
||||||
|
IDD = IDD_ABOUT
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void DoDataExchange(CDataExchange* pDX) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
DECLARE_MESSAGE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
// SYNTHETIC: CONFIG 0x00403cb0
|
||||||
|
// CAboutDialog::`scalar deleting destructor'
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403d30
|
||||||
|
// CAboutDialog::_GetBaseMessageMap
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403d40
|
||||||
|
// CAboutDialog::GetMessageMap
|
||||||
|
|
||||||
|
// GLOBAL: CONFIG 0x00406100
|
||||||
|
// CAboutDialog::messageMap
|
||||||
|
|
||||||
|
// GLOBAL: CONFIG 0x00406108
|
||||||
|
// CAboutDialog::_messageEntries
|
||||||
|
|
||||||
|
#endif // !defined(AFX_ABOUTDLG_H)
|
||||||
22
CONFIG_orig/ConfigCommandLineInfo.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include "ConfigCommandLineInfo.h"
|
||||||
|
|
||||||
|
#include "decomp.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(CCommandLineInfo, 0x24)
|
||||||
|
DECOMP_SIZE_ASSERT(CConfigCommandLineInfo, 0x24)
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403b10
|
||||||
|
CConfigCommandLineInfo::CConfigCommandLineInfo()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_run_config_dialog = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403bf0
|
||||||
|
void CConfigCommandLineInfo::ParseParam(LPCSTR pszParam, BOOL bFlag, BOOL bLast)
|
||||||
|
{
|
||||||
|
if (bFlag) {
|
||||||
|
if (lstrcmpiA(pszParam, "config") == 0) {
|
||||||
|
currentConfigApp->m_run_config_dialog = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
CONFIG_orig/ConfigCommandLineInfo.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#if !defined(AFX_CONFIGCOMMANDLINEINFO_H)
|
||||||
|
#define AFX_CONFIGCOMMANDLINEINFO_H
|
||||||
|
|
||||||
|
#include "StdAfx.h"
|
||||||
|
#include "compat.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "decomp.h"
|
||||||
|
|
||||||
|
// VTABLE: CONFIG 0x004060e8
|
||||||
|
// SIZE 0x24
|
||||||
|
class CConfigCommandLineInfo : public CCommandLineInfo {
|
||||||
|
public:
|
||||||
|
CConfigCommandLineInfo();
|
||||||
|
|
||||||
|
void ParseParam(LPCSTR pszParam, BOOL bFlag, BOOL bLast) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// SYNTHETIC: CONFIG 0x00403b80
|
||||||
|
// CConfigCommandLineInfo::`scalar deleting destructor'
|
||||||
|
|
||||||
|
#endif // !defined(AFX_CONFIGCOMMANDLINEINFO_H)
|
||||||
346
CONFIG_orig/MainDlg.cpp
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
#include "MainDlg.h"
|
||||||
|
|
||||||
|
#include "AboutDlg.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "res/resource.h"
|
||||||
|
|
||||||
|
#include <mxdirectx/legodxinfo.h>
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(CDialog, 0x60)
|
||||||
|
DECOMP_SIZE_ASSERT(CMainDialog, 0x70)
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403d50
|
||||||
|
CMainDialog::CMainDialog(CWnd* pParent) : CDialog(IDD, pParent)
|
||||||
|
{
|
||||||
|
afxCurrentWinApp;
|
||||||
|
m_icon = LoadIconA(AfxFindResourceHandle(MAKEINTRESOURCE(IDI_CONFIG), RT_GROUP_ICON), MAKEINTRESOURCE(IDI_CONFIG));
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403e50
|
||||||
|
void CMainDialog::DoDataExchange(CDataExchange* pDX)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN_MESSAGE_MAP(CMainDialog, CDialog)
|
||||||
|
ON_WM_SYSCOMMAND()
|
||||||
|
ON_WM_PAINT()
|
||||||
|
ON_WM_QUERYDRAGICON()
|
||||||
|
ON_COMMAND(IDC_CHK_FLIP_VIDEO_MEM_PAGES, OnCheckboxFlipVideoMemPages)
|
||||||
|
ON_LBN_SELCHANGE(IDC_LIST_3DDEVICES, OnList3DevicesSelectionChanged)
|
||||||
|
ON_COMMAND(IDC_RAD_PALETTE_16BIT, OnRadiobuttonPalette16bit)
|
||||||
|
ON_COMMAND(IDC_RAD_PALETTE_256, OnRadiobuttonPalette256)
|
||||||
|
ON_COMMAND(IDC_CHK_3D_VIDEO_MEMORY, OnCheckbox3DVideoMemory)
|
||||||
|
ON_WM_DESTROY() // FIXME: CONFIG.EXE calls Default
|
||||||
|
ON_COMMAND(IDABORT, OnButtonCancel)
|
||||||
|
ON_COMMAND(IDC_CHK_3DSOUND, OnCheckbox3DSound)
|
||||||
|
ON_COMMAND(IDC_RAD_MODEL_QUALITY_LOW, OnRadiobuttonModelLowQuality)
|
||||||
|
ON_COMMAND(IDC_RAD_MODEL_QUALITY_HIGH, OnRadiobuttonModelHighQuality)
|
||||||
|
ON_COMMAND(IDC_RAD_TEXTURE_QUALITY_LOW, OnRadiobuttonTextureLowQuality)
|
||||||
|
ON_COMMAND(IDC_RAD_TEXTURE_QUALITY_HIGH, OnRadiobuttonTextureHighQuality)
|
||||||
|
ON_COMMAND(IDC_CHK_JOYSTICK, OnCheckboxJoystick)
|
||||||
|
ON_COMMAND(IDC_BTN_ADVANCED, OnButtonAdvanced)
|
||||||
|
ON_COMMAND(IDC_CHK_DRAW_CURSOR, OnCheckboxDrawCursor)
|
||||||
|
ON_COMMAND(IDC_CHK_MUSIC, OnCheckboxMusic)
|
||||||
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403e80
|
||||||
|
BOOL CMainDialog::OnInitDialog()
|
||||||
|
{
|
||||||
|
CDialog::OnInitDialog();
|
||||||
|
SwitchToAdvanced(FALSE);
|
||||||
|
CMenu* system_menu = CMenu::FromHandle(::GetSystemMenu(m_hWnd, FALSE));
|
||||||
|
CString about_text;
|
||||||
|
about_text.LoadString(IDS_ABOUT);
|
||||||
|
if (system_menu) {
|
||||||
|
AppendMenuA(system_menu->m_hMenu, MF_SEPARATOR, 0, NULL);
|
||||||
|
AppendMenuA(system_menu->m_hMenu, MF_STRING, 16, (LPCTSTR) about_text);
|
||||||
|
}
|
||||||
|
SendMessage(WM_SETICON, ICON_BIG, (LPARAM) m_icon);
|
||||||
|
SendMessage(WM_SETICON, ICON_SMALL, (LPARAM) m_icon);
|
||||||
|
LegoDeviceEnumerate* enumerator = currentConfigApp->m_device_enumerator;
|
||||||
|
enumerator->FUN_1009d210();
|
||||||
|
m_modified = currentConfigApp->ReadRegisterSettings();
|
||||||
|
CWnd* list_3d_devices = GetDlgItem(IDC_LIST_3DDEVICES);
|
||||||
|
int driver_i = 0;
|
||||||
|
int device_i = 0;
|
||||||
|
int selected = 0;
|
||||||
|
char device_name[256];
|
||||||
|
const list<MxDriver>& driver_list = enumerator->GetDriverList();
|
||||||
|
for (list<MxDriver>::const_iterator it_driver = driver_list.begin(); it_driver != driver_list.end(); it_driver++) {
|
||||||
|
const MxDriver& driver = *it_driver;
|
||||||
|
for (list<Direct3DDeviceInfo>::const_iterator it_device = driver.m_devices.begin();
|
||||||
|
it_device != driver.m_devices.end();
|
||||||
|
it_device++) {
|
||||||
|
const Direct3DDeviceInfo& device = *it_device;
|
||||||
|
if (&device == currentConfigApp->m_device) {
|
||||||
|
selected = device_i;
|
||||||
|
}
|
||||||
|
device_i += 1;
|
||||||
|
sprintf(
|
||||||
|
device_name,
|
||||||
|
driver_i == 0 ? "%s ( Primary Device )" : "%s ( Secondary Device )",
|
||||||
|
device.m_deviceName
|
||||||
|
);
|
||||||
|
::SendMessage(list_3d_devices->m_hWnd, LB_ADDSTRING, 0, (LPARAM) device_name);
|
||||||
|
}
|
||||||
|
driver_i += 1;
|
||||||
|
}
|
||||||
|
::SendMessage(list_3d_devices->m_hWnd, LB_SETCURSEL, selected, 0);
|
||||||
|
UpdateInterface();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404080
|
||||||
|
void CMainDialog::OnSysCommand(UINT nID, LPARAM lParam)
|
||||||
|
{
|
||||||
|
if ((nID & 0xfff0) == 0x10) {
|
||||||
|
CAboutDialog about_dialog;
|
||||||
|
about_dialog.DoModal();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404150
|
||||||
|
void CMainDialog::OnPaint()
|
||||||
|
{
|
||||||
|
if (IsIconic()) {
|
||||||
|
CPaintDC painter(this);
|
||||||
|
::SendMessage(m_hWnd, WM_ICONERASEBKGND, (WPARAM) painter.m_hDC, 0);
|
||||||
|
RECT dim;
|
||||||
|
GetClientRect(&dim);
|
||||||
|
DrawIcon(
|
||||||
|
painter.m_hDC,
|
||||||
|
(dim.right - dim.left - GetSystemMetrics(SM_CXICON) + 1) / 2,
|
||||||
|
(dim.bottom - dim.top - GetSystemMetrics(SM_CYICON) + 1) / 2,
|
||||||
|
m_icon
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404230
|
||||||
|
HCURSOR CMainDialog::OnQueryDragIcon()
|
||||||
|
{
|
||||||
|
return m_icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404240
|
||||||
|
void CMainDialog::OnList3DevicesSelectionChanged()
|
||||||
|
{
|
||||||
|
LegoDeviceEnumerate* device_enumerator = currentConfigApp->m_device_enumerator;
|
||||||
|
int selected = ::SendMessage(GetDlgItem(IDC_LIST_3DDEVICES)->m_hWnd, LB_GETCURSEL, 0, 0);
|
||||||
|
device_enumerator->GetDevice(selected, currentConfigApp->m_driver, currentConfigApp->m_device);
|
||||||
|
if (currentConfigApp->GetHardwareDeviceColorModel()) {
|
||||||
|
GetDlgItem(IDC_CHK_DRAW_CURSOR)->EnableWindow(TRUE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
currentConfigApp->m_3d_video_ram = FALSE;
|
||||||
|
currentConfigApp->m_flip_surfaces = FALSE;
|
||||||
|
CheckDlgButton(IDC_CHK_3D_VIDEO_MEMORY, currentConfigApp->m_3d_video_ram);
|
||||||
|
CheckDlgButton(IDC_CHK_FLIP_VIDEO_MEM_PAGES, currentConfigApp->m_flip_surfaces);
|
||||||
|
}
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404320
|
||||||
|
void CMainDialog::OnCancel()
|
||||||
|
{
|
||||||
|
CDialog::OnCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404330
|
||||||
|
void CMainDialog::OnDestroy()
|
||||||
|
{
|
||||||
|
CDialog::Default();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404340
|
||||||
|
void CMainDialog::OnButtonCancel()
|
||||||
|
{
|
||||||
|
if (m_modified) {
|
||||||
|
currentConfigApp->WriteRegisterSettings();
|
||||||
|
}
|
||||||
|
OnCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404360
|
||||||
|
void CMainDialog::UpdateInterface()
|
||||||
|
{
|
||||||
|
currentConfigApp->ValidateSettings();
|
||||||
|
GetDlgItem(IDC_CHK_3D_VIDEO_MEMORY)
|
||||||
|
->EnableWindow(!currentConfigApp->m_flip_surfaces && !currentConfigApp->GetHardwareDeviceColorModel());
|
||||||
|
CheckDlgButton(IDC_CHK_FLIP_VIDEO_MEM_PAGES, currentConfigApp->m_flip_surfaces);
|
||||||
|
CheckDlgButton(IDC_CHK_3D_VIDEO_MEMORY, currentConfigApp->m_3d_video_ram);
|
||||||
|
BOOL full_screen = currentConfigApp->m_full_screen;
|
||||||
|
currentConfigApp->AdjustDisplayBitDepthBasedOnRenderStatus();
|
||||||
|
if (currentConfigApp->GetHardwareDeviceColorModel()) {
|
||||||
|
CheckDlgButton(IDC_CHK_DRAW_CURSOR, TRUE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CheckDlgButton(IDC_CHK_DRAW_CURSOR, FALSE);
|
||||||
|
currentConfigApp->m_draw_cursor = FALSE;
|
||||||
|
GetDlgItem(IDC_CHK_DRAW_CURSOR)->EnableWindow(FALSE);
|
||||||
|
}
|
||||||
|
if (full_screen) {
|
||||||
|
CheckRadioButton(
|
||||||
|
IDC_RAD_PALETTE_256,
|
||||||
|
IDC_RAD_PALETTE_16BIT,
|
||||||
|
currentConfigApp->m_display_bit_depth == 8 ? IDC_RAD_PALETTE_256 : IDC_RAD_PALETTE_16BIT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CheckDlgButton(IDC_RAD_PALETTE_256, 0);
|
||||||
|
CheckDlgButton(IDC_RAD_PALETTE_16BIT, 0);
|
||||||
|
currentConfigApp->m_display_bit_depth = 0;
|
||||||
|
}
|
||||||
|
GetDlgItem(IDC_RAD_PALETTE_256)
|
||||||
|
->EnableWindow(full_screen && currentConfigApp->GetConditionalDeviceRenderBitDepth());
|
||||||
|
GetDlgItem(IDC_RAD_PALETTE_16BIT)->EnableWindow(full_screen && currentConfigApp->GetDeviceRenderBitStatus());
|
||||||
|
CheckDlgButton(IDC_CHK_3DSOUND, currentConfigApp->m_3d_sound);
|
||||||
|
CheckDlgButton(IDC_CHK_DRAW_CURSOR, currentConfigApp->m_draw_cursor);
|
||||||
|
switch (currentConfigApp->m_model_quality) {
|
||||||
|
case 1:
|
||||||
|
CheckRadioButton(IDC_RAD_MODEL_QUALITY_LOW, IDC_RAD_MODEL_QUALITY_HIGH, IDC_RAD_MODEL_QUALITY_LOW);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
CheckRadioButton(IDC_RAD_MODEL_QUALITY_LOW, IDC_RAD_MODEL_QUALITY_HIGH, IDC_RAD_MODEL_QUALITY_HIGH);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CheckRadioButton(
|
||||||
|
IDC_RAD_TEXTURE_QUALITY_LOW,
|
||||||
|
IDC_RAD_TEXTURE_QUALITY_HIGH,
|
||||||
|
currentConfigApp->m_texture_quality == 0 ? IDC_RAD_TEXTURE_QUALITY_LOW : IDC_RAD_TEXTURE_QUALITY_HIGH
|
||||||
|
);
|
||||||
|
CheckDlgButton(IDC_CHK_JOYSTICK, currentConfigApp->m_use_joystick);
|
||||||
|
CheckDlgButton(IDC_CHK_MUSIC, currentConfigApp->m_music);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x004045e0
|
||||||
|
void CMainDialog::OnCheckbox3DSound()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_3d_sound = IsDlgButtonChecked(IDC_CHK_3DSOUND);
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404610
|
||||||
|
void CMainDialog::OnCheckbox3DVideoMemory()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_3d_video_ram = IsDlgButtonChecked(IDC_CHK_3D_VIDEO_MEMORY);
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404640
|
||||||
|
void CMainDialog::OnRadiobuttonPalette16bit()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_display_bit_depth = 16;
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404670
|
||||||
|
void CMainDialog::OnRadiobuttonPalette256()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_display_bit_depth = 8;
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x004046a0
|
||||||
|
void CMainDialog::OnCheckboxFlipVideoMemPages()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_flip_surfaces = IsDlgButtonChecked(IDC_CHK_FLIP_VIDEO_MEM_PAGES);
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x004046d0
|
||||||
|
void CMainDialog::OnRadiobuttonModelLowQuality()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_model_quality = 1;
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404700
|
||||||
|
void CMainDialog::OnRadiobuttonModelHighQuality()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_model_quality = 2;
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404730
|
||||||
|
void CMainDialog::OnRadiobuttonTextureLowQuality()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_texture_quality = 0;
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404760
|
||||||
|
void CMainDialog::OnRadiobuttonTextureHighQuality()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_texture_quality = 1;
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404790
|
||||||
|
void CMainDialog::OnCheckboxJoystick()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_use_joystick = IsDlgButtonChecked(IDC_CHK_JOYSTICK);
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x004047c0
|
||||||
|
void CMainDialog::OnButtonAdvanced()
|
||||||
|
{
|
||||||
|
SwitchToAdvanced(!m_advanced);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x004047d0
|
||||||
|
void CMainDialog::SwitchToAdvanced(BOOL p_advanced)
|
||||||
|
{
|
||||||
|
RECT dialog_rect;
|
||||||
|
RECT grp_advanced_rect;
|
||||||
|
::GetWindowRect(m_hWnd, &dialog_rect);
|
||||||
|
::GetWindowRect(GetDlgItem(IDC_GRP_ADVANCED)->m_hWnd, &grp_advanced_rect);
|
||||||
|
CWnd* button_advanced = GetDlgItem(IDC_BTN_ADVANCED);
|
||||||
|
m_advanced = p_advanced;
|
||||||
|
int height;
|
||||||
|
if (p_advanced) {
|
||||||
|
height = grp_advanced_rect.bottom - dialog_rect.top + 10;
|
||||||
|
GetDlgItem(IDC_BMP_SHARK)->EnableWindow(TRUE);
|
||||||
|
button_advanced->SetWindowText("Basic");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
height = grp_advanced_rect.top - dialog_rect.top;
|
||||||
|
GetDlgItem(IDC_BMP_SHARK)->EnableWindow(FALSE);
|
||||||
|
button_advanced->SetWindowText("Advanced");
|
||||||
|
}
|
||||||
|
SetWindowPos(&wndTop, 0, 0, dialog_rect.right - dialog_rect.left, height, SWP_NOMOVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404890
|
||||||
|
void CMainDialog::OnCheckboxDrawCursor()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_draw_cursor = IsDlgButtonChecked(IDC_CHK_DRAW_CURSOR);
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x004048c0
|
||||||
|
void CMainDialog::OnCheckboxMusic()
|
||||||
|
{
|
||||||
|
currentConfigApp->m_music = IsDlgButtonChecked(IDC_CHK_MUSIC);
|
||||||
|
m_modified = TRUE;
|
||||||
|
UpdateInterface();
|
||||||
|
}
|
||||||
70
CONFIG_orig/MainDlg.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#if !defined(AFX_MAINDLG_H)
|
||||||
|
#define AFX_MAINDLG_H
|
||||||
|
|
||||||
|
#include "StdAfx.h"
|
||||||
|
#include "compat.h"
|
||||||
|
#include "decomp.h"
|
||||||
|
#include "res/resource.h"
|
||||||
|
|
||||||
|
// VTABLE: CONFIG 0x004063e0
|
||||||
|
// SIZE 0x70
|
||||||
|
class CMainDialog : public CDialog {
|
||||||
|
public:
|
||||||
|
CMainDialog(CWnd* pParent);
|
||||||
|
enum {
|
||||||
|
IDD = IDD_MAIN
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void DoDataExchange(CDataExchange* pDX) override;
|
||||||
|
void UpdateInterface();
|
||||||
|
void SwitchToAdvanced(BOOL p_advanced);
|
||||||
|
|
||||||
|
undefined m_unk0x60[4]; // 0x60
|
||||||
|
HCURSOR m_icon; // 0x64
|
||||||
|
BOOL m_modified; // 0x68
|
||||||
|
BOOL m_advanced; // 0x6c
|
||||||
|
// Implementation
|
||||||
|
|
||||||
|
protected:
|
||||||
|
BOOL OnInitDialog() override;
|
||||||
|
void OnSysCommand(UINT nID, LPARAM lParam);
|
||||||
|
void OnPaint();
|
||||||
|
HCURSOR OnQueryDragIcon();
|
||||||
|
void OnList3DevicesSelectionChanged();
|
||||||
|
void OnCancel();
|
||||||
|
void OnDestroy();
|
||||||
|
void OnButtonCancel();
|
||||||
|
void OnCheckbox3DSound();
|
||||||
|
void OnCheckbox3DVideoMemory();
|
||||||
|
void OnRadiobuttonPalette16bit();
|
||||||
|
void OnRadiobuttonPalette256();
|
||||||
|
void OnCheckboxFlipVideoMemPages();
|
||||||
|
void OnRadiobuttonModelLowQuality();
|
||||||
|
void OnRadiobuttonModelHighQuality();
|
||||||
|
void OnRadiobuttonTextureLowQuality();
|
||||||
|
void OnRadiobuttonTextureHighQuality();
|
||||||
|
void OnCheckboxJoystick();
|
||||||
|
void OnButtonAdvanced();
|
||||||
|
void OnCheckboxDrawCursor();
|
||||||
|
void OnCheckboxMusic();
|
||||||
|
|
||||||
|
DECLARE_MESSAGE_MAP()
|
||||||
|
};
|
||||||
|
|
||||||
|
// SYNTHETIC: CONFIG 0x00403de0
|
||||||
|
// CMainDialog::`scalar deleting destructor'
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403e60
|
||||||
|
// CMainDialog::_GetBaseMessageMap
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403e70
|
||||||
|
// CMainDialog::GetMessageMap
|
||||||
|
|
||||||
|
// GLOBAL: CONFIG 0x00406120
|
||||||
|
// CMainDialog::messageMap
|
||||||
|
|
||||||
|
// GLOBAL: CONFIG 0x00406128
|
||||||
|
// CMainDialog::_messageEntries
|
||||||
|
|
||||||
|
#endif // !defined(AFX_MAINDLG_H)
|
||||||
5
CONFIG_orig/StdAfx.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
// stdafx.cpp : source file that includes just the standard includes
|
||||||
|
// simple.pch will be the pre-compiled header
|
||||||
|
// stdafx.obj will contain the pre-compiled type information
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
31
CONFIG_orig/StdAfx.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#if !defined(AFX_STDAFX_H)
|
||||||
|
#define AFX_STDAFX_H
|
||||||
|
|
||||||
|
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
|
||||||
|
|
||||||
|
#include <afxext.h> // MFC extensions
|
||||||
|
#include <afxwin.h> // MFC core and standard components
|
||||||
|
#ifndef _AFX_NO_AFXCMN_SUPPORT
|
||||||
|
#include <afxcmn.h> // MFC support for Windows Common Controls
|
||||||
|
#endif // _AFX_NO_AFXCMN_SUPPORT
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x402ca0
|
||||||
|
// CObject::Serialize
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x402cb0
|
||||||
|
// CObject::AssertValid
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x402cc0
|
||||||
|
// CObject::Dump
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403c90
|
||||||
|
// CWnd::BeginModalState
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403ca0
|
||||||
|
// CWnd::EndModalState
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // !defined(AFX_STDAFX_H)
|
||||||
435
CONFIG_orig/config.cpp
Normal file
@ -0,0 +1,435 @@
|
|||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "ConfigCommandLineInfo.h"
|
||||||
|
#include "MainDlg.h"
|
||||||
|
#include "detectdx5.h"
|
||||||
|
|
||||||
|
#include <direct.h> // _chdir
|
||||||
|
#include <mxdirectx/legodxinfo.h>
|
||||||
|
#include <mxdirectx/mxdirect3d.h>
|
||||||
|
#include <process.h> // _spawnl
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(CWinApp, 0xc4)
|
||||||
|
DECOMP_SIZE_ASSERT(CConfigApp, 0x108)
|
||||||
|
|
||||||
|
DECOMP_STATIC_ASSERT(offsetof(CConfigApp, m_display_bit_depth) == 0xd0)
|
||||||
|
|
||||||
|
BEGIN_MESSAGE_MAP(CConfigApp, CWinApp)
|
||||||
|
ON_COMMAND(ID_HELP, OnHelp)
|
||||||
|
END_MESSAGE_MAP()
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00402c40
|
||||||
|
CConfigApp::CConfigApp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MiB (1024 * 1024)
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00402dc0
|
||||||
|
BOOL CConfigApp::InitInstance()
|
||||||
|
{
|
||||||
|
if (!IsLegoNotRunning()) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (!DetectDirectX5()) {
|
||||||
|
AfxMessageBox(
|
||||||
|
"\"LEGO\xae Island\" is not detecting DirectX 5 or later. Please quit all other applications and try "
|
||||||
|
"again."
|
||||||
|
);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
#ifdef _AFXDLL
|
||||||
|
Enable3dControls();
|
||||||
|
#else
|
||||||
|
Enable3dControlsStatic();
|
||||||
|
#endif
|
||||||
|
CConfigCommandLineInfo cmdInfo;
|
||||||
|
ParseCommandLine(cmdInfo);
|
||||||
|
if (_stricmp(afxCurrentAppName, "config") == 0) {
|
||||||
|
m_run_config_dialog = TRUE;
|
||||||
|
}
|
||||||
|
m_device_enumerator = new LegoDeviceEnumerate;
|
||||||
|
if (m_device_enumerator->DoEnumerate()) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
m_driver = NULL;
|
||||||
|
m_device = NULL;
|
||||||
|
m_full_screen = TRUE;
|
||||||
|
m_wide_view_angle = TRUE;
|
||||||
|
m_use_joystick = FALSE;
|
||||||
|
m_music = TRUE;
|
||||||
|
m_flip_surfaces = FALSE;
|
||||||
|
m_3d_video_ram = FALSE;
|
||||||
|
m_joystick_index = -1;
|
||||||
|
m_display_bit_depth = 16;
|
||||||
|
MEMORYSTATUS memory_status;
|
||||||
|
memory_status.dwLength = sizeof(memory_status);
|
||||||
|
GlobalMemoryStatus(&memory_status);
|
||||||
|
if (memory_status.dwTotalPhys < 12 * MiB) {
|
||||||
|
m_3d_sound = FALSE;
|
||||||
|
m_model_quality = 0;
|
||||||
|
m_texture_quality = 1;
|
||||||
|
}
|
||||||
|
else if (memory_status.dwTotalPhys < 20 * MiB) {
|
||||||
|
m_3d_sound = FALSE;
|
||||||
|
m_model_quality = 1;
|
||||||
|
m_texture_quality = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_model_quality = 2;
|
||||||
|
m_3d_sound = TRUE;
|
||||||
|
m_texture_quality = 1;
|
||||||
|
}
|
||||||
|
if (!m_run_config_dialog) {
|
||||||
|
ReadRegisterSettings();
|
||||||
|
ValidateSettings();
|
||||||
|
WriteRegisterSettings();
|
||||||
|
delete m_device_enumerator;
|
||||||
|
m_device_enumerator = NULL;
|
||||||
|
m_driver = NULL;
|
||||||
|
m_device = NULL;
|
||||||
|
char password[256];
|
||||||
|
ReadReg("password", password, sizeof(password));
|
||||||
|
const char* exe = _stricmp("ogel", password) == 0 ? "isled.exe" : "isle.exe";
|
||||||
|
char diskpath[1024];
|
||||||
|
if (ReadReg("diskpath", diskpath, sizeof(diskpath))) {
|
||||||
|
_chdir(diskpath);
|
||||||
|
}
|
||||||
|
_spawnl(_P_NOWAIT, exe, exe, "/diskstream", "/script", "\\lego\\scripts\\isle\\isle.si", NULL);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
CMainDialog main_dialog(NULL);
|
||||||
|
main_dialog.DoModal();
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403100
|
||||||
|
BOOL CConfigApp::IsLegoNotRunning()
|
||||||
|
{
|
||||||
|
HWND hWnd = FindWindowA("Lego Island MainNoM App", "LEGO\xae");
|
||||||
|
if (_stricmp(afxCurrentAppName, "config") == 0 || !hWnd) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
if (SetForegroundWindow(hWnd)) {
|
||||||
|
ShowWindow(hWnd, SW_RESTORE);
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x004031b0
|
||||||
|
BOOL CConfigApp::WriteReg(const char* p_key, const char* p_value) const
|
||||||
|
{
|
||||||
|
HKEY hKey;
|
||||||
|
DWORD pos;
|
||||||
|
|
||||||
|
if (RegCreateKeyExA(
|
||||||
|
HKEY_LOCAL_MACHINE,
|
||||||
|
"SOFTWARE\\Mindscape\\LEGO Island",
|
||||||
|
0,
|
||||||
|
"string",
|
||||||
|
0,
|
||||||
|
KEY_READ | KEY_WRITE,
|
||||||
|
NULL,
|
||||||
|
&hKey,
|
||||||
|
&pos
|
||||||
|
) == ERROR_SUCCESS) {
|
||||||
|
if (RegSetValueExA(hKey, p_key, 0, REG_SZ, (LPBYTE) p_value, strlen(p_value)) == ERROR_SUCCESS) {
|
||||||
|
if (RegCloseKey(hKey) == ERROR_SUCCESS) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403240
|
||||||
|
BOOL CConfigApp::ReadReg(LPCSTR p_key, LPCSTR p_value, DWORD p_size) const
|
||||||
|
{
|
||||||
|
HKEY hKey;
|
||||||
|
DWORD valueType;
|
||||||
|
|
||||||
|
BOOL out = FALSE;
|
||||||
|
DWORD size = p_size;
|
||||||
|
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Mindscape\\LEGO Island", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
||||||
|
if (RegQueryValueExA(hKey, p_key, NULL, &valueType, (LPBYTE) p_value, &size) == ERROR_SUCCESS) {
|
||||||
|
if (RegCloseKey(hKey) == ERROR_SUCCESS) {
|
||||||
|
out = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x004032b0
|
||||||
|
BOOL CConfigApp::ReadRegBool(LPCSTR p_key, BOOL* p_bool) const
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
|
||||||
|
BOOL read = ReadReg(p_key, buffer, sizeof(buffer));
|
||||||
|
if (read) {
|
||||||
|
if (strcmp("YES", buffer) == 0) {
|
||||||
|
*p_bool = TRUE;
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp("NO", buffer) == 0) {
|
||||||
|
*p_bool = FALSE;
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
read = FALSE;
|
||||||
|
}
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403380
|
||||||
|
BOOL CConfigApp::ReadRegInt(LPCSTR p_key, int* p_value) const
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
|
||||||
|
BOOL read = ReadReg(p_key, buffer, sizeof(buffer));
|
||||||
|
if (read) {
|
||||||
|
*p_value = atoi(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x004033d0
|
||||||
|
BOOL CConfigApp::IsDeviceInBasicRGBMode() const
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* BUG: should be:
|
||||||
|
* return !GetHardwareDeviceColorModel() && (m_device->m_HELDesc.dcmColorModel & D3DCOLOR_RGB);
|
||||||
|
*/
|
||||||
|
return !GetHardwareDeviceColorModel() && m_device->m_HELDesc.dcmColorModel == D3DCOLOR_RGB;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403400
|
||||||
|
D3DCOLORMODEL CConfigApp::GetHardwareDeviceColorModel() const
|
||||||
|
{
|
||||||
|
return m_device->m_HWDesc.dcmColorModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403410
|
||||||
|
BOOL CConfigApp::IsPrimaryDriver() const
|
||||||
|
{
|
||||||
|
return m_driver == &m_device_enumerator->GetDriverList().front();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403430
|
||||||
|
BOOL CConfigApp::ReadRegisterSettings()
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
BOOL is_modified = FALSE;
|
||||||
|
int tmp = -1;
|
||||||
|
|
||||||
|
if (ReadReg("3D Device ID", buffer, sizeof(buffer))) {
|
||||||
|
tmp = m_device_enumerator->ParseDeviceName(buffer);
|
||||||
|
if (tmp >= 0) {
|
||||||
|
tmp = m_device_enumerator->GetDevice(tmp, m_driver, m_device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tmp != 0) {
|
||||||
|
is_modified = TRUE;
|
||||||
|
m_device_enumerator->FUN_1009d210();
|
||||||
|
tmp = m_device_enumerator->FUN_1009d0d0();
|
||||||
|
m_device_enumerator->GetDevice(tmp, m_driver, m_device);
|
||||||
|
}
|
||||||
|
if (!ReadRegInt("Display Bit Depth", &m_display_bit_depth)) {
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (!ReadRegBool("Flip Surfaces", &m_flip_surfaces)) {
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (!ReadRegBool("Full Screen", &m_full_screen)) {
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (!ReadRegBool("Back Buffers in Video RAM", &m_3d_video_ram)) {
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (!ReadRegBool("Wide View Angle", &m_wide_view_angle)) {
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (!ReadRegBool("3DSound", &m_3d_sound)) {
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (!ReadRegBool("Draw Cursor", &m_draw_cursor)) {
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (!ReadRegInt("Island Quality", &m_model_quality)) {
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (!ReadRegInt("Island Texture", &m_texture_quality)) {
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (!ReadRegBool("UseJoystick", &m_use_joystick)) {
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (!ReadRegBool("Music", &m_music)) {
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (!ReadRegInt("JoystickIndex", &m_joystick_index)) {
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
return is_modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403630
|
||||||
|
BOOL CConfigApp::ValidateSettings()
|
||||||
|
{
|
||||||
|
BOOL is_modified = FALSE;
|
||||||
|
|
||||||
|
if (!IsPrimaryDriver() && !m_full_screen) {
|
||||||
|
m_full_screen = TRUE;
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (IsDeviceInBasicRGBMode()) {
|
||||||
|
if (m_3d_video_ram) {
|
||||||
|
m_3d_video_ram = FALSE;
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (m_flip_surfaces) {
|
||||||
|
m_flip_surfaces = FALSE;
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (m_display_bit_depth != 16) {
|
||||||
|
m_display_bit_depth = 16;
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!GetHardwareDeviceColorModel()) {
|
||||||
|
m_draw_cursor = FALSE;
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!m_3d_video_ram) {
|
||||||
|
m_3d_video_ram = TRUE;
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (m_full_screen && !m_flip_surfaces) {
|
||||||
|
m_flip_surfaces = TRUE;
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_flip_surfaces) {
|
||||||
|
if (!m_3d_video_ram) {
|
||||||
|
m_3d_video_ram = TRUE;
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (!m_full_screen) {
|
||||||
|
m_full_screen = TRUE;
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((m_display_bit_depth != 8 && m_display_bit_depth != 16) && (m_display_bit_depth != 0 || m_full_screen)) {
|
||||||
|
m_display_bit_depth = 8;
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (m_model_quality < 0 || m_model_quality > 2) {
|
||||||
|
m_model_quality = 1;
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
if (m_texture_quality < 0 || m_texture_quality > 1) {
|
||||||
|
m_texture_quality = 0;
|
||||||
|
is_modified = TRUE;
|
||||||
|
}
|
||||||
|
return is_modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x004037a0
|
||||||
|
DWORD CConfigApp::GetConditionalDeviceRenderBitDepth() const
|
||||||
|
{
|
||||||
|
if (IsDeviceInBasicRGBMode()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (GetHardwareDeviceColorModel()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return m_device->m_HELDesc.dwDeviceRenderBitDepth & 0x800;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x004037e0
|
||||||
|
DWORD CConfigApp::GetDeviceRenderBitStatus() const
|
||||||
|
{
|
||||||
|
if (GetHardwareDeviceColorModel()) {
|
||||||
|
return m_device->m_HWDesc.dwDeviceRenderBitDepth & 0x400;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return m_device->m_HELDesc.dwDeviceRenderBitDepth & 0x400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403810
|
||||||
|
BOOL CConfigApp::AdjustDisplayBitDepthBasedOnRenderStatus()
|
||||||
|
{
|
||||||
|
if (m_display_bit_depth == 8) {
|
||||||
|
if (GetConditionalDeviceRenderBitDepth()) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_display_bit_depth == 16) {
|
||||||
|
if (GetDeviceRenderBitStatus()) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (GetConditionalDeviceRenderBitDepth()) {
|
||||||
|
m_display_bit_depth = 8;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
if (GetDeviceRenderBitStatus()) {
|
||||||
|
m_display_bit_depth = 16;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
m_display_bit_depth = 8;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 00403890
|
||||||
|
void CConfigApp::WriteRegisterSettings() const
|
||||||
|
|
||||||
|
{
|
||||||
|
char buffer[128];
|
||||||
|
|
||||||
|
#define WriteRegBool(NAME, VALUE) WriteReg(NAME, VALUE ? "YES" : "NO")
|
||||||
|
#define WriteRegInt(NAME, VALUE) \
|
||||||
|
do { \
|
||||||
|
sprintf(buffer, "%d", VALUE); \
|
||||||
|
WriteReg(NAME, buffer); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
m_device_enumerator->FormatDeviceName(buffer, m_driver, m_device);
|
||||||
|
WriteReg("3D Device ID", buffer);
|
||||||
|
WriteReg("3D Device Name", m_device->m_deviceName);
|
||||||
|
WriteRegInt("Display Bit Depth", m_display_bit_depth);
|
||||||
|
WriteRegBool("Flip Surfaces", m_flip_surfaces);
|
||||||
|
WriteRegBool("Full Screen", m_full_screen);
|
||||||
|
WriteRegBool("Back Buffers in Video RAM", m_3d_video_ram);
|
||||||
|
WriteRegBool("Wide View Angle", m_wide_view_angle);
|
||||||
|
WriteRegBool("3DSound", m_3d_sound);
|
||||||
|
WriteRegBool("Draw Cursor", m_draw_cursor);
|
||||||
|
WriteRegInt("Island Quality", m_model_quality);
|
||||||
|
WriteRegInt("Island Texture", m_texture_quality);
|
||||||
|
WriteRegBool("UseJoystick", m_use_joystick);
|
||||||
|
WriteRegBool("Music", m_music);
|
||||||
|
WriteRegInt("JoystickIndex", m_joystick_index);
|
||||||
|
|
||||||
|
#undef WriteRegBool
|
||||||
|
#undef WriteRegInt
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00403a90
|
||||||
|
int CConfigApp::ExitInstance()
|
||||||
|
{
|
||||||
|
if (m_device_enumerator) {
|
||||||
|
delete m_device_enumerator;
|
||||||
|
m_device_enumerator = NULL;
|
||||||
|
}
|
||||||
|
return CWinApp::ExitInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GLOBAL: CONFIG 0x00408e50
|
||||||
|
CConfigApp g_theApp;
|
||||||
91
CONFIG_orig/config.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#if !defined(AFX_CONFIG_H)
|
||||||
|
#define AFX_CONFIG_H
|
||||||
|
|
||||||
|
#include "StdAfx.h"
|
||||||
|
#include "compat.h"
|
||||||
|
#include "decomp.h"
|
||||||
|
|
||||||
|
#include <d3d.h>
|
||||||
|
|
||||||
|
class LegoDeviceEnumerate;
|
||||||
|
struct Direct3DDeviceInfo;
|
||||||
|
struct MxDriver;
|
||||||
|
|
||||||
|
#define currentConfigApp ((CConfigApp*) afxCurrentWinApp)
|
||||||
|
|
||||||
|
// VTABLE: CONFIG 0x00406040
|
||||||
|
// SIZE 0x108
|
||||||
|
class CConfigApp : public CWinApp {
|
||||||
|
public:
|
||||||
|
CConfigApp();
|
||||||
|
|
||||||
|
// Overrides
|
||||||
|
// ClassWizard generated virtual function overrides
|
||||||
|
//{{AFX_VIRTUAL(CConfigApp)
|
||||||
|
|
||||||
|
public:
|
||||||
|
BOOL InitInstance() override;
|
||||||
|
int ExitInstance() override;
|
||||||
|
//}}AFX_VIRTUAL
|
||||||
|
|
||||||
|
// Implementation
|
||||||
|
|
||||||
|
BOOL WriteReg(const char* p_key, const char* p_value) const;
|
||||||
|
BOOL ReadReg(LPCSTR p_key, LPCSTR p_value, DWORD p_size) const;
|
||||||
|
BOOL ReadRegBool(LPCSTR p_key, BOOL* p_bool) const;
|
||||||
|
BOOL ReadRegInt(LPCSTR p_key, int* p_value) const;
|
||||||
|
BOOL IsDeviceInBasicRGBMode() const;
|
||||||
|
D3DCOLORMODEL GetHardwareDeviceColorModel() const;
|
||||||
|
BOOL IsPrimaryDriver() const;
|
||||||
|
BOOL ReadRegisterSettings();
|
||||||
|
BOOL ValidateSettings();
|
||||||
|
DWORD GetConditionalDeviceRenderBitDepth() const;
|
||||||
|
DWORD GetDeviceRenderBitStatus() const;
|
||||||
|
BOOL AdjustDisplayBitDepthBasedOnRenderStatus();
|
||||||
|
void CConfigApp::WriteRegisterSettings() const;
|
||||||
|
|
||||||
|
//{{AFX_MSG(CConfigApp)
|
||||||
|
// NOTE - the ClassWizard will add and remove member functions here.
|
||||||
|
// DO NOT EDIT what you see in these blocks of generated code !
|
||||||
|
//}}AFX_MSG
|
||||||
|
DECLARE_MESSAGE_MAP()
|
||||||
|
|
||||||
|
private:
|
||||||
|
BOOL IsLegoNotRunning();
|
||||||
|
|
||||||
|
public:
|
||||||
|
LegoDeviceEnumerate* m_device_enumerator; // 0x0c4
|
||||||
|
MxDriver* m_driver; // 0x0c8
|
||||||
|
Direct3DDeviceInfo* m_device; // 0x0cc
|
||||||
|
int m_display_bit_depth; // 0x0d0
|
||||||
|
BOOL m_flip_surfaces; // 0x0d4
|
||||||
|
BOOL m_full_screen; // 0x0d8
|
||||||
|
BOOL m_3d_video_ram; // 0x0dc
|
||||||
|
BOOL m_wide_view_angle; // 0x0e0
|
||||||
|
BOOL m_3d_sound; // 0x0e4
|
||||||
|
BOOL m_draw_cursor; // 0x0e8
|
||||||
|
BOOL m_use_joystick; // 0x0ec
|
||||||
|
int m_joystick_index; // 0x0f0
|
||||||
|
BOOL m_run_config_dialog; // 0x0f4
|
||||||
|
int m_model_quality; // 0x0f8
|
||||||
|
int m_texture_quality; // 0x0fc
|
||||||
|
undefined m_unk0x100[4]; // 0x100
|
||||||
|
BOOL m_music; // 0x104
|
||||||
|
};
|
||||||
|
|
||||||
|
// SYNTHETIC: CONFIG 0x00402cd0
|
||||||
|
// CConfigApp::`scalar deleting destructor'
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x402c20
|
||||||
|
// CConfigApp::_GetBaseMessageMap
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x402c30
|
||||||
|
// CConfigApp::GetMessageMap
|
||||||
|
|
||||||
|
// GLOBAL: CONFIG 0x406008
|
||||||
|
// CConfigApp::messageMap
|
||||||
|
|
||||||
|
// GLOBAL: CONFIG 0x406010
|
||||||
|
// CConfigApp::_messageEntries
|
||||||
|
|
||||||
|
#endif // !defined(AFX_CONFIG_H)
|
||||||
142
CONFIG_orig/detectdx5.cpp
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
#include "detectdx5.h"
|
||||||
|
|
||||||
|
#include <ddraw.h>
|
||||||
|
#include <dinput.h>
|
||||||
|
|
||||||
|
typedef HRESULT WINAPI DirectDrawCreate_fn(GUID FAR* lpGUID, LPDIRECTDRAW FAR* lplpDD, IUnknown FAR* pUnkOuter);
|
||||||
|
typedef HRESULT WINAPI
|
||||||
|
DirectInputCreateA_fn(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTA* ppDI, LPUNKNOWN punkOuter);
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x004048f0
|
||||||
|
BOOL DetectDirectX5()
|
||||||
|
{
|
||||||
|
unsigned int version;
|
||||||
|
BOOL found;
|
||||||
|
DetectDirectX(&version, &found);
|
||||||
|
return version >= 0x500;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: CONFIG 0x00404920
|
||||||
|
void DetectDirectX(unsigned int* p_version, BOOL* p_found)
|
||||||
|
{
|
||||||
|
OSVERSIONINFOA os_version;
|
||||||
|
|
||||||
|
os_version.dwOSVersionInfoSize = sizeof(os_version);
|
||||||
|
if (!GetVersionExA(&os_version)) {
|
||||||
|
*p_version = 0;
|
||||||
|
*p_found = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (os_version.dwPlatformId == 2) {
|
||||||
|
*p_found = 2;
|
||||||
|
if (os_version.dwMajorVersion < 4) {
|
||||||
|
*p_found = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (os_version.dwMajorVersion != 4) {
|
||||||
|
*p_version = 0x501;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*p_version = 0x200;
|
||||||
|
HMODULE dinput_module = LoadLibraryA("DINPUT.DLL");
|
||||||
|
if (!dinput_module) {
|
||||||
|
OutputDebugStringA("Couldn't LoadLibrary DInput\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DirectInputCreateA_fn* func_DirectInputCreateA =
|
||||||
|
(DirectInputCreateA_fn*) GetProcAddress(dinput_module, "DirectInputCreateA");
|
||||||
|
FreeLibrary(dinput_module);
|
||||||
|
if (!func_DirectInputCreateA) {
|
||||||
|
OutputDebugStringA("Couldn't GetProcAddress DInputCreate\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*p_version = 0x300;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*p_found = 1;
|
||||||
|
if (LOWORD(os_version.dwBuildNumber) >= 0x550) {
|
||||||
|
*p_version = 0x501;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HMODULE ddraw_module = LoadLibraryA("DDRAW.DLL");
|
||||||
|
if (!ddraw_module) {
|
||||||
|
*p_version = 0;
|
||||||
|
*p_found = 0;
|
||||||
|
FreeLibrary(ddraw_module);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DirectDrawCreate_fn* func_DirectDrawCreate =
|
||||||
|
(DirectDrawCreate_fn*) GetProcAddress(ddraw_module, "DirectDrawCreate");
|
||||||
|
if (!func_DirectDrawCreate) {
|
||||||
|
*p_version = 0;
|
||||||
|
*p_found = 0;
|
||||||
|
FreeLibrary(ddraw_module);
|
||||||
|
OutputDebugStringA("Couldn't LoadLibrary DDraw\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LPDIRECTDRAW ddraw;
|
||||||
|
if (FAILED(func_DirectDrawCreate(NULL, &ddraw, NULL))) {
|
||||||
|
*p_version = 0;
|
||||||
|
*p_found = 0;
|
||||||
|
FreeLibrary(ddraw_module);
|
||||||
|
OutputDebugStringA("Couldn't create DDraw\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*p_version = 0x100;
|
||||||
|
LPDIRECTDRAW2 ddraw2;
|
||||||
|
if (FAILED(ddraw->QueryInterface(IID_IDirectDraw2, (LPVOID*) &ddraw2))) {
|
||||||
|
ddraw->Release();
|
||||||
|
FreeLibrary(ddraw_module);
|
||||||
|
OutputDebugStringA("Couldn't QI DDraw2\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ddraw->Release();
|
||||||
|
*p_version = 0x200;
|
||||||
|
HMODULE dinput_module = LoadLibraryA("DINPUT.DLL");
|
||||||
|
if (!dinput_module) {
|
||||||
|
OutputDebugStringA("Couldn't LoadLibrary DInput\r\n");
|
||||||
|
ddraw2->Release();
|
||||||
|
FreeLibrary(ddraw_module);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DirectInputCreateA_fn* func_DirectInputCreateA =
|
||||||
|
(DirectInputCreateA_fn*) GetProcAddress(dinput_module, "DirectInputCreateA");
|
||||||
|
FreeLibrary(dinput_module);
|
||||||
|
if (!func_DirectInputCreateA) {
|
||||||
|
FreeLibrary(ddraw_module);
|
||||||
|
ddraw2->Release();
|
||||||
|
OutputDebugStringA("Couldn't GetProcAddress DInputCreate\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*p_version = 0x300;
|
||||||
|
DDSURFACEDESC surface_desc;
|
||||||
|
memset(&surface_desc, 0, sizeof(surface_desc));
|
||||||
|
surface_desc.dwSize = sizeof(surface_desc);
|
||||||
|
surface_desc.dwFlags = DDSD_CAPS;
|
||||||
|
surface_desc.ddsCaps.dwCaps = DDCAPS2_NONLOCALVIDMEM;
|
||||||
|
if (FAILED(ddraw2->SetCooperativeLevel(NULL, DISCL_BACKGROUND))) {
|
||||||
|
ddraw2->Release();
|
||||||
|
FreeLibrary(ddraw_module);
|
||||||
|
*p_version = 0;
|
||||||
|
OutputDebugStringA("Couldn't Set coop level\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LPDIRECTDRAWSURFACE surface;
|
||||||
|
if (FAILED(ddraw2->CreateSurface(&surface_desc, &surface, NULL))) {
|
||||||
|
ddraw2->Release();
|
||||||
|
FreeLibrary(ddraw_module);
|
||||||
|
*p_version = 0;
|
||||||
|
OutputDebugStringA("Couldn't CreateSurface\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LPDIRECTDRAWSURFACE3 surface3;
|
||||||
|
if (FAILED(surface->QueryInterface(IID_IDirectDrawSurface3, (LPVOID*) &surface3))) {
|
||||||
|
ddraw2->Release();
|
||||||
|
FreeLibrary(ddraw_module);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*p_version = 0x500;
|
||||||
|
surface3->Release();
|
||||||
|
ddraw2->Release();
|
||||||
|
FreeLibrary(ddraw_module);
|
||||||
|
}
|
||||||
10
CONFIG_orig/detectdx5.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#if !defined(AFX_DETECTDX5_H)
|
||||||
|
#define AFX_DETECTDX5_H
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
extern BOOL DetectDirectX5();
|
||||||
|
|
||||||
|
extern void DetectDirectX(unsigned int* p_version, BOOL* p_found);
|
||||||
|
|
||||||
|
#endif // !defined(AFX_DETECTDX5_H)
|
||||||
BIN
CONFIG_orig/res/CONFIG.ico
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
CONFIG_orig/res/config.aps
Normal file
82
CONFIG_orig/res/config.rc
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#include "resource.h"
|
||||||
|
#include "afxres.h"
|
||||||
|
|
||||||
|
IDI_CONFIG ICON "lego.ico"
|
||||||
|
|
||||||
|
IDB_SHARK BITMAP "shark.bmp"
|
||||||
|
|
||||||
|
IDD_ABOUT DIALOG 0, 0, 217, 55
|
||||||
|
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
|
||||||
|
CAPTION "About Configure LEGO\xAE Island"
|
||||||
|
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
|
FONT 8, "MS Sans Serif"
|
||||||
|
BEGIN
|
||||||
|
CONTROL IDI_CONFIG, -1, "STATIC", SS_ICON | WS_CHILD | WS_VISIBLE, 11, 17, 18, 20
|
||||||
|
CONTROL "Configure LEGO Island Version 1.0", -1, "STATIC", SS_LEFT | SS_NOPREFIX | WS_CHILD | WS_VISIBLE | WS_GROUP, 40, 10, 119, 8
|
||||||
|
CONTROL "Copyright \xA9 1997 mindscape", -1, "STATIC", SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 40, 25, 119, 8
|
||||||
|
CONTROL "OK", IDOK, "BUTTON", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP, 178, 7, 32, 14
|
||||||
|
END
|
||||||
|
|
||||||
|
STRINGTABLE
|
||||||
|
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
|
BEGIN
|
||||||
|
IDS_ABOUT, "&About Config..."
|
||||||
|
END
|
||||||
|
|
||||||
|
IDD_MAIN DIALOGEX 0, 0, 289, 247
|
||||||
|
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
|
||||||
|
EXSTYLE WS_EX_APPWINDOW
|
||||||
|
CAPTION "Configure LEGO Island"
|
||||||
|
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||||
|
FONT 8, "MS Sans Serif"
|
||||||
|
BEGIN
|
||||||
|
CONTROL "OK", IDABORT, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 172, 125, 44, 14
|
||||||
|
CONTROL "Cancel", IDCANCEL, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 238, 125, 44, 14
|
||||||
|
CONTROL "Fast", IDC_RAD_MODEL_QUALITY_LOW, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 124, 18, 42, 10
|
||||||
|
CONTROL "High", IDC_RAD_MODEL_QUALITY_HIGH, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 192, 18, 69, 10
|
||||||
|
CONTROL "Fast", IDC_RAD_TEXTURE_QUALITY_LOW, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 124, 49, 43, 10
|
||||||
|
CONTROL "High", IDC_RAD_TEXTURE_QUALITY_HIGH, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 192, 49, 69, 10
|
||||||
|
CONTROL "256 Color", IDC_RAD_PALETTE_256, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 124, 80, 50, 10
|
||||||
|
CONTROL "High Color(16 bit)", IDC_RAD_PALETTE_16BIT, "BUTTON", BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE, 192, 80, 76, 10
|
||||||
|
CONTROL "Use Joystick", IDC_CHK_JOYSTICK, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 192, 104, 60, 10
|
||||||
|
CONTROL "", IDC_LIST_3DDEVICES, "LISTBOX", LBS_NOTIFY | LBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL, 115, 168, 161, 36 , WS_EX_TRANSPARENT
|
||||||
|
CONTROL "Flip Video Memory Pages", IDC_CHK_FLIP_VIDEO_MEM_PAGES, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, 116, 224, 96, 15
|
||||||
|
CONTROL "Draw 3D to Video Memory", IDC_CHK_3D_VIDEO_MEMORY, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, 116, 210, 99, 10
|
||||||
|
CONTROL "Color Palette", -1, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 107, 69, 175, 26
|
||||||
|
CONTROL "Direct 3D Devices", -1, "STATIC", SS_CENTER | WS_CHILD | WS_VISIBLE | WS_GROUP, 123, 158, 143, 10
|
||||||
|
CONTROL "3D Sound", IDC_CHK_3DSOUND, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, 220, 210, 45, 10
|
||||||
|
CONTROL "Island Model Quality", -1, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 107, 7, 175, 26
|
||||||
|
CONTROL "Island Texture Quality", -1, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 107, 38, 175, 26
|
||||||
|
CONTROL "Advanced Settings", IDC_GRP_ADVANCED, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 107, 147, 175, 93
|
||||||
|
CONTROL "Advanced", IDC_BTN_ADVANCED, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 109, 124, 44, 14
|
||||||
|
CONTROL IDB_SHARK, IDC_BMP_SHARK, "STATIC", SS_BITMAP | WS_CHILD | WS_VISIBLE, 7, 7, 83, 249
|
||||||
|
CONTROL "Draw Cursor", IDC_CHK_DRAW_CURSOR, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, 219, 227, 54, 10
|
||||||
|
CONTROL "Music", IDC_CHK_MUSIC, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 124, 104, 35, 10
|
||||||
|
END
|
||||||
|
|
||||||
|
1 VERSIONINFO
|
||||||
|
FILEVERSION 1,1,0,0
|
||||||
|
PRODUCTVERSION 1,1,0,0
|
||||||
|
FILEOS 0x4
|
||||||
|
FILETYPE 0x1
|
||||||
|
BEGIN
|
||||||
|
BLOCK "StringFileInfo"
|
||||||
|
BEGIN
|
||||||
|
BLOCK "040904b0"
|
||||||
|
BEGIN
|
||||||
|
VALUE "CompanyName", "Mindscape, Inc."
|
||||||
|
VALUE "FileDescription", "LEGOIsland & Configuration application"
|
||||||
|
VALUE "FileVersion", "1, 1, 0, 0"
|
||||||
|
VALUE "InternalName", "LEGOISLE.EXE"
|
||||||
|
VALUE "LegalCopyright", "Copyright \xA9 1997"
|
||||||
|
VALUE "OriginalFilename", "CONFIG.EXE"
|
||||||
|
VALUE "ProductName", "LEGO Island"
|
||||||
|
VALUE "ProductVersion", "1, 1, 0, 0"
|
||||||
|
END
|
||||||
|
END
|
||||||
|
|
||||||
|
BLOCK "VarFileInfo"
|
||||||
|
BEGIN
|
||||||
|
VALUE "Translation", 0x0409, 0x04B0 // U.S. English, Unicode
|
||||||
|
END
|
||||||
|
END
|
||||||
BIN
CONFIG_orig/res/lego.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
25
CONFIG_orig/res/resource.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#define IDI_CONFIG 128
|
||||||
|
|
||||||
|
#define IDD_ABOUT 100
|
||||||
|
#define IDS_ABOUT 101
|
||||||
|
|
||||||
|
#define IDD_MAIN 102
|
||||||
|
|
||||||
|
#define IDC_LIST_3DDEVICES 1000
|
||||||
|
#define IDC_CHK_FLIP_VIDEO_MEM_PAGES 1001
|
||||||
|
#define IDC_CHK_3D_VIDEO_MEMORY 1003
|
||||||
|
#define IDC_RAD_PALETTE_256 1004
|
||||||
|
#define IDC_RAD_PALETTE_16BIT 1005
|
||||||
|
#define IDC_CHK_3DSOUND 1006
|
||||||
|
#define IDC_CHK_DRAW_CURSOR 1008
|
||||||
|
#define IDC_RAD_MODEL_QUALITY_LOW 1010
|
||||||
|
#define IDC_RAD_MODEL_QUALITY_HIGH 1011
|
||||||
|
#define IDC_RAD_TEXTURE_QUALITY_LOW 1013
|
||||||
|
#define IDC_RAD_TEXTURE_QUALITY_HIGH 1014
|
||||||
|
#define IDC_CHK_JOYSTICK 1015
|
||||||
|
#define IDC_GRP_ADVANCED 1017
|
||||||
|
#define IDC_BTN_ADVANCED 1020
|
||||||
|
#define IDC_BMP_SHARK 1023
|
||||||
|
#define IDC_CHK_MUSIC 1024
|
||||||
|
|
||||||
|
#define IDB_SHARK 135
|
||||||
16
CONFIG_orig/res/resrc1.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//{{NO_DEPENDENCIES}}
|
||||||
|
// Microsoft Developer Studio generated include file.
|
||||||
|
// Used by config.rc
|
||||||
|
//
|
||||||
|
#define IDAPPLY 4
|
||||||
|
|
||||||
|
// Next default values for new objects
|
||||||
|
//
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
#define _APS_NEXT_RESOURCE_VALUE 101
|
||||||
|
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||||
|
#define _APS_NEXT_CONTROL_VALUE 1000
|
||||||
|
#define _APS_NEXT_SYMED_VALUE 101
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
BIN
CONFIG_orig/res/shark.bmp
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
ICON_RES_SOURCE/CONFIGraw.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
ICON_RES_SOURCE/TMAraw.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
218
ISLE/isleapp.cpp
@ -1,3 +1,17 @@
|
|||||||
|
//------------------------------------------------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// BE WARNED!!!
|
||||||
|
//
|
||||||
|
// YOU ARE STEPPING INTO THE TERRITORY OF DECOMPILED CODE VERSES A DUMBASS
|
||||||
|
//
|
||||||
|
// THERE WILL BE A LOT OF SHIT THAT IS STUPID - AND A LOT OF SHIT THAT CAN BE FIXED BUT I DONT KNOW HOW
|
||||||
|
//
|
||||||
|
// YOU HAVE BEEN WARNED
|
||||||
|
//
|
||||||
|
//------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ident - this file is post complete decomp, but has been modified to include the checks and updates from pre-complete decomp
|
||||||
|
|
||||||
#include "isleapp.h"
|
#include "isleapp.h"
|
||||||
|
|
||||||
#include "3dmanager/lego3dmanager.h"
|
#include "3dmanager/lego3dmanager.h"
|
||||||
@ -28,6 +42,9 @@
|
|||||||
#include "roi/legoroi.h"
|
#include "roi/legoroi.h"
|
||||||
#include "viewmanager/viewmanager.h"
|
#include "viewmanager/viewmanager.h"
|
||||||
|
|
||||||
|
// i shit you not, this was a thing that could've happened
|
||||||
|
//#include "discord-rpc/discord.h"
|
||||||
|
|
||||||
#include <dsound.h>
|
#include <dsound.h>
|
||||||
|
|
||||||
DECOMP_SIZE_ASSERT(IsleApp, 0x8c)
|
DECOMP_SIZE_ASSERT(IsleApp, 0x8c)
|
||||||
@ -66,10 +83,12 @@ int g_targetDepth = 16;
|
|||||||
BOOL g_reqEnableRMDevice = FALSE;
|
BOOL g_reqEnableRMDevice = FALSE;
|
||||||
|
|
||||||
// STRING: ISLE 0x4101c4
|
// STRING: ISLE 0x4101c4
|
||||||
#define WNDCLASS_NAME "Lego Island MainNoM App"
|
//#define WNDCLASS_NAME "Lego Island MainNoM App"
|
||||||
|
#define WNDCLASS_NAME "TMAOMNI PrimaryRuntimeWrapper"
|
||||||
|
|
||||||
// STRING: ISLE 0x4101dc
|
// STRING: ISLE 0x4101dc
|
||||||
#define WINDOW_TITLE "LEGO\xAE"
|
//#define WINDOW_TITLE "LEGO\xAE"
|
||||||
|
#define WINDOW_TITLE "LEGO Island: The Modder's Arrival"
|
||||||
|
|
||||||
// Might be static functions of IsleApp
|
// Might be static functions of IsleApp
|
||||||
BOOL FindExistingInstance();
|
BOOL FindExistingInstance();
|
||||||
@ -172,14 +191,28 @@ void IsleApp::Close()
|
|||||||
TickleManager()->Tickle();
|
TickleManager()->Tickle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Discord_Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*BOOL IsleApp::GetWorkingDir()
|
||||||
|
{
|
||||||
|
string s1 = GetCommandLine();
|
||||||
|
s1 = s1.substr(0, s1.find_last_of("\\/"));
|
||||||
|
//p_assign = s1.c_str();
|
||||||
|
const char* ret = s1.c_str();
|
||||||
|
if (!WriteReg("temp", ret))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
//return ret;
|
||||||
|
}*/
|
||||||
|
|
||||||
// FUNCTION: ISLE 0x4013b0
|
// FUNCTION: ISLE 0x4013b0
|
||||||
BOOL IsleApp::SetupLegoOmni()
|
BOOL IsleApp::SetupLegoOmni()
|
||||||
{
|
{
|
||||||
BOOL result = FALSE;
|
BOOL result = FALSE;
|
||||||
char mediaPath[256];
|
char mediaPath[256];
|
||||||
GetProfileStringA("LEGO Island", "MediaPath", "", mediaPath, sizeof(mediaPath));
|
GetProfileStringA("LEGO Island TMA", "MediaPath", "", mediaPath, sizeof(mediaPath));
|
||||||
|
|
||||||
#ifdef COMPAT_MODE
|
#ifdef COMPAT_MODE
|
||||||
BOOL failure;
|
BOOL failure;
|
||||||
@ -231,6 +264,31 @@ void IsleApp::SetupVideoFlags(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: FIXME
|
||||||
|
/*void InitDiscord()
|
||||||
|
{
|
||||||
|
DiscordEventHandlers handlers;
|
||||||
|
memset(&handlers, 0, sizeof(handlers));
|
||||||
|
handlers.ready = handleDiscordReady;
|
||||||
|
handlers.errored = handleDiscordError;
|
||||||
|
|
||||||
|
Discord_Initialize("1249977928561721357", &handlers, 1, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdatePresence()
|
||||||
|
{
|
||||||
|
char buffer[256];
|
||||||
|
DiscordRichPresence discordPresence;
|
||||||
|
memset(&discordPresence, 0, sizeof(discordPresence));
|
||||||
|
discordPresence.state = "Lego Island:";
|
||||||
|
//sprintf(buffer, "Ranked | Mode: %d", GameEngine.GetMode());
|
||||||
|
discordPresence.details = "The Modder's Arrival";
|
||||||
|
discordPresence.endTimestamp = time(0) + 5 * 60;
|
||||||
|
//discordPresence.largeImageKey = "img";
|
||||||
|
discordPresence.instance = 1;
|
||||||
|
Discord_UpdatePresence(&discordPresence);
|
||||||
|
}*/
|
||||||
|
|
||||||
// FUNCTION: ISLE 0x401610
|
// FUNCTION: ISLE 0x401610
|
||||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
|
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
|
||||||
{
|
{
|
||||||
@ -253,9 +311,9 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
if (!soundReady) {
|
if (!soundReady) {
|
||||||
MessageBoxA(
|
MessageBoxA(
|
||||||
NULL,
|
NULL,
|
||||||
"\"LEGO\xAE Island\" is not detecting a DirectSound compatible sound card. Please quit all other "
|
"\"LEGO Island: The Modder's Arrival\" failed to detect a DirectSound compatible sound card/driver. Please ensure any "
|
||||||
"applications and try again.",
|
"possibly interfering applications have been terminated before re-launching \"LEGO Island: The Modder's Arrival\".",
|
||||||
"Lego Island Error",
|
"Sound Card Failure!",
|
||||||
MB_OK
|
MB_OK
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
@ -264,12 +322,28 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
// Create global app instance
|
// Create global app instance
|
||||||
g_isle = new IsleApp();
|
g_isle = new IsleApp();
|
||||||
|
|
||||||
|
// Crash if config open. You wana break the game? Too Bad!!
|
||||||
|
HWND hWnd = FindWindowA(NULL, "Configuration for LEGO Island: The Modder's Arrival");
|
||||||
|
if (hWnd) {
|
||||||
|
MessageBoxA(
|
||||||
|
NULL,
|
||||||
|
"\"LEGO Island: The Modder's Arrival\" requests that you exit \"LEGO Island: The Modder's Arrival\" configuration dialogue "
|
||||||
|
"before continuing. This ensures that no unwanted behavior occurs by preventing you from changing configurations "
|
||||||
|
"while the game is running.",
|
||||||
|
"Please close Config!",
|
||||||
|
MB_OK
|
||||||
|
);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Create window
|
// Create window
|
||||||
|
// Flip Surfaces don't work in Windowed mode, crash.
|
||||||
if (g_isle->SetupWindow(hInstance, lpCmdLine) != SUCCESS) {
|
if (g_isle->SetupWindow(hInstance, lpCmdLine) != SUCCESS) {
|
||||||
MessageBoxA(
|
MessageBoxA(
|
||||||
NULL,
|
NULL,
|
||||||
"\"LEGO\xAE Island\" failed to start. Please quit all other applications and try again.",
|
"\"LEGO Island: The Modder's Arrival\" encountered an error while attempting to create the application window. Please ensure any possibly "
|
||||||
"LEGO\xAE Island Error",
|
"interfering applications have been terminated before re-launching \"LEGO Island: The Modder's Arrival\".",
|
||||||
|
"Failed to Create Window!",
|
||||||
MB_OK
|
MB_OK
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
@ -570,6 +644,7 @@ MxResult IsleApp::SetupWindow(HINSTANCE hInstance, LPSTR lpCmdLine)
|
|||||||
wndclass.hIcon = LoadIconA(hInstance, MAKEINTRESOURCEA(APP_ICON));
|
wndclass.hIcon = LoadIconA(hInstance, MAKEINTRESOURCEA(APP_ICON));
|
||||||
wndclass.hCursor = m_cursorArrow = m_cursorCurrent = LoadCursorA(hInstance, MAKEINTRESOURCEA(ISLE_ARROW));
|
wndclass.hCursor = m_cursorArrow = m_cursorCurrent = LoadCursorA(hInstance, MAKEINTRESOURCEA(ISLE_ARROW));
|
||||||
m_cursorBusy = LoadCursorA(hInstance, MAKEINTRESOURCEA(ISLE_BUSY));
|
m_cursorBusy = LoadCursorA(hInstance, MAKEINTRESOURCEA(ISLE_BUSY));
|
||||||
|
//m_cursorBusy = CreateAniCursor(ISLE_BUSY);
|
||||||
m_cursorNo = LoadCursorA(hInstance, MAKEINTRESOURCEA(ISLE_NO));
|
m_cursorNo = LoadCursorA(hInstance, MAKEINTRESOURCEA(ISLE_NO));
|
||||||
wndclass.hInstance = hInstance;
|
wndclass.hInstance = hInstance;
|
||||||
wndclass.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
|
wndclass.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);
|
||||||
@ -682,6 +757,34 @@ MxResult IsleApp::SetupWindow(HINSTANCE hInstance, LPSTR lpCmdLine)
|
|||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOL IsleApp::WriteReg(const char* p_key, const char* p_value) const
|
||||||
|
{
|
||||||
|
HKEY hKey;
|
||||||
|
DWORD pos;
|
||||||
|
|
||||||
|
if (RegCreateKeyExA(
|
||||||
|
HKEY_LOCAL_MACHINE,
|
||||||
|
"SOFTWARE\\ActionSoft\\LEGO Island TMA",
|
||||||
|
0,
|
||||||
|
"string",
|
||||||
|
0,
|
||||||
|
KEY_READ | KEY_WRITE,
|
||||||
|
NULL,
|
||||||
|
&hKey,
|
||||||
|
&pos
|
||||||
|
) == ERROR_SUCCESS) {
|
||||||
|
if (RegSetValueExA(hKey, p_key, 0, REG_SZ, (LPBYTE) p_value, strlen(p_value)) == ERROR_SUCCESS) {
|
||||||
|
if (RegCloseKey(hKey) == ERROR_SUCCESS) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RegCloseKey(hKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
// FUNCTION: ISLE 0x402740
|
// FUNCTION: ISLE 0x402740
|
||||||
BOOL IsleApp::ReadReg(LPCSTR name, LPSTR outValue, DWORD outSize)
|
BOOL IsleApp::ReadReg(LPCSTR name, LPSTR outValue, DWORD outSize)
|
||||||
{
|
{
|
||||||
@ -690,7 +793,7 @@ BOOL IsleApp::ReadReg(LPCSTR name, LPSTR outValue, DWORD outSize)
|
|||||||
|
|
||||||
BOOL out = FALSE;
|
BOOL out = FALSE;
|
||||||
DWORD size = outSize;
|
DWORD size = outSize;
|
||||||
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Mindscape\\LEGO Island", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\ActionSoft\\LEGO Island TMA", 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
||||||
if (RegQueryValueExA(hKey, name, NULL, &valueType, (LPBYTE) outValue, &size) == ERROR_SUCCESS) {
|
if (RegQueryValueExA(hKey, name, NULL, &valueType, (LPBYTE) outValue, &size) == ERROR_SUCCESS) {
|
||||||
if (RegCloseKey(hKey) == ERROR_SUCCESS) {
|
if (RegCloseKey(hKey) == ERROR_SUCCESS) {
|
||||||
out = TRUE;
|
out = TRUE;
|
||||||
@ -741,21 +844,86 @@ void IsleApp::LoadConfig()
|
|||||||
{
|
{
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
|
|
||||||
|
//big nasty fuckin' hack comin' up
|
||||||
|
//a hack which doesn't even work, FUCK
|
||||||
|
/*GetWorkingDir();
|
||||||
|
|
||||||
|
if (!ReadReg("temp", buffer, sizeof(buffer))) {
|
||||||
|
MessageBoxA(
|
||||||
|
NULL,
|
||||||
|
"\"LEGO Island: The Modder's Arrival\" encountered an error while attempting to aquire the current directory from temporary registry key. "
|
||||||
|
"Please restart the computer and/or contact support to resolve the issue.",
|
||||||
|
"Failed to Aquire Key: \"temp\"",
|
||||||
|
MB_OK
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
strcpy(m_curDirBase, buffer);
|
||||||
|
strcpy(m_curDir1, m_curDirBase);
|
||||||
|
strcat(m_curDir1, "\"");
|
||||||
|
|
||||||
|
if (!WriteReg("diskpath", m_curDirBase))
|
||||||
|
{
|
||||||
|
MessageBoxA(
|
||||||
|
NULL,
|
||||||
|
"\"LEGO Island: The Modder's Arrival\" encountered an error while attempting to assign the diskpath registry key. "
|
||||||
|
"Please restart the computer and/or contact support to resolve the issue.",
|
||||||
|
"Failed to Assign Key: \"diskpath\"",
|
||||||
|
MB_OK
|
||||||
|
);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
//We're not streaming off CD in this mod, return disk path for cd path
|
||||||
|
//if (!WriteReg("cdpath", GetWorkingDir()))
|
||||||
|
//{
|
||||||
|
// MessageBoxA(
|
||||||
|
// NULL,
|
||||||
|
// "\"LEGO Island: The Modder's Arrival\" encountered an error while attempting to assign the cdpath registry key. "
|
||||||
|
// "Please restart the computer and/or contact support to resolve the issue.",
|
||||||
|
// "Failed to Assign Key: \"cdpath\"",
|
||||||
|
// MB_OK
|
||||||
|
// );
|
||||||
|
//}
|
||||||
|
|
||||||
if (!ReadReg("diskpath", buffer, sizeof(buffer))) {
|
if (!ReadReg("diskpath", buffer, sizeof(buffer))) {
|
||||||
strcpy(buffer, MxOmni::GetHD());
|
//strcpy(buffer, MxOmni::GetHD());
|
||||||
|
MessageBoxA(
|
||||||
|
NULL,
|
||||||
|
"\"LEGO Island: The Modder's Arrival\" encountered an error while attempting to aquire the \"diskpath\" registry key. "
|
||||||
|
"Please ensure you followed the instructions in the README text document properly and relaunch the game.",
|
||||||
|
"Failed to Aquire Key: \"diskpath\"",
|
||||||
|
MB_OK
|
||||||
|
);
|
||||||
|
Close();
|
||||||
|
MxOmni::DestroyInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_hdPath = new char[strlen(buffer) + 1];
|
m_hdPath = new char[strlen(buffer) + 1];
|
||||||
strcpy(m_hdPath, buffer);
|
strcpy(m_hdPath, buffer);
|
||||||
MxOmni::SetHD(m_hdPath);
|
MxOmni::SetHD(m_hdPath);
|
||||||
|
MxOmni::SetCD(m_hdPath);
|
||||||
|
|
||||||
if (!ReadReg("cdpath", buffer, sizeof(buffer))) {
|
//if (!ReadReg("cdpath", buffer, sizeof(buffer))) {
|
||||||
strcpy(buffer, MxOmni::GetCD());
|
// strcpy(buffer, GetWorkingDir());
|
||||||
}
|
//}
|
||||||
|
|
||||||
m_cdPath = new char[strlen(buffer) + 1];
|
//if (!ReadReg("savepath", buffer, sizeof(buffer))) {
|
||||||
strcpy(m_cdPath, buffer);
|
// if (!WriteReg("savepath", MxOmni::GetHD() + "\\SAV"))
|
||||||
MxOmni::SetCD(m_cdPath);
|
// {
|
||||||
|
// MessageBoxA(
|
||||||
|
// NULL,
|
||||||
|
// "\"LEGO Island: The Modder's Arrival\" encountered an error while attempting to assign the savepath registry key. "
|
||||||
|
// "Please restart the computer and/or contact support to resolve the issue.",
|
||||||
|
// "Failed to Assign Key: \"savepath\"",
|
||||||
|
// MB_OK
|
||||||
|
// );
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//m_cdPath = new char[strlen(buffer) + 1];
|
||||||
|
//strcpy(m_cdPath, buffer);
|
||||||
|
//MxOmni::SetCD(m_cdPath);
|
||||||
|
|
||||||
ReadRegBool("Flip Surfaces", &m_flipSurfaces);
|
ReadRegBool("Flip Surfaces", &m_flipSurfaces);
|
||||||
ReadRegBool("Full Screen", &m_fullScreen);
|
ReadRegBool("Full Screen", &m_fullScreen);
|
||||||
@ -799,6 +967,23 @@ void IsleApp::LoadConfig()
|
|||||||
if (ReadReg("savepath", buffer, sizeof(buffer))) {
|
if (ReadReg("savepath", buffer, sizeof(buffer))) {
|
||||||
m_savePath = new char[strlen(buffer) + 1];
|
m_savePath = new char[strlen(buffer) + 1];
|
||||||
strcpy(m_savePath, buffer);
|
strcpy(m_savePath, buffer);
|
||||||
|
} else {
|
||||||
|
m_savePath = new char[strlen(buffer) + 1];
|
||||||
|
//strcpy(m_savePath, m_curDirBase);
|
||||||
|
strcpy(m_savePath, m_hdPath);
|
||||||
|
strcat(m_savePath, "\\SAV");
|
||||||
|
if (!WriteReg("savepath", m_savePath))
|
||||||
|
{
|
||||||
|
MessageBoxA(
|
||||||
|
NULL,
|
||||||
|
"\"LEGO Island: The Modder's Arrival\" encountered an error while attempting to assign the savepath registry key. "
|
||||||
|
"Please restart the computer and/or contact support to resolve the issue.",
|
||||||
|
"Failed to Assign Key: \"savepath\"",
|
||||||
|
MB_OK
|
||||||
|
);
|
||||||
|
Close();
|
||||||
|
MxOmni::DestroyInstance();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -853,6 +1038,7 @@ inline void IsleApp::Tick(BOOL sleepIfNotNextFrame)
|
|||||||
MxDSAction ds;
|
MxDSAction ds;
|
||||||
|
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
|
// since we have disabled cd drive functionality, this should only trigger if lego\scripts\isle\isle.si is missing
|
||||||
stream = Streamer()->Open("\\lego\\scripts\\nocd", MxStreamer::e_diskStream);
|
stream = Streamer()->Open("\\lego\\scripts\\nocd", MxStreamer::e_diskStream);
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -28,6 +28,7 @@ class IsleApp {
|
|||||||
);
|
);
|
||||||
MxResult SetupWindow(HINSTANCE hInstance, LPSTR lpCmdLine);
|
MxResult SetupWindow(HINSTANCE hInstance, LPSTR lpCmdLine);
|
||||||
|
|
||||||
|
BOOL WriteReg(const char* p_key, const char* p_value) const;
|
||||||
BOOL ReadReg(LPCSTR name, LPSTR outValue, DWORD outSize);
|
BOOL ReadReg(LPCSTR name, LPSTR outValue, DWORD outSize);
|
||||||
BOOL ReadRegBool(LPCSTR name, BOOL* out);
|
BOOL ReadRegBool(LPCSTR name, BOOL* out);
|
||||||
BOOL ReadRegInt(LPCSTR name, int* out);
|
BOOL ReadRegInt(LPCSTR name, int* out);
|
||||||
|
|||||||
BIN
ISLE/res/TMA.ico
Normal file
|
After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 326 B After Width: | Height: | Size: 1.2 KiB |
BIN
ISLE/res/busy.ani
Normal file
|
Before Width: | Height: | Size: 326 B After Width: | Height: | Size: 326 B |
BIN
ISLE/res/exclaim.cur
Normal file
|
After Width: | Height: | Size: 766 B |
@ -1,13 +1,21 @@
|
|||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
|
||||||
|
//Why in the fuck does this resource code not accept animated cursor icons (.ani)?
|
||||||
|
//What the fuck??
|
||||||
|
|
||||||
|
//3 days later - Hello Dipshit, the syntax for animated cursors is ANICURSOR, not CURSOR
|
||||||
|
//hope you found this helpful :3
|
||||||
|
|
||||||
ISLE_ARROW CURSOR "arrow.cur"
|
ISLE_ARROW CURSOR "arrow.cur"
|
||||||
ISLE_NO CURSOR "no.cur"
|
//ISLE_NO CURSOR "no.cur"
|
||||||
|
ISLE_NO CURSOR "exclaim.cur"
|
||||||
ISLE_BUSY CURSOR "busy.cur"
|
ISLE_BUSY CURSOR "busy.cur"
|
||||||
APP_ICON ICON "isle.ico"
|
//ISLE_BUSY ANICURSOR "busy.ani"
|
||||||
|
APP_ICON ICON "TMA.ico"
|
||||||
|
|
||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION 1,1,0,0
|
FILEVERSION 0,0,0,1
|
||||||
PRODUCTVERSION 1,1,0,0
|
PRODUCTVERSION 0,0,0,1
|
||||||
FILEOS 0x40004
|
FILEOS 0x40004
|
||||||
FILETYPE 0x1
|
FILETYPE 0x1
|
||||||
{
|
{
|
||||||
@ -16,14 +24,14 @@ BLOCK "StringFileInfo"
|
|||||||
BLOCK "040904b0"
|
BLOCK "040904b0"
|
||||||
{
|
{
|
||||||
VALUE "Comments", "DG JB AG RC EE"
|
VALUE "Comments", "DG JB AG RC EE"
|
||||||
VALUE "CompanyName", "Mindscape"
|
VALUE "CompanyName", "FUNNIMAN.EXE"
|
||||||
VALUE "FileDescription", "isle"
|
VALUE "FileDescription", "b_TMAOmni Library Launcher"
|
||||||
VALUE "FileVersion", "1, 1, 0, 0"
|
VALUE "FileVersion", "0, 0, 0, 1"
|
||||||
VALUE "InternalName", "isle"
|
VALUE "InternalName", "b_TMAOmni Library Launcher"
|
||||||
VALUE "LegalCopyright", "Copyright \xA9 1997"
|
VALUE "LegalCopyright", "Copyright \xA9 2025"
|
||||||
VALUE "OriginalFilename", "isle.exe"
|
VALUE "OriginalFilename", "ISLE_LAUNCHER.EXE"
|
||||||
VALUE "ProductName", "Adventures on LEGO Island"
|
VALUE "ProductName", "LEGO Island: The Modder's Arrival"
|
||||||
VALUE "ProductVersion", "1, 1, 0, 0"
|
VALUE "ProductVersion", "0, 0, 0, 1"
|
||||||
VALUE "SpecialBuild", "Very"
|
VALUE "SpecialBuild", "Very"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
ISLE/res/no.cur
|
Before Width: | Height: | Size: 326 B After Width: | Height: | Size: 766 B |
31
LEGO1/lego/legoomni/include/act3brickster.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef ACT3BRICKSTER_H
|
||||||
|
#define ACT3BRICKSTER_H
|
||||||
|
|
||||||
|
#include "act3actors.h"
|
||||||
|
|
||||||
|
// VTABLE: LEGO1 0x100d7838 LegoPathActor
|
||||||
|
// VTABLE: LEGO1 0x100d7908 LegoAnimActor
|
||||||
|
// SIZE 0x1b4
|
||||||
|
class Act3Brickster : public Act3Actor {
|
||||||
|
public:
|
||||||
|
Act3Brickster();
|
||||||
|
~Act3Brickster() override;
|
||||||
|
|
||||||
|
void ParseAction(char* p_extra) override; // vtable+0x20
|
||||||
|
void VTable0x70(float p_und) override; // vtable+0x70
|
||||||
|
MxResult VTable0x94(LegoPathActor*, MxBool) override; // vtable+0x94
|
||||||
|
void SwitchBoundary(
|
||||||
|
LegoPathBoundary*& p_boundary,
|
||||||
|
LegoUnknown100db7f4*& p_edge,
|
||||||
|
float& p_unk0xe4
|
||||||
|
) override; // vtable+0x98
|
||||||
|
MxResult VTable0x9c() override; // vtable+0x9c
|
||||||
|
|
||||||
|
// SYNTHETIC: LEGO1 0x10043250
|
||||||
|
// Act3Brickster::`scalar deleting destructor'
|
||||||
|
|
||||||
|
private:
|
||||||
|
undefined4 m_unk0x20[15]; // 0x20
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ACT3BRICKSTER_H
|
||||||
25
LEGO1/lego/legoomni/include/act3cop.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef ACT3COP_H
|
||||||
|
#define ACT3COP_H
|
||||||
|
|
||||||
|
#include "act3actors.h"
|
||||||
|
|
||||||
|
// VTABLE: LEGO1 0x100d7750 LegoPathActor
|
||||||
|
// VTABLE: LEGO1 0x100d7820 LegoAnimActor
|
||||||
|
// SIZE 0x188
|
||||||
|
class Act3Cop : public Act3Actor {
|
||||||
|
public:
|
||||||
|
Act3Cop();
|
||||||
|
|
||||||
|
void ParseAction(char* p_extra) override; // vtable+0x20
|
||||||
|
void VTable0x70(float p_und) override; // vtable+0x70
|
||||||
|
MxResult VTable0x94(LegoPathActor*, MxBool) override; // vtable+0x94
|
||||||
|
MxResult VTable0x9c() override; // vtable+0x9c
|
||||||
|
|
||||||
|
// SYNTHETIC: LEGO1 0x10043120
|
||||||
|
// Act3Cop::`scalar deleting destructor'
|
||||||
|
|
||||||
|
private:
|
||||||
|
undefined4 m_unk0x20[4]; // 0x20
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ACT3COP_H
|
||||||
42
LEGO1/lego/legoomni/include/act3shark.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#ifndef ACT3SHARK_H
|
||||||
|
#define ACT3SHARK_H
|
||||||
|
|
||||||
|
#include "legoanimactor.h"
|
||||||
|
|
||||||
|
// VTABLE: LEGO1 0x100d7920 LegoPathActor
|
||||||
|
// VTABLE: LEGO1 0x100d79f0 LegoAnimActor
|
||||||
|
// SIZE 0x1a8
|
||||||
|
class Act3Shark : public LegoAnimActor {
|
||||||
|
public:
|
||||||
|
Act3Shark();
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100430d0
|
||||||
|
const char* ClassName() const override // vtable+0x0c
|
||||||
|
{
|
||||||
|
// STRING: LEGO1 0x100f03a0
|
||||||
|
return "Act3Shark";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseAction(char*) override; // vtable+0x20
|
||||||
|
void VTable0x70(float p_float) override; // vtable+0x70
|
||||||
|
|
||||||
|
// LegoAnimActor vtable
|
||||||
|
virtual MxResult FUN_10042ce0(void*); // vtable+0x10
|
||||||
|
|
||||||
|
// SYNTHETIC: LEGO1 0x10043030
|
||||||
|
// Act3Shark::`scalar deleting destructor'
|
||||||
|
|
||||||
|
private:
|
||||||
|
list<void*> m_unk0x1c; // 0x1c
|
||||||
|
undefined4 m_unk0x28; // 0x28
|
||||||
|
undefined4 m_unk0x2c; // 0x2c
|
||||||
|
undefined m_unk0x30[0x0c]; // 0x30
|
||||||
|
Mx3DPointFloat m_unk0x3c; // 0x3c
|
||||||
|
};
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10042c90
|
||||||
|
// List<void *>::~List<void *>
|
||||||
|
// TODO: Update once type is known.
|
||||||
|
// STUB to resolve diff in scalar dtor and not create a new one.
|
||||||
|
|
||||||
|
#endif // ACT3SHARK_H
|
||||||
@ -91,10 +91,13 @@ class LegoCarBuild : public LegoWorld {
|
|||||||
LegoCarBuild();
|
LegoCarBuild();
|
||||||
~LegoCarBuild() override;
|
~LegoCarBuild() override;
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
// FUNCTION: LEGO1 0x10022930
|
// FUNCTION: LEGO1 0x10022930
|
||||||
// FUNCTION: BETA10 0x10070070
|
// FUNCTION: BETA10 0x10070070
|
||||||
MxBool VTable0x5c() override { return TRUE; } // vtable+0x5c
|
MxBool VTable0x5c() override { return TRUE; } // vtable+0x5c
|
||||||
|
|
||||||
|
>>>>>>> Stashed changes
|
||||||
// FUNCTION: LEGO1 0x10022940
|
// FUNCTION: LEGO1 0x10022940
|
||||||
// FUNCTION: BETA10 0x10070090
|
// FUNCTION: BETA10 0x10070090
|
||||||
const char* ClassName() const override // vtable+0x0c
|
const char* ClassName() const override // vtable+0x0c
|
||||||
@ -114,6 +117,10 @@ class LegoCarBuild : public LegoWorld {
|
|||||||
|
|
||||||
MxResult Create(MxDSAction& p_dsAction) override; // vtable+0x18
|
MxResult Create(MxDSAction& p_dsAction) override; // vtable+0x18
|
||||||
void ReadyWorld() override; // vtable+0x50
|
void ReadyWorld() override; // vtable+0x50
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
MxBool VTable0x5c() override; // vtable+0x5c
|
||||||
|
=======
|
||||||
|
>>>>>>> Stashed changes
|
||||||
MxBool Escape() override; // vtable+0x64
|
MxBool Escape() override; // vtable+0x64
|
||||||
void Enable(MxBool p_enable) override; // vtable+0x68
|
void Enable(MxBool p_enable) override; // vtable+0x68
|
||||||
virtual void VTable0x6c(); // vtable+0x6c
|
virtual void VTable0x6c(); // vtable+0x6c
|
||||||
|
|||||||
@ -2,7 +2,10 @@
|
|||||||
#define LEGOEXTRAACTOR_H
|
#define LEGOEXTRAACTOR_H
|
||||||
|
|
||||||
#include "legoanimactor.h"
|
#include "legoanimactor.h"
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
#include "legopathboundary.h"
|
#include "legopathboundary.h"
|
||||||
|
>>>>>>> Stashed changes
|
||||||
|
|
||||||
// VTABLE: LEGO1 0x100d6c00 LegoAnimActor
|
// VTABLE: LEGO1 0x100d6c00 LegoAnimActor
|
||||||
// VTABLE: LEGO1 0x100d6c10 LegoPathActor
|
// VTABLE: LEGO1 0x100d6c10 LegoPathActor
|
||||||
@ -37,9 +40,15 @@ class LegoExtraActor : public virtual LegoAnimActor {
|
|||||||
return !strcmp(p_name, LegoExtraActor::ClassName()) || LegoAnimActor::IsA(p_name);
|
return !strcmp(p_name, LegoExtraActor::ClassName()) || LegoAnimActor::IsA(p_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
void SetWorldSpeed(MxFloat p_worldSpeed) override; // vtable+0x30
|
||||||
|
MxS32 VTable0x68(Vector3&, Vector3&, Vector3&) override; // vtable+0x68
|
||||||
|
MxU32 VTable0x6c(
|
||||||
|
=======
|
||||||
void SetWorldSpeed(MxFloat p_worldSpeed) override; // vtable+0x30
|
void SetWorldSpeed(MxFloat p_worldSpeed) override; // vtable+0x30
|
||||||
MxS32 VTable0x68(Vector3& p_point1, Vector3& p_point2, Vector3& p_point3) override; // vtable+0x68
|
MxS32 VTable0x68(Vector3& p_point1, Vector3& p_point2, Vector3& p_point3) override; // vtable+0x68
|
||||||
inline MxU32 VTable0x6c(
|
inline MxU32 VTable0x6c(
|
||||||
|
>>>>>>> Stashed changes
|
||||||
LegoPathBoundary* p_boundary,
|
LegoPathBoundary* p_boundary,
|
||||||
Vector3& p_v1,
|
Vector3& p_v1,
|
||||||
Vector3& p_v2,
|
Vector3& p_v2,
|
||||||
@ -78,6 +87,8 @@ class LegoExtraActor : public virtual LegoAnimActor {
|
|||||||
LegoAnimActorStruct* m_disAnim; // 0x64
|
LegoAnimActorStruct* m_disAnim; // 0x64
|
||||||
};
|
};
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
// FUNCTION: LEGO1 0x1002b980
|
// FUNCTION: LEGO1 0x1002b980
|
||||||
inline MxU32 LegoExtraActor::VTable0x6c(
|
inline MxU32 LegoExtraActor::VTable0x6c(
|
||||||
LegoPathBoundary* p_boundary,
|
LegoPathBoundary* p_boundary,
|
||||||
@ -180,6 +191,7 @@ inline MxU32 LegoExtraActor::VTable0x6c(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
>>>>>>> Stashed changes
|
||||||
// GLOBAL: LEGO1 0x100d6be8
|
// GLOBAL: LEGO1 0x100d6be8
|
||||||
// LegoExtraActor::`vbtable'{for `LegoAnimActor'}
|
// LegoExtraActor::`vbtable'{for `LegoAnimActor'}
|
||||||
|
|
||||||
|
|||||||
70
LEGO1/lego/legoomni/include/legojetski.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#ifndef LEGOJETSKI_H
|
||||||
|
#define LEGOJETSKI_H
|
||||||
|
|
||||||
|
#include "legojetskiraceactor.h"
|
||||||
|
#include "legoracemap.h"
|
||||||
|
|
||||||
|
// VTABLE: LEGO1 0x100d5a08 LegoCarRaceActor
|
||||||
|
// VTABLE: LEGO1 0x100d5a28 LegoRaceActor
|
||||||
|
// VTABLE: LEGO1 0x100d5a30 LegoAnimActor
|
||||||
|
// VTABLE: LEGO1 0x100d5a40 LegoPathActor
|
||||||
|
// VTABLE: LEGO1 0x100d5b10 LegoRaceMap
|
||||||
|
// SIZE 0x1dc
|
||||||
|
class LegoJetski : public LegoJetskiRaceActor, public LegoRaceMap {
|
||||||
|
public:
|
||||||
|
LegoJetski();
|
||||||
|
~LegoJetski() override;
|
||||||
|
|
||||||
|
MxLong Notify(MxParam& p_param) override; // vtable+0x04
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x10013e90
|
||||||
|
const char* ClassName() const override // vtable+0x0c
|
||||||
|
{
|
||||||
|
// STRING: LEGO1 0x100f053c
|
||||||
|
return "LegoJetski";
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x10013eb0
|
||||||
|
MxBool IsA(const char* p_name) const override // vtable+0x10
|
||||||
|
{
|
||||||
|
return !strcmp(p_name, LegoJetski::ClassName()) || LegoJetskiRaceActor::IsA(p_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseAction(char*) override; // vtable+0x20
|
||||||
|
void SetWorldSpeed(MxFloat p_worldSpeed) override; // vtable+0x30
|
||||||
|
MxU32 VTable0x6c(
|
||||||
|
LegoPathBoundary* p_boundary,
|
||||||
|
Vector3& p_v1,
|
||||||
|
Vector3& p_v2,
|
||||||
|
float p_f1,
|
||||||
|
float p_f2,
|
||||||
|
Vector3& p_v3
|
||||||
|
) override; // vtable+0x6c
|
||||||
|
void VTable0x70(float p_float) override; // vtable+0x70
|
||||||
|
MxResult VTable0x94(LegoPathActor* p_actor, MxBool p_bool) override; // vtable+0x94
|
||||||
|
void SwitchBoundary(LegoPathBoundary*& p_boundary, LegoUnknown100db7f4*& p_edge, float& p_unk0xe4)
|
||||||
|
override; // vtable+0x98
|
||||||
|
MxResult VTable0x9c() override; // vtable+0x9c
|
||||||
|
|
||||||
|
virtual void FUN_100136f0(float p_worldSpeed);
|
||||||
|
|
||||||
|
// SYNTHETIC: LEGO1 0x10013e30
|
||||||
|
// LegoJetski::`scalar deleting destructor'
|
||||||
|
};
|
||||||
|
|
||||||
|
// GLOBAL: LEGO1 0x100d59b8
|
||||||
|
// LegoJetski::`vbtable'{for `LegoCarRaceActor'}
|
||||||
|
|
||||||
|
// GLOBAL: LEGO1 0x100d59c8
|
||||||
|
// LegoJetski::`vbtable'{for `LegoRaceActor'}
|
||||||
|
|
||||||
|
// GLOBAL: LEGO1 0x100d59d8
|
||||||
|
// LegoJetski::`vbtable'{for `LegoAnimActor'}
|
||||||
|
|
||||||
|
// GLOBAL: LEGO1 0x100d59e0
|
||||||
|
// LegoJetski::`vbtable'
|
||||||
|
|
||||||
|
// GLOBAL: LEGO1 0x100d59f0
|
||||||
|
// LegoJetski::`vbtable'{for `LegoJetskiRaceActor'}
|
||||||
|
|
||||||
|
#endif // LEGOJETSKI_H
|
||||||
@ -15,9 +15,12 @@ class RadioState : public LegoState {
|
|||||||
public:
|
public:
|
||||||
RadioState();
|
RadioState();
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
=======
|
||||||
// FUNCTION: LEGO1 0x1002cf50
|
// FUNCTION: LEGO1 0x1002cf50
|
||||||
MxBool IsSerializable() override { return FALSE; } // vtable+0x14
|
MxBool IsSerializable() override { return FALSE; } // vtable+0x14
|
||||||
|
|
||||||
|
>>>>>>> Stashed changes
|
||||||
// FUNCTION: LEGO1 0x1002cf60
|
// FUNCTION: LEGO1 0x1002cf60
|
||||||
// FUNCTION: BETA10 0x100f2850
|
// FUNCTION: BETA10 0x100f2850
|
||||||
const char* ClassName() const override // vtable+0x0c
|
const char* ClassName() const override // vtable+0x0c
|
||||||
@ -32,6 +35,11 @@ class RadioState : public LegoState {
|
|||||||
return !strcmp(p_name, RadioState::ClassName()) || LegoState::IsA(p_name);
|
return !strcmp(p_name, RadioState::ClassName()) || LegoState::IsA(p_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
MxBool IsSerializable() override; // vtable+0x14
|
||||||
|
|
||||||
|
=======
|
||||||
|
>>>>>>> Stashed changes
|
||||||
// SYNTHETIC: LEGO1 0x1002d020
|
// SYNTHETIC: LEGO1 0x1002d020
|
||||||
// RadioState::`scalar deleting destructor'
|
// RadioState::`scalar deleting destructor'
|
||||||
|
|
||||||
|
|||||||
47
LEGO1/lego/legoomni/src/actors/act3brickster.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "act3brickster.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(Act3Brickster, 0x1b4)
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10040e10
|
||||||
|
Act3Brickster::Act3Brickster()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x10040f20
|
||||||
|
Act3Brickster::~Act3Brickster()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10040ff0
|
||||||
|
void Act3Brickster::ParseAction(char* p_extra)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10041050
|
||||||
|
void Act3Brickster::VTable0x70(float p_und)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x100416b0
|
||||||
|
MxResult Act3Brickster::VTable0x94(LegoPathActor*, MxBool)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10042990
|
||||||
|
void Act3Brickster::SwitchBoundary(LegoPathBoundary*& p_boundary, LegoUnknown100db7f4*& p_edge, float& p_unk0xe4)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x100429d0
|
||||||
|
MxResult Act3Brickster::VTable0x9c()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
35
LEGO1/lego/legoomni/src/actors/act3cop.cpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#include "act3cop.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(Act3Cop, 0x188)
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x1003fe30
|
||||||
|
Act3Cop::Act3Cop()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x1003ff70
|
||||||
|
MxResult Act3Cop::VTable0x94(LegoPathActor*, MxBool)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10040060
|
||||||
|
void Act3Cop::ParseAction(char* p_extra)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x100401f0
|
||||||
|
void Act3Cop::VTable0x70(float p_und)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10040d20
|
||||||
|
MxResult Act3Cop::VTable0x9c()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
28
LEGO1/lego/legoomni/src/actors/act3shark.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#include "act3shark.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(Act3Shark, 0x1a8)
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10042ab0
|
||||||
|
Act3Shark::Act3Shark()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10042ce0
|
||||||
|
MxResult Act3Shark::FUN_10042ce0(void*)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10042d40
|
||||||
|
void Act3Shark::VTable0x70(float p_float)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10042f30
|
||||||
|
void Act3Shark::ParseAction(char*)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
@ -228,6 +228,15 @@ RadioState::RadioState()
|
|||||||
m_active = FALSE;
|
m_active = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
// FUNCTION: LEGO1 0x1002cf50
|
||||||
|
MxBool RadioState::IsSerializable()
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
=======
|
||||||
|
>>>>>>> Stashed changes
|
||||||
// FUNCTION: LEGO1 0x1002d090
|
// FUNCTION: LEGO1 0x1002d090
|
||||||
MxU32 RadioState::FUN_1002d090()
|
MxU32 RadioState::FUN_1002d090()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -130,6 +130,16 @@ LegoCarBuild::LegoCarBuild()
|
|||||||
NotificationManager()->Register(this);
|
NotificationManager()->Register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
// FUNCTION: LEGO1 0x10022930
|
||||||
|
// FUNCTION: BETA10 0x10070070
|
||||||
|
MxBool LegoCarBuild::VTable0x5c()
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
=======
|
||||||
|
>>>>>>> Stashed changes
|
||||||
// FUNCTION: LEGO1 0x10022a80
|
// FUNCTION: LEGO1 0x10022a80
|
||||||
// FUNCTION: BETA10 0x1006aea3
|
// FUNCTION: BETA10 0x1006aea3
|
||||||
LegoCarBuild::~LegoCarBuild()
|
LegoCarBuild::~LegoCarBuild()
|
||||||
|
|||||||
@ -661,6 +661,10 @@ MxBool LegoBuildingManager::FUN_10030110(LegoBuildingInfo* p_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FUNCTION: LEGO1 0x10030150
|
// FUNCTION: LEGO1 0x10030150
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
// FUNCTION: BETA10 0x100644ff
|
||||||
|
=======
|
||||||
|
>>>>>>> Stashed changes
|
||||||
void LegoBuildingManager::ScheduleAnimation(LegoEntity* p_entity, MxLong p_length, MxBool p_haveSound, MxBool p_unk0x28)
|
void LegoBuildingManager::ScheduleAnimation(LegoEntity* p_entity, MxLong p_length, MxBool p_haveSound, MxBool p_unk0x28)
|
||||||
{
|
{
|
||||||
m_world = CurrentWorld();
|
m_world = CurrentWorld();
|
||||||
|
|||||||
@ -618,11 +618,15 @@ void LegoPlantManager::ScheduleAnimation(LegoEntity* p_entity, MxLong p_length)
|
|||||||
|
|
||||||
entry->m_entity = p_entity;
|
entry->m_entity = p_entity;
|
||||||
entry->m_roi = p_entity->GetROI();
|
entry->m_roi = p_entity->GetROI();
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
entry->m_time = Timer()->GetTime() + p_length + 1000;
|
||||||
|
=======
|
||||||
|
|
||||||
MxLong time = Timer()->GetTime();
|
MxLong time = Timer()->GetTime();
|
||||||
time += p_length;
|
time += p_length;
|
||||||
entry->m_time = time + 1000;
|
entry->m_time = time + 1000;
|
||||||
|
|
||||||
|
>>>>>>> Stashed changes
|
||||||
FUN_100271b0(p_entity, -1);
|
FUN_100271b0(p_entity, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
90
LEGO1/lego/legoomni/src/entity/legojetski.cpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include "legojetski.h"
|
||||||
|
|
||||||
|
#include "mxmisc.h"
|
||||||
|
#include "mxnotificationmanager.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(LegoJetski, 0x1dc)
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x100136a0
|
||||||
|
void LegoJetski::SetWorldSpeed(MxFloat p_worldSpeed)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100136f0
|
||||||
|
void LegoJetski::FUN_100136f0(float p_worldSpeed)
|
||||||
|
{
|
||||||
|
if (p_worldSpeed < 0) {
|
||||||
|
LegoCarRaceActor::m_unk0x0c = 2;
|
||||||
|
m_maxLinearVel = 0;
|
||||||
|
SetWorldSpeed(0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_maxLinearVel = p_worldSpeed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10013740
|
||||||
|
void LegoJetski::VTable0x70(float p_float)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x10013820
|
||||||
|
LegoJetski::LegoJetski()
|
||||||
|
{
|
||||||
|
NotificationManager()->Register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x10013aa0
|
||||||
|
LegoJetski::~LegoJetski()
|
||||||
|
{
|
||||||
|
NotificationManager()->Unregister(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10013bb0
|
||||||
|
void LegoJetski::ParseAction(char*)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10013c30
|
||||||
|
MxLong LegoJetski::Notify(MxParam& p_param)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10013c40
|
||||||
|
MxResult LegoJetski::VTable0x94(LegoPathActor* p_actor, MxBool p_bool)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10014150
|
||||||
|
MxU32 LegoJetski::VTable0x6c(
|
||||||
|
LegoPathBoundary* p_boundary,
|
||||||
|
Vector3& p_v1,
|
||||||
|
Vector3& p_v2,
|
||||||
|
float p_f1,
|
||||||
|
float p_f2,
|
||||||
|
Vector3& p_v3
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x100141d0
|
||||||
|
void LegoJetski::SwitchBoundary(LegoPathBoundary*& p_boundary, LegoUnknown100db7f4*& p_edge, float& p_unk0xe4)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// STUB: LEGO1 0x10014210
|
||||||
|
MxResult LegoJetski::VTable0x9c()
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
@ -343,7 +343,7 @@ MxBool LegoNavController::CalculateNewPosDir(
|
|||||||
m_rotationalVel = CalculateNewVel(m_targetRotationalVel, m_rotationalVel, m_rotationalAccel * 40.0f, deltaTime);
|
m_rotationalVel = CalculateNewVel(m_targetRotationalVel, m_rotationalVel, m_rotationalAccel * 40.0f, deltaTime);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_rotationalVel = m_targetRotationalVel;
|
m_rotationalVel = ((m_targetRotationalVel * 25.0f) * deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_linearVel = CalculateNewVel(m_targetLinearVel, m_linearVel, m_linearAccel, deltaTime);
|
m_linearVel = CalculateNewVel(m_targetLinearVel, m_linearVel, m_linearAccel, deltaTime);
|
||||||
|
|||||||
@ -3,6 +3,10 @@
|
|||||||
#include "anim/legoanim.h"
|
#include "anim/legoanim.h"
|
||||||
#include "legocachesoundmanager.h"
|
#include "legocachesoundmanager.h"
|
||||||
#include "legolocomotionanimpresenter.h"
|
#include "legolocomotionanimpresenter.h"
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
#include "legopathboundary.h"
|
||||||
|
=======
|
||||||
|
>>>>>>> Stashed changes
|
||||||
#include "legosoundmanager.h"
|
#include "legosoundmanager.h"
|
||||||
#include "legoworld.h"
|
#include "legoworld.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
@ -420,3 +424,108 @@ MxS32 LegoExtraActor::VTable0x68(Vector3& p_point1, Vector3& p_point2, Vector3&
|
|||||||
{
|
{
|
||||||
return LegoPathActor::VTable0x68(p_point1, p_point2, p_point3);
|
return LegoPathActor::VTable0x68(p_point1, p_point2, p_point3);
|
||||||
}
|
}
|
||||||
|
<<<<<<< Updated upstream
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x1002b980
|
||||||
|
MxU32 LegoExtraActor::VTable0x6c(
|
||||||
|
LegoPathBoundary* p_boundary,
|
||||||
|
Vector3& p_v1,
|
||||||
|
Vector3& p_v2,
|
||||||
|
float p_f1,
|
||||||
|
float p_f2,
|
||||||
|
Vector3& p_v3
|
||||||
|
)
|
||||||
|
{
|
||||||
|
LegoAnimPresenterSet& presenters = p_boundary->GetPresenters();
|
||||||
|
|
||||||
|
for (LegoAnimPresenterSet::iterator itap = presenters.begin(); itap != presenters.end(); itap++) {
|
||||||
|
if ((*itap)->VTable0x94(p_v1, p_v2, p_f1, p_f2, p_v3)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LegoPathActorSet& plpas = p_boundary->GetActors();
|
||||||
|
LegoPathActorSet lpas(plpas);
|
||||||
|
|
||||||
|
for (LegoPathActorSet::iterator itpa = lpas.begin(); itpa != lpas.end(); itpa++) {
|
||||||
|
if (plpas.find(*itpa) != plpas.end()) {
|
||||||
|
LegoPathActor* actor = *itpa;
|
||||||
|
|
||||||
|
if (this != actor && !(actor->GetActorState() & LegoPathActor::c_noCollide)) {
|
||||||
|
LegoROI* roi = actor->GetROI();
|
||||||
|
|
||||||
|
if ((roi != NULL && roi->GetVisibility()) || actor->GetCameraFlag()) {
|
||||||
|
if (actor->GetUserNavFlag()) {
|
||||||
|
MxMatrix local2world = roi->GetLocal2World();
|
||||||
|
Vector3 local60(local2world[3]);
|
||||||
|
Mx3DPointFloat local54(p_v1);
|
||||||
|
|
||||||
|
local54 -= local60;
|
||||||
|
float local1c = p_v2.Dot(p_v2, p_v2);
|
||||||
|
float local24 = p_v2.Dot(p_v2, local54) * 2.0f;
|
||||||
|
float local20 = local54.Dot(local54, local54);
|
||||||
|
|
||||||
|
if (m_unk0x15 != 0 && local20 < 10.0f) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
local20 -= 1.0f;
|
||||||
|
|
||||||
|
if (local1c >= 0.001 || local1c <= -0.001) {
|
||||||
|
float local40 = (local24 * local24) + (local20 * local1c * -4.0f);
|
||||||
|
|
||||||
|
if (local40 >= -0.001) {
|
||||||
|
local1c *= 2.0f;
|
||||||
|
local24 = -local24;
|
||||||
|
|
||||||
|
if (local40 < 0.0f) {
|
||||||
|
local40 = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
local40 = sqrt(local40);
|
||||||
|
float local20X = (local24 + local40) / local1c;
|
||||||
|
float local1cX = (local24 - local40) / local1c;
|
||||||
|
|
||||||
|
if (local1cX < local20X) {
|
||||||
|
local40 = local20X;
|
||||||
|
local20X = local1cX;
|
||||||
|
local1cX = local40;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((local20X >= 0.0f && local20X <= p_f1) || (local1cX >= 0.0f && local1cX <= p_f1) ||
|
||||||
|
(local20X <= -0.01 && p_f1 + 0.01 <= local1cX)) {
|
||||||
|
p_v3 = p_v1;
|
||||||
|
|
||||||
|
if (HitActor(actor, TRUE) < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
actor->HitActor(this, FALSE);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (roi->FUN_100a9410(p_v1, p_v2, p_f1, p_f2, p_v3, m_collideBox && actor->GetCollideBox())) {
|
||||||
|
if (HitActor(actor, TRUE) < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
actor->HitActor(this, FALSE);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_unk0x15 != 0) {
|
||||||
|
m_unk0x15--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
=======
|
||||||
|
>>>>>>> Stashed changes
|
||||||
|
|||||||
19
LEGO1/lego/sources/geom/legobox.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "legobox.h"
|
||||||
|
|
||||||
|
#include "decomp.h"
|
||||||
|
#include "misc/legoutil.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(LegoBox, 0x18)
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100d3740
|
||||||
|
LegoResult LegoBox::Read(LegoStorage* p_storage)
|
||||||
|
{
|
||||||
|
LegoResult result;
|
||||||
|
if ((result = m_min.Read(p_storage)) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result = m_max.Read(p_storage)) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
32
LEGO1/lego/sources/geom/legobox.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef __LEGOBOX_H
|
||||||
|
#define __LEGOBOX_H
|
||||||
|
|
||||||
|
#include "legovertex.h"
|
||||||
|
|
||||||
|
// SIZE 0x18
|
||||||
|
class LegoBox {
|
||||||
|
public:
|
||||||
|
LegoVertex& GetMin() { return m_min; }
|
||||||
|
void SetMin(LegoVertex& p_min) { m_min = p_min; }
|
||||||
|
LegoVertex& GetMax() { return m_max; }
|
||||||
|
void SetMax(LegoVertex& p_max) { m_max = p_max; }
|
||||||
|
// LegoVertex GetCenter()
|
||||||
|
// {
|
||||||
|
// return LegoVertex(
|
||||||
|
// (m_min.GetX() + m_max.GetX()) / 2,
|
||||||
|
// (m_min.GetY() + m_max.GetY()) / 2,
|
||||||
|
// (m_min.GetZ() + m_max.GetZ()) / 2
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
LegoFloat GetDX() { return m_max.GetX() - m_min.GetX(); }
|
||||||
|
LegoFloat GetDY() { return m_max.GetY() - m_min.GetY(); }
|
||||||
|
LegoFloat GetDZ() { return m_max.GetZ() - m_min.GetZ(); }
|
||||||
|
LegoBool IsEmpty() { return m_min.IsOrigin() && m_max.IsOrigin(); }
|
||||||
|
LegoResult Read(LegoStorage* p_storage);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
LegoVertex m_min; // 0x00
|
||||||
|
LegoVertex m_max; // 0x0c
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __LEGOBOX_H
|
||||||
91
LEGO1/lego/sources/geom/legomesh.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#include "legomesh.h"
|
||||||
|
|
||||||
|
#include "misc/legostorage.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(LegoMeshUnkComponent, 0x1c)
|
||||||
|
DECOMP_SIZE_ASSERT(LegoMesh, 0x24)
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100d3810
|
||||||
|
LegoMesh::LegoMesh()
|
||||||
|
{
|
||||||
|
m_alpha = 0.0F;
|
||||||
|
m_shading = e_flat;
|
||||||
|
m_unk0x14 = 0;
|
||||||
|
m_textureName = NULL;
|
||||||
|
m_unk0x0d = 0;
|
||||||
|
m_unk0x10 = NULL;
|
||||||
|
m_unk0x20 = 0;
|
||||||
|
m_unk0x21 = FALSE;
|
||||||
|
m_materialName = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100d3860
|
||||||
|
LegoMesh::~LegoMesh()
|
||||||
|
{
|
||||||
|
if (m_textureName != NULL) {
|
||||||
|
delete[] m_textureName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_materialName != NULL) {
|
||||||
|
delete[] m_materialName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_unk0x10 != NULL) {
|
||||||
|
delete m_unk0x10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100d38f0
|
||||||
|
LegoResult LegoMesh::Read(LegoStorage* p_storage)
|
||||||
|
{
|
||||||
|
LegoResult result;
|
||||||
|
LegoU32 textureLength, materialLength;
|
||||||
|
if ((result = m_color.Read(p_storage)) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result = p_storage->Read(&m_alpha, sizeof(m_alpha))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result = p_storage->Read(&m_shading, sizeof(m_shading))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result = p_storage->Read(&m_unk0x0d, sizeof(m_unk0x0d))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result = p_storage->Read(&m_unk0x20, sizeof(m_unk0x20))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result = p_storage->Read(&m_unk0x21, sizeof(m_unk0x21))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result = p_storage->Read(&textureLength, sizeof(textureLength))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (textureLength) {
|
||||||
|
m_textureName = new LegoChar[textureLength + 1];
|
||||||
|
|
||||||
|
if ((result = p_storage->Read(m_textureName, textureLength)) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_textureName[textureLength] = '\0';
|
||||||
|
strlwr(m_textureName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((result = p_storage->Read(&materialLength, sizeof(materialLength))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (materialLength) {
|
||||||
|
m_materialName = new LegoChar[materialLength + 1];
|
||||||
|
|
||||||
|
if ((result = p_storage->Read(m_materialName, materialLength)) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_materialName[materialLength] = '\0';
|
||||||
|
strlwr(m_materialName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
78
LEGO1/lego/sources/geom/legomesh.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#ifndef __LEGOMESH_H
|
||||||
|
#define __LEGOMESH_H
|
||||||
|
|
||||||
|
#include "decomp.h"
|
||||||
|
#include "misc/legocolor.h"
|
||||||
|
#include "misc/legotypes.h"
|
||||||
|
|
||||||
|
class LegoStorage;
|
||||||
|
|
||||||
|
// SIZE 0x1c
|
||||||
|
struct LegoMeshUnkComponent {
|
||||||
|
~LegoMeshUnkComponent()
|
||||||
|
{
|
||||||
|
if (m_unk0x08) {
|
||||||
|
delete m_unk0x08;
|
||||||
|
}
|
||||||
|
if (m_unk0x0c) {
|
||||||
|
delete m_unk0x0c;
|
||||||
|
}
|
||||||
|
if (m_unk0x10) {
|
||||||
|
delete m_unk0x10;
|
||||||
|
}
|
||||||
|
if (m_unk0x14) {
|
||||||
|
delete m_unk0x14;
|
||||||
|
}
|
||||||
|
if (m_unk0x18) {
|
||||||
|
delete m_unk0x18;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
undefined m_unk0x00[8]; // 0x00
|
||||||
|
undefined* m_unk0x08; // 0x08
|
||||||
|
undefined* m_unk0x0c; // 0x0c
|
||||||
|
undefined* m_unk0x10; // 0x10
|
||||||
|
undefined* m_unk0x14; // 0x14
|
||||||
|
undefined* m_unk0x18; // 0x18
|
||||||
|
};
|
||||||
|
|
||||||
|
// VTABLE: LEGO1 0x100dd228
|
||||||
|
// SIZE 0x24
|
||||||
|
class LegoMesh {
|
||||||
|
public:
|
||||||
|
enum {
|
||||||
|
e_flat,
|
||||||
|
e_gouraud,
|
||||||
|
e_wireframe
|
||||||
|
};
|
||||||
|
|
||||||
|
LegoMesh();
|
||||||
|
virtual ~LegoMesh();
|
||||||
|
LegoColor GetColor() { return m_color; }
|
||||||
|
void SetColor(LegoColor p_color) { m_color = p_color; }
|
||||||
|
LegoFloat GetAlpha() { return m_alpha; }
|
||||||
|
LegoU8 GetShading() { return m_shading; }
|
||||||
|
void SetShading(LegoU8 p_shading) { m_shading = p_shading; }
|
||||||
|
LegoU8 GetUnknown0x0d() { return m_unk0x0d; }
|
||||||
|
const LegoChar* GetTextureName() { return m_textureName; }
|
||||||
|
const LegoChar* GetMaterialName() { return m_materialName; }
|
||||||
|
LegoBool GetUnknown0x21() { return m_unk0x21; }
|
||||||
|
LegoResult Read(LegoStorage* p_storage);
|
||||||
|
|
||||||
|
// SYNTHETIC: LEGO1 0x100d3840
|
||||||
|
// LegoMesh::`scalar deleting destructor'
|
||||||
|
|
||||||
|
protected:
|
||||||
|
LegoColor m_color; // 0x04
|
||||||
|
LegoFloat m_alpha; // 0x08
|
||||||
|
LegoU8 m_shading; // 0x0c
|
||||||
|
LegoU8 m_unk0x0d; // 0x0d
|
||||||
|
LegoMeshUnkComponent* m_unk0x10; // 0x10 - unused, except in destructor
|
||||||
|
undefined4 m_unk0x14; // 0x14 - unused
|
||||||
|
LegoChar* m_textureName; // 0x18
|
||||||
|
LegoChar* m_materialName; // 0x1c
|
||||||
|
undefined m_unk0x20; // 0x20 - unused
|
||||||
|
LegoBool m_unk0x21; // 0x21
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __LEGOMESH_H
|
||||||
19
LEGO1/lego/sources/geom/legosphere.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "legosphere.h"
|
||||||
|
|
||||||
|
#include "decomp.h"
|
||||||
|
#include "misc/legostorage.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(LegoSphere, 0x10)
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100d3770
|
||||||
|
LegoResult LegoSphere::Read(LegoStorage* p_storage)
|
||||||
|
{
|
||||||
|
LegoResult result;
|
||||||
|
if ((result = m_center.Read(p_storage)) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result = p_storage->Read(&m_radius, sizeof(m_radius))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
21
LEGO1/lego/sources/geom/legosphere.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef __LEGOSPHERE_H
|
||||||
|
#define __LEGOSPHERE_H
|
||||||
|
|
||||||
|
#include "legovertex.h"
|
||||||
|
|
||||||
|
// SIZE 0x10
|
||||||
|
class LegoSphere {
|
||||||
|
public:
|
||||||
|
LegoSphere() { m_radius = 0.0F; }
|
||||||
|
LegoVertex& GetCenter() { return m_center; }
|
||||||
|
void SetCenter(LegoVertex& p_center) { m_center = p_center; }
|
||||||
|
LegoFloat GetRadius() { return m_radius; }
|
||||||
|
void SetRadius(LegoFloat p_radius) { m_radius = p_radius; }
|
||||||
|
LegoResult Read(LegoStorage* p_storage);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
LegoVertex m_center; // 0x00
|
||||||
|
LegoFloat m_radius; // 0x0c
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __LEGOSPHERE_H
|
||||||
30
LEGO1/lego/sources/geom/legovertex.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include "legovertex.h"
|
||||||
|
|
||||||
|
#include "decomp.h"
|
||||||
|
#include "misc/legostorage.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(LegoVertex, 0x0c)
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100d37b0
|
||||||
|
LegoVertex::LegoVertex()
|
||||||
|
{
|
||||||
|
m_coordinates[0] = 0.0F;
|
||||||
|
m_coordinates[1] = 0.0F;
|
||||||
|
m_coordinates[2] = 0.0F;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100d37c0
|
||||||
|
LegoResult LegoVertex::Read(LegoStorage* p_storage)
|
||||||
|
{
|
||||||
|
LegoResult result;
|
||||||
|
if ((result = p_storage->Read(&m_coordinates[0], sizeof(m_coordinates[0]))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result = p_storage->Read(&m_coordinates[1], sizeof(m_coordinates[1]))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result = p_storage->Read(&m_coordinates[2], sizeof(m_coordinates[2]))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
30
LEGO1/lego/sources/geom/legovertex.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef __LEGOVERTEX_H
|
||||||
|
#define __LEGOVERTEX_H
|
||||||
|
|
||||||
|
#include "misc/legotypes.h"
|
||||||
|
|
||||||
|
class LegoStorage;
|
||||||
|
|
||||||
|
// SIZE 0x0c
|
||||||
|
class LegoVertex {
|
||||||
|
public:
|
||||||
|
LegoVertex();
|
||||||
|
LegoFloat GetCoordinate(LegoU32 p_i) { return m_coordinates[p_i]; }
|
||||||
|
void SetCoordinate(LegoU32 p_i, LegoFloat p_coordinate) { m_coordinates[p_i] = p_coordinate; }
|
||||||
|
LegoFloat GetX() { return m_coordinates[0]; }
|
||||||
|
void SetX(LegoFloat p_x) { m_coordinates[0] = p_x; }
|
||||||
|
LegoFloat GetY() { return m_coordinates[1]; }
|
||||||
|
void SetY(LegoFloat p_y) { m_coordinates[1] = p_y; }
|
||||||
|
LegoFloat GetZ() { return m_coordinates[2]; }
|
||||||
|
void SetZ(LegoFloat p_z) { m_coordinates[2] = p_z; }
|
||||||
|
LegoBool IsOrigin() { return m_coordinates[0] == 0.0 && m_coordinates[1] == 0.0 && m_coordinates[2] == 0.0; }
|
||||||
|
LegoResult Read(LegoStorage* p_storage);
|
||||||
|
|
||||||
|
LegoFloat& operator[](int i) { return m_coordinates[i]; }
|
||||||
|
LegoFloat operator[](int i) const { return m_coordinates[i]; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
LegoFloat m_coordinates[3]; // 0x00
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __LEGOVERTEX_H
|
||||||
22
LEGO1/lego/sources/misc/legocolor.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include "legocolor.h"
|
||||||
|
|
||||||
|
#include "decomp.h"
|
||||||
|
#include "legostorage.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(LegoColor, 0x03)
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100d3a20
|
||||||
|
LegoResult LegoColor::Read(LegoStorage* p_storage)
|
||||||
|
{
|
||||||
|
LegoResult result;
|
||||||
|
if ((result = p_storage->Read(&m_red, sizeof(m_red))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result = p_storage->Read(&m_green, sizeof(m_green))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if ((result = p_storage->Read(&m_blue, sizeof(m_blue))) != SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
26
LEGO1/lego/sources/misc/legocolor.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef __LEGOCOLOR_H
|
||||||
|
#define __LEGOCOLOR_H
|
||||||
|
|
||||||
|
#include "legotypes.h"
|
||||||
|
|
||||||
|
class LegoStorage;
|
||||||
|
|
||||||
|
// SIZE 0x03
|
||||||
|
class LegoColor {
|
||||||
|
public:
|
||||||
|
LegoColor() { m_red = m_green = m_blue = 0; }
|
||||||
|
LegoU8 GetRed() { return m_red; }
|
||||||
|
void SetRed(LegoU8 p_red) { m_red = p_red; }
|
||||||
|
LegoU8 GetGreen() { return m_green; }
|
||||||
|
void SetGreen(LegoU8 p_green) { m_green = p_green; }
|
||||||
|
LegoU8 GetBlue() { return m_blue; }
|
||||||
|
void SetBlue(LegoU8 p_blue) { m_blue = p_blue; }
|
||||||
|
LegoResult Read(LegoStorage* p_storage);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
LegoU8 m_red; // 0x00
|
||||||
|
LegoU8 m_green; // 0x01
|
||||||
|
LegoU8 m_blue; // 0x02
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __LEGOCOLOR_H
|
||||||
54
LEGO1/omni/include/mxsmack.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#ifndef MXSMACK_H
|
||||||
|
#define MXSMACK_H
|
||||||
|
|
||||||
|
#include "decomp.h"
|
||||||
|
#include "mxrectlist.h"
|
||||||
|
#include "mxtypes.h"
|
||||||
|
|
||||||
|
#include <smack.h>
|
||||||
|
|
||||||
|
struct MxBITMAPINFO;
|
||||||
|
|
||||||
|
// These functions are not part of the public interface,
|
||||||
|
// but present in SMACK.LIB and used directly by Mindscape.
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
u32 SmackGetSizeTables();
|
||||||
|
void SmackDoTables(
|
||||||
|
u8* p_huffmanTrees,
|
||||||
|
u8* p_huffmanTables,
|
||||||
|
u32 p_codeSize,
|
||||||
|
u32 p_abSize,
|
||||||
|
u32 p_detailSize,
|
||||||
|
u32 p_typeSize
|
||||||
|
);
|
||||||
|
void SmackDoFrameToBuffer(u8* p_source, u8* p_huffmanTables, u8* p_unk0x6b4);
|
||||||
|
u32 SmackGetSizeDeltas(u32 p_width, u32 p_height);
|
||||||
|
u8 SmackGetRect(u8* p_unk0x6b4, u32* p_rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SIZE 0x6b8
|
||||||
|
struct MxSmack {
|
||||||
|
SmackTag m_smackTag; // 0x00
|
||||||
|
undefined m_unk0x390[784]; // 0x390
|
||||||
|
MxU32* m_frameSizes; // 0x6a0
|
||||||
|
MxU8* m_frameTypes; // 0x6a4
|
||||||
|
MxU8* m_huffmanTrees; // 0x6a8
|
||||||
|
MxU8* m_huffmanTables; // 0x6ac
|
||||||
|
MxU32 m_maxFrameSize; // 0x6b0
|
||||||
|
MxU8* m_unk0x6b4; // 0x6b4
|
||||||
|
|
||||||
|
static MxResult LoadHeader(MxU8* p_data, MxSmack* p_mxSmack);
|
||||||
|
static void Destroy(MxSmack* p_mxSmack);
|
||||||
|
static MxResult LoadFrame(
|
||||||
|
MxBITMAPINFO* p_bitmapInfo,
|
||||||
|
MxU8* p_bitmapData,
|
||||||
|
MxSmack* p_mxSmack,
|
||||||
|
MxU8* p_chunkData,
|
||||||
|
MxBool p_paletteChanged,
|
||||||
|
MxRectList* p_list
|
||||||
|
);
|
||||||
|
static MxBool GetRect(MxU8* p_unk0x6b4, MxU16* p_und, u32* p_smackRect, MxRect32* p_rect);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MXSMACK_H
|
||||||
69
LEGO1/omni/include/mxstreamlist.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#ifndef MXSTREAMLIST_H
|
||||||
|
#define MXSTREAMLIST_H
|
||||||
|
|
||||||
|
#include "mxdsstreamingaction.h"
|
||||||
|
#include "mxdssubscriber.h"
|
||||||
|
#include "mxnextactiondatastart.h"
|
||||||
|
#include "mxstl/stlcompat.h"
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class MxStreamList : public list<T> {
|
||||||
|
public:
|
||||||
|
MxBool PopFront(T& p_obj)
|
||||||
|
{
|
||||||
|
if (this->empty()) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_obj = this->front();
|
||||||
|
this->pop_front();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// SIZE 0x0c
|
||||||
|
class MxStreamListMxDSAction : public MxStreamList<MxDSAction*> {
|
||||||
|
public:
|
||||||
|
// FUNCTION: BETA10 0x10150e30
|
||||||
|
MxDSAction* FindAndErase(MxDSAction* p_action) { return FindInternal(p_action, TRUE); }
|
||||||
|
|
||||||
|
// FUNCTION: BETA10 0x10150fc0
|
||||||
|
MxDSAction* Find(MxDSAction* p_action) { return FindInternal(p_action, FALSE); }
|
||||||
|
|
||||||
|
// There chance this list actually holds MxDSStreamingListAction
|
||||||
|
// instead of MxDSAction. Until then, we use this helper.
|
||||||
|
MxBool PopFrontStreamingAction(MxDSStreamingAction*& p_obj)
|
||||||
|
{
|
||||||
|
if (empty()) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_obj = (MxDSStreamingAction*) front();
|
||||||
|
pop_front();
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
MxDSAction* FindInternal(MxDSAction* p_action, MxBool p_delete);
|
||||||
|
};
|
||||||
|
|
||||||
|
// SIZE 0x0c
|
||||||
|
class MxStreamListMxNextActionDataStart : public MxStreamList<MxNextActionDataStart*> {
|
||||||
|
public:
|
||||||
|
MxNextActionDataStart* Find(MxU32 p_id, MxS16 p_value);
|
||||||
|
MxNextActionDataStart* FindAndErase(MxU32 p_id, MxS16 p_value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// SIZE 0x0c
|
||||||
|
class MxStreamListMxDSSubscriber : public MxStreamList<MxDSSubscriber*> {
|
||||||
|
public:
|
||||||
|
MxDSSubscriber* Find(MxDSObject* p_object);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TEMPLATE: BETA10 0x10150950
|
||||||
|
// MxStreamList<MxDSAction *>::PopFront
|
||||||
|
|
||||||
|
// TEMPLATE: BETA10 0x10150a70
|
||||||
|
// MxStreamList<MxDSSubscriber *>::PopFront
|
||||||
|
|
||||||
|
#endif // MXSTREAMLIST_H
|
||||||
23
LEGO1/omni/src/stream/mxdssource.cpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include "mxdssource.h"
|
||||||
|
|
||||||
|
#include "mxdsbuffer.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(MxDSSource, 0x14)
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100bffd0
|
||||||
|
MxResult MxDSSource::ReadToBuffer(MxDSBuffer* p_buffer)
|
||||||
|
{
|
||||||
|
return Read(p_buffer->GetBuffer(), p_buffer->GetWriteOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100bfff0
|
||||||
|
MxLong MxDSSource::GetLengthInDWords()
|
||||||
|
{
|
||||||
|
return m_lengthInDWords;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100c0000
|
||||||
|
MxU32* MxDSSource::GetBuffer()
|
||||||
|
{
|
||||||
|
return m_pBuffer;
|
||||||
|
}
|
||||||
83
LEGO1/omni/src/stream/mxstreamlist.cpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#include "mxstreamlist.h"
|
||||||
|
|
||||||
|
// Wrappers around STL list that are used by the MxStream* classes.
|
||||||
|
DECOMP_SIZE_ASSERT(MxStreamListMxDSAction, 0x0c);
|
||||||
|
DECOMP_SIZE_ASSERT(MxStreamListMxNextActionDataStart, 0x0c);
|
||||||
|
DECOMP_SIZE_ASSERT(MxStreamListMxDSSubscriber, 0x0c);
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100b8450
|
||||||
|
// FUNCTION: BETA10 0x10134c1d
|
||||||
|
MxDSSubscriber* MxStreamListMxDSSubscriber::Find(MxDSObject* p_object)
|
||||||
|
{
|
||||||
|
for (iterator it = begin(); it != end(); it++) {
|
||||||
|
if (p_object->GetObjectId() == -1 || p_object->GetObjectId() == (*it)->GetObjectId()) {
|
||||||
|
if (p_object->GetUnknown24() == -2 || p_object->GetUnknown24() == (*it)->GetUnknown48()) {
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100bfa80
|
||||||
|
// FUNCTION: BETA10 0x10147e02
|
||||||
|
MxDSAction* MxStreamListMxDSAction::FindInternal(MxDSAction* p_action, MxBool p_delete)
|
||||||
|
{
|
||||||
|
// DECOMP ALPHA 0x1008b99d ?
|
||||||
|
|
||||||
|
MxDSAction* found = NULL;
|
||||||
|
|
||||||
|
#ifdef COMPAT_MODE
|
||||||
|
iterator it;
|
||||||
|
for (it = begin(); it != end(); it++) {
|
||||||
|
#else
|
||||||
|
for (iterator it = begin(); it != end(); it++) {
|
||||||
|
#endif
|
||||||
|
if (p_action->GetObjectId() == -1 || p_action->GetObjectId() == (*it)->GetObjectId()) {
|
||||||
|
if (p_action->GetUnknown24() == -2 || p_action->GetUnknown24() == -3 ||
|
||||||
|
p_action->GetUnknown24() == (*it)->GetUnknown24()) {
|
||||||
|
found = *it;
|
||||||
|
if (p_action->GetUnknown24() != -3) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_delete && found != NULL) {
|
||||||
|
erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100c21e0
|
||||||
|
// FUNCTION: BETA10 0x1014f4e6
|
||||||
|
MxNextActionDataStart* MxStreamListMxNextActionDataStart::Find(MxU32 p_id, MxS16 p_value)
|
||||||
|
{
|
||||||
|
for (iterator it = begin(); it != end(); it++) {
|
||||||
|
if (p_id == (*it)->GetObjectId() && p_value == (*it)->GetUnknown24()) {
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100c2240
|
||||||
|
// FUNCTION: BETA10 0x1014f58c
|
||||||
|
MxNextActionDataStart* MxStreamListMxNextActionDataStart::FindAndErase(MxU32 p_id, MxS16 p_value)
|
||||||
|
{
|
||||||
|
MxNextActionDataStart* match = NULL;
|
||||||
|
|
||||||
|
for (iterator it = begin(); it != end(); it++) {
|
||||||
|
if (p_id == (*it)->GetObjectId() && (p_value == -2 || p_value == (*it)->GetUnknown24())) {
|
||||||
|
match = *it;
|
||||||
|
erase(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
17
LEGO1/omni/src/stream/mxstreamprovider.cpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "mxstreamprovider.h"
|
||||||
|
|
||||||
|
#include "decomp.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(MxStreamProvider, 0x10);
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100d07c0
|
||||||
|
MxResult MxStreamProvider::SetResourceToGet(MxStreamController* p_resource)
|
||||||
|
{
|
||||||
|
m_pLookup = p_resource;
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100d07d0
|
||||||
|
void MxStreamProvider::VTable0x20(MxDSAction* p_action)
|
||||||
|
{
|
||||||
|
}
|
||||||
270
LEGO1/omni/src/video/mxsmack.cpp
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
#include "mxsmack.h"
|
||||||
|
|
||||||
|
#include "mxbitmap.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(SmackTag, 0x390);
|
||||||
|
DECOMP_SIZE_ASSERT(MxSmack, 0x6b8);
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100c5a90
|
||||||
|
// FUNCTION: BETA10 0x10151e70
|
||||||
|
MxResult MxSmack::LoadHeader(MxU8* p_data, MxSmack* p_mxSmack)
|
||||||
|
{
|
||||||
|
// Macros for readability
|
||||||
|
// If bit0 of SmackerType is set, there is an extra frame ("ring frame")
|
||||||
|
// at the end. It is a duplicate of the first frame to simplify looping.
|
||||||
|
#define FRAME_COUNT(_tag) (_tag->Frames + (_tag->SmackerType & 1))
|
||||||
|
|
||||||
|
MxResult result = SUCCESS;
|
||||||
|
MxU32* frameSizes = NULL;
|
||||||
|
MxU8* frameTypes = NULL;
|
||||||
|
MxU8* huffmanTrees = NULL;
|
||||||
|
MxU32 sizetables = 0;
|
||||||
|
|
||||||
|
// Forced to declare here because of the gotos.
|
||||||
|
MxU32 i;
|
||||||
|
MxU32 treeSize;
|
||||||
|
MxU32* data;
|
||||||
|
MxU32 size;
|
||||||
|
MxS32 width;
|
||||||
|
|
||||||
|
if (!p_data || !p_mxSmack) {
|
||||||
|
return FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmackTag* smackTag = &p_mxSmack->m_smackTag;
|
||||||
|
p_mxSmack->m_frameTypes = NULL;
|
||||||
|
p_mxSmack->m_frameSizes = NULL;
|
||||||
|
p_mxSmack->m_huffmanTrees = NULL;
|
||||||
|
p_mxSmack->m_huffmanTables = NULL;
|
||||||
|
|
||||||
|
memcpy(smackTag, p_data, SmackHeaderSize(smackTag));
|
||||||
|
p_data += SmackHeaderSize(smackTag);
|
||||||
|
|
||||||
|
frameSizes = new MxU32[FRAME_COUNT(smackTag)];
|
||||||
|
|
||||||
|
if (!frameSizes) {
|
||||||
|
result = FAILURE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(frameSizes, p_data, FRAME_COUNT(smackTag) * sizeof(MxU32));
|
||||||
|
|
||||||
|
p_data += FRAME_COUNT(smackTag) * sizeof(MxU32);
|
||||||
|
p_mxSmack->m_maxFrameSize = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < FRAME_COUNT(smackTag); i++) {
|
||||||
|
if (p_mxSmack->m_maxFrameSize < frameSizes[i]) {
|
||||||
|
p_mxSmack->m_maxFrameSize = frameSizes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frameTypes = new MxU8[FRAME_COUNT(smackTag)];
|
||||||
|
|
||||||
|
if (!frameTypes) {
|
||||||
|
result = FAILURE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(frameTypes, p_data, FRAME_COUNT(smackTag));
|
||||||
|
p_data += FRAME_COUNT(smackTag);
|
||||||
|
|
||||||
|
treeSize = smackTag->tablesize + 0x1000;
|
||||||
|
huffmanTrees = new MxU8[treeSize <= 0x2000 ? 0x2000 : treeSize];
|
||||||
|
|
||||||
|
if (!huffmanTrees) {
|
||||||
|
result = FAILURE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(huffmanTrees + 0x1000, p_data, smackTag->tablesize);
|
||||||
|
p_data += smackTag->tablesize;
|
||||||
|
|
||||||
|
sizetables = SmackGetSizeTables();
|
||||||
|
p_mxSmack->m_huffmanTables =
|
||||||
|
new MxU8[smackTag->codesize + smackTag->detailsize + smackTag->typesize + smackTag->absize + sizetables];
|
||||||
|
|
||||||
|
if (!p_mxSmack->m_huffmanTables) {
|
||||||
|
result = FAILURE;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmackDoTables(
|
||||||
|
huffmanTrees,
|
||||||
|
p_mxSmack->m_huffmanTables,
|
||||||
|
smackTag->codesize,
|
||||||
|
smackTag->absize,
|
||||||
|
smackTag->detailsize,
|
||||||
|
smackTag->typesize
|
||||||
|
);
|
||||||
|
|
||||||
|
size = SmackGetSizeDeltas(smackTag->Width, smackTag->Height) + 32;
|
||||||
|
p_mxSmack->m_unk0x6b4 = new MxU8[size];
|
||||||
|
memset(p_mxSmack->m_unk0x6b4, 0, size);
|
||||||
|
|
||||||
|
width = p_mxSmack->m_smackTag.Width;
|
||||||
|
data = (MxU32*) p_mxSmack->m_unk0x6b4;
|
||||||
|
|
||||||
|
*data = 1;
|
||||||
|
data++;
|
||||||
|
*data = NULL; // MxU8* bitmapData
|
||||||
|
data++;
|
||||||
|
*data = smackTag->Width / 4;
|
||||||
|
data++;
|
||||||
|
*data = smackTag->Height / 4;
|
||||||
|
data++;
|
||||||
|
*data = width - 4;
|
||||||
|
data++;
|
||||||
|
*data = width * 3;
|
||||||
|
data++;
|
||||||
|
*data = width;
|
||||||
|
data++;
|
||||||
|
*data = width * 3 + (width - smackTag->Width);
|
||||||
|
data++;
|
||||||
|
data++;
|
||||||
|
*data = smackTag->Width;
|
||||||
|
data++;
|
||||||
|
*data = smackTag->Height;
|
||||||
|
|
||||||
|
done:
|
||||||
|
p_mxSmack->m_frameTypes = frameTypes;
|
||||||
|
p_mxSmack->m_frameSizes = frameSizes;
|
||||||
|
p_mxSmack->m_huffmanTrees = huffmanTrees;
|
||||||
|
return result;
|
||||||
|
|
||||||
|
#undef FRAME_COUNT
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100c5d40
|
||||||
|
// FUNCTION: BETA10 0x10152298
|
||||||
|
void MxSmack::Destroy(MxSmack* p_mxSmack)
|
||||||
|
{
|
||||||
|
if (p_mxSmack->m_frameSizes) {
|
||||||
|
delete[] p_mxSmack->m_frameSizes;
|
||||||
|
}
|
||||||
|
if (p_mxSmack->m_frameTypes) {
|
||||||
|
delete[] p_mxSmack->m_frameTypes;
|
||||||
|
}
|
||||||
|
if (p_mxSmack->m_huffmanTrees) {
|
||||||
|
delete[] p_mxSmack->m_huffmanTrees;
|
||||||
|
}
|
||||||
|
if (p_mxSmack->m_huffmanTables) {
|
||||||
|
delete[] p_mxSmack->m_huffmanTables;
|
||||||
|
}
|
||||||
|
if (p_mxSmack->m_unk0x6b4) {
|
||||||
|
delete[] p_mxSmack->m_unk0x6b4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100c5db0
|
||||||
|
// FUNCTION: BETA10 0x10152391
|
||||||
|
MxResult MxSmack::LoadFrame(
|
||||||
|
MxBITMAPINFO* p_bitmapInfo,
|
||||||
|
MxU8* p_bitmapData,
|
||||||
|
MxSmack* p_mxSmack,
|
||||||
|
MxU8* p_chunkData,
|
||||||
|
MxBool p_paletteChanged,
|
||||||
|
MxRectList* p_list
|
||||||
|
)
|
||||||
|
{
|
||||||
|
p_bitmapInfo->m_bmiHeader.biHeight = -MxBitmap::HeightAbs(p_bitmapInfo->m_bmiHeader.biHeight);
|
||||||
|
*(MxU8**) (p_mxSmack->m_unk0x6b4 + 4) = p_bitmapData;
|
||||||
|
|
||||||
|
// Reference: https://wiki.multimedia.cx/index.php/Smacker#Palette_Chunk
|
||||||
|
if (p_paletteChanged) {
|
||||||
|
MxU8 palette[772];
|
||||||
|
|
||||||
|
MxU8* intoChunk = p_chunkData + 1;
|
||||||
|
MxU8* intoPalette = palette;
|
||||||
|
MxU16 paletteIndex = 0;
|
||||||
|
// TODO: struct incorrect, Palette at wrong offset?
|
||||||
|
MxU8* currentPalette = &p_mxSmack->m_smackTag.Palette[4];
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (*intoChunk & 0x80) {
|
||||||
|
MxU8 length = (*intoChunk & 0x7f) + 1;
|
||||||
|
memcpy(intoPalette, ¤tPalette[paletteIndex * 3], length * 3);
|
||||||
|
intoPalette += length * 3;
|
||||||
|
paletteIndex += length;
|
||||||
|
intoChunk++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (*intoChunk & 0x40) {
|
||||||
|
MxU8 length = (*intoChunk & 0x3f) + 1;
|
||||||
|
memcpy(intoPalette, ¤tPalette[*(intoChunk + 1) * 3], length * 3);
|
||||||
|
intoPalette += length * 3;
|
||||||
|
paletteIndex += length;
|
||||||
|
intoChunk += 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*(MxU32*) intoPalette = *(MxU32*) intoChunk;
|
||||||
|
intoPalette += 3;
|
||||||
|
paletteIndex++;
|
||||||
|
intoChunk += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (paletteIndex < 256);
|
||||||
|
|
||||||
|
for (MxU32 i = 0; i < 256; i++) {
|
||||||
|
memcpy(currentPalette, &palette[i * 3], 3);
|
||||||
|
currentPalette += 3;
|
||||||
|
p_bitmapInfo->m_bmiColors[i].rgbBlue = palette[i * 3 + 2] * 4;
|
||||||
|
p_bitmapInfo->m_bmiColors[i].rgbGreen = palette[i * 3 + 1] * 4;
|
||||||
|
p_bitmapInfo->m_bmiColors[i].rgbRed = palette[i * 3] * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_chunkData += *p_chunkData * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
SmackDoFrameToBuffer(p_chunkData, p_mxSmack->m_huffmanTables, p_mxSmack->m_unk0x6b4);
|
||||||
|
|
||||||
|
MxU16 und = 1;
|
||||||
|
u32 smackRect[4];
|
||||||
|
MxRect32 rect;
|
||||||
|
|
||||||
|
while (GetRect(p_mxSmack->m_unk0x6b4, &und, smackRect, &rect)) {
|
||||||
|
MxRect32* newRect = new MxRect32(rect);
|
||||||
|
p_list->Append(newRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FUNCTION: LEGO1 0x100c6050
|
||||||
|
// FUNCTION: BETA10 0x10152739
|
||||||
|
MxBool MxSmack::GetRect(MxU8* p_unk0x6b4, MxU16* p_und, u32* p_smackRect, MxRect32* p_rect)
|
||||||
|
{
|
||||||
|
u32 left, bottom, top, right;
|
||||||
|
|
||||||
|
if (!*p_und) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*p_und == 1) {
|
||||||
|
if (!SmackGetRect(p_unk0x6b4, p_smackRect)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
*p_und = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
left = p_smackRect[0];
|
||||||
|
top = p_smackRect[1];
|
||||||
|
right = p_smackRect[2] + p_smackRect[0];
|
||||||
|
bottom = p_smackRect[3] + p_smackRect[1];
|
||||||
|
|
||||||
|
while (SmackGetRect(p_unk0x6b4, p_smackRect)) {
|
||||||
|
if (left > p_smackRect[0]) {
|
||||||
|
left = p_smackRect[0];
|
||||||
|
}
|
||||||
|
if (right < p_smackRect[0] + p_smackRect[2]) {
|
||||||
|
right = p_smackRect[0] + p_smackRect[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
bottom = p_smackRect[1] + p_smackRect[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
*p_und = 0;
|
||||||
|
*p_rect = MxRect32(left, top, right, bottom);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
5
LEGO1/realtime/matrix.cpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#include "matrix.h"
|
||||||
|
|
||||||
|
#include "decomp.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(Matrix4, 0x08);
|
||||||
7
LEGO1/realtime/vector.cpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#include "vector.h"
|
||||||
|
|
||||||
|
#include "decomp.h"
|
||||||
|
|
||||||
|
DECOMP_SIZE_ASSERT(Vector2, 0x08);
|
||||||
|
DECOMP_SIZE_ASSERT(Vector3, 0x08);
|
||||||
|
DECOMP_SIZE_ASSERT(Vector4, 0x08);
|
||||||
@ -12,8 +12,8 @@ LEGO1_ARROW CURSOR "arrow.cur"
|
|||||||
LEGO1_BUSY CURSOR "busy.cur"
|
LEGO1_BUSY CURSOR "busy.cur"
|
||||||
|
|
||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION 1,1,0,0
|
FILEVERSION 0,0,0,1
|
||||||
PRODUCTVERSION 1,1,0,0
|
PRODUCTVERSION 0,0,0,1
|
||||||
FILEOS 0x40004
|
FILEOS 0x40004
|
||||||
FILETYPE 0x2
|
FILETYPE 0x2
|
||||||
{
|
{
|
||||||
@ -21,14 +21,14 @@ BLOCK "StringFileInfo"
|
|||||||
{
|
{
|
||||||
BLOCK "040904b0"
|
BLOCK "040904b0"
|
||||||
{
|
{
|
||||||
VALUE "CompanyName", "Mindscape, Inc."
|
VALUE "CompanyName", "FUNNIMAN.EXE"
|
||||||
VALUE "FileDescription", "LegoOmni Library"
|
VALUE "FileDescription", "b_TMAOmni Library"
|
||||||
VALUE "FileVersion", "1, 1, 0, 0"
|
VALUE "FileVersion", "0, 0, 0, 1"
|
||||||
VALUE "InternalName", "LegoOmni Library"
|
VALUE "InternalName", "b_TMAOmni Library"
|
||||||
VALUE "LegalCopyright", "Copyright \xA9 1997"
|
VALUE "LegalCopyright", "Copyright \xA9 2025"
|
||||||
VALUE "OriginalFilename", "LegoOmni Library"
|
VALUE "OriginalFilename", "B_TMA.DLL"
|
||||||
VALUE "ProductName", "LegoOmni Library"
|
VALUE "ProductName", "LEGO Island: The Modder's Arrival"
|
||||||
VALUE "ProductVersion", "1, 1, 0, 0"
|
VALUE "ProductVersion", "0, 0, 0, 1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
39
LEGO1/res/lego1_beta.rc
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include "resource.h"
|
||||||
|
|
||||||
|
LEGO1_LEAF4 CURSOR "leaf_4.cur"
|
||||||
|
LEGO1_LEAF7 CURSOR "leaf_7.cur"
|
||||||
|
LEGO1_LEAF1 CURSOR "leaf_1.cur"
|
||||||
|
LEGO1_LEAF2 CURSOR "leaf_2.cur"
|
||||||
|
LEGO1_LEAF6 CURSOR "leaf_6.cur"
|
||||||
|
LEGO1_LEAF0 CURSOR "leaf_0.cur"
|
||||||
|
LEGO1_LEAF5 CURSOR "leaf_5.cur"
|
||||||
|
LEGO1_LEAF3 CURSOR "leaf_3.cur"
|
||||||
|
LEGO1_ARROW CURSOR "arrow.cur"
|
||||||
|
LEGO1_BUSY CURSOR "busy.cur"
|
||||||
|
|
||||||
|
1 VERSIONINFO
|
||||||
|
FILEVERSION 0,0,0,1
|
||||||
|
PRODUCTVERSION 0,0,0,1
|
||||||
|
FILEOS 0x40004
|
||||||
|
FILETYPE 0x2
|
||||||
|
{
|
||||||
|
BLOCK "StringFileInfo"
|
||||||
|
{
|
||||||
|
BLOCK "040904b0"
|
||||||
|
{
|
||||||
|
VALUE "CompanyName", "FUNNIMAN.EXE"
|
||||||
|
VALUE "FileDescription", "x10_TMAOmni Library"
|
||||||
|
VALUE "FileVersion", "0, 0, 0, 1"
|
||||||
|
VALUE "InternalName", "x10_TMAOmni Library"
|
||||||
|
VALUE "LegalCopyright", "Copyright \xA9 2025"
|
||||||
|
VALUE "OriginalFilename", "X10_TMA.DLL"
|
||||||
|
VALUE "ProductName", "LEGO Island: The Modder's Arrival"
|
||||||
|
VALUE "ProductVersion", "0, 0, 0, 1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BLOCK "VarFileInfo"
|
||||||
|
{
|
||||||
|
VALUE "Translation", 0x0409, 0x04B0
|
||||||
|
}
|
||||||
|
}
|
||||||
49
README.md
@ -1,16 +1,6 @@
|
|||||||
# LEGO Island Decompilation
|
# LEGO Island: The Modder's Arrival
|
||||||
|
|
||||||
[Development Vlog](https://www.youtube.com/playlist?list=PLbpl-gZkNl2COf_bB6cfgTapD5WduAfPz) | [Contributing](/CONTRIBUTING.md) | [Matrix](https://matrix.to/#/#isledecomp:matrix.org) | [Forums](https://forum.mattkc.com/viewforum.php?f=1) | [Patreon](https://www.patreon.com/mattkc)
|
## [Original LEGO Island decomp](https://github.com/isledecomp/isle)
|
||||||
|
|
||||||
This is a functionally complete decompilation of LEGO Island (Version 1.1, English). It aims to be as accurate as possible, matching the recompiled instructions to the original machine code as much as possible. The goal is to provide a workable codebase that can be modified, improved, and ported to other platforms later on.
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
<img src="https://legoisland.org/progress/ISLEPROGRESS.SVG" width="50%"><img src="https://legoisland.org/progress/LEGO1PROGRESS.SVG" width="50%">
|
|
||||||
|
|
||||||
Both `ISLE.EXE` and `LEGO1.DLL` are completely decompiled and, to the best of our knowledge, are functionally identical to the originals. However, work is still ongoing to improve the accuracy, naming, documentation, and structure of the source code. While there may still be unresolved bugs that are not present in retail, the game should be fully playable with the binaries derived from this source code.
|
|
||||||
|
|
||||||
Due to various complexities with regard to the compiler, these binaries are not a byte-for-byte match of the original executables. We remain hopeful that this can be resolved at some point.
|
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
@ -30,18 +20,14 @@ You will need the following software installed:
|
|||||||
|
|
||||||
1. Open a Command Prompt (`cmd`).
|
1. Open a Command Prompt (`cmd`).
|
||||||
1. From Visual C++ 4.2, run `BIN/VCVARS32.BAT x86` to populate the path and other environment variables for compiling with MSVC.
|
1. From Visual C++ 4.2, run `BIN/VCVARS32.BAT x86` to populate the path and other environment variables for compiling with MSVC.
|
||||||
1. Make a folder for compiled objects to go, such as a `build` folder inside the source repository (the folder you cloned/downloaded to).
|
1. Run "reconfigureCMake.bat" to configure CMake for building
|
||||||
1. In your Command Prompt, `cd` to the build folder.
|
|
||||||
1. Configure the project with CMake by running:
|
|
||||||
```
|
|
||||||
cmake <path-to-source> -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
|
||||||
```
|
|
||||||
- **Visual C++ 4.2 has issues with paths containing spaces**. If you get configure or build errors, make sure neither CMake, the repository, nor Visual C++ 4.2 is in a path that contains spaces.
|
- **Visual C++ 4.2 has issues with paths containing spaces**. If you get configure or build errors, make sure neither CMake, the repository, nor Visual C++ 4.2 is in a path that contains spaces.
|
||||||
- Replace `<path-to-source>` with the source repository. This can be `..` if your build folder is inside the source repository.
|
- Replace `<path-to-source>` with the source repository. This can be `..` if your build folder is inside the source repository.
|
||||||
- `RelWithDebInfo` is recommended because it will produce debug symbols useful for further decompilation work. However, you can change this to `Release` if you don't need them. While `Debug` builds can be compiled and used, they are not recommended as the primary goal is to match the code to the original binary. This is because the retail binaries were compiled as `Release` builds.
|
- `RelWithDebInfo` is recommended because it will produce debug symbols useful for further decompilation work. However, you can change this to `Release` if you don't need them. While `Debug` builds can be compiled and used, they are not recommended as the primary goal is to match the code to the original binary. This is because the retail binaries were compiled as `Release` builds.
|
||||||
- `NMake Makefiles` is most recommended because it will be immediately compatible with Visual C++ 4.2. For faster builds, you can use `Ninja` (if you have it installed), however due to limitations in Visual C++ 4.2, you can only build `Release` builds this way (debug symbols cannot be generated with `Ninja`).
|
- `NMake Makefiles` is most recommended because it will be immediately compatible with Visual C++ 4.2. For faster builds, you can use `Ninja` (if you have it installed), however due to limitations in Visual C++ 4.2, you can only build `Release` builds this way (debug symbols cannot be generated with `Ninja`).
|
||||||
1. Build the project by running `nmake` or `cmake --build <build-folder>`
|
1. Build the project by running `nmake` or `cmake --build <build-folder>` or by running "compile.bat"
|
||||||
1. When this is done, there should be a recompiled `ISLE.EXE` and `LEGO1.DLL` in the build folder.
|
1. When this is done, there should be a compiled `TMA_LAUNCHER.EXE` and `B_TMA.DLL` in the build folder.
|
||||||
1. Note that `nmake` must be run twice under certain conditions, so it is advisable to always (re-)compile using `nmake && nmake`.
|
1. Note that `nmake` must be run twice under certain conditions, so it is advisable to always (re-)compile using `nmake && nmake`.
|
||||||
|
|
||||||
If you have a CMake-compatible IDE, it should be pretty straightforward to use this repository, as long as you can use `VCVARS32.BAT` and set the generator to `NMake Makefiles`.
|
If you have a CMake-compatible IDE, it should be pretty straightforward to use this repository, as long as you can use `VCVARS32.BAT` and set the generator to `NMake Makefiles`.
|
||||||
@ -67,24 +53,19 @@ You can pass as many CMake flags as you'd like in the `CMAKE_FLAGS` environment
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
The simplest way to use the recompiled binaries is to swap the original executables (`ISLE.EXE`, `LEGO1.DLL`, and `CONFIG.EXE`) in LEGO Island's installation directory for the ones that you've built from this source code. By default, LEGO Island is installed to `C:\Program Files\LEGO Island` on 32-bit operating systems and `C:\Program Files (x86)\LEGO Island` on 64-bit operating systems.
|
In order to run TMA, you must first configure the `diskpath` environment variable (`usage of cdpath has been disabled entirely`). This is necessary since, currently, nothing creates this environment variable, or populates it.
|
||||||
|
|
||||||
For advanced users, you can get LEGO Island to run from anywhere as long as `ISLE.EXE` and `LEGO1.DLL` are in the same directory and the `cdpath` and `diskpath` registry keys (usually found in `HKEY_LOCAL_MACHINE\Software\Mindscape\LEGO Island` on 32-bit operating systems and `HKEY_LOCAL_MACHINE\Software\Wow6432Node\Mindscape\LEGO Island` on 64-bit operating systems) point to the correct location for the asset files (the directory that contains the `LEGO` folder).
|
The registry keys can be created in (usually found in `HKEY_LOCAL_MACHINE\Software\ActionSoft\LEGO Island TMA` on 32-bit operating systems and `HKEY_LOCAL_MACHINE\Software\Wow6432Node\ActionSoft\LEGO Island TMA` on 64-bit operating systems), and should point to the location where the `TMA_LAUNCHER.EXE`, `B_TMA.DLL`, and LEGO files/folders are stored (by default, this is the build folder). As long as you
|
||||||
|
|
||||||
If you see an error about `d3drm.dll`, you will need to acquire a copy and place it in the same directory as the game executables, as it has not shipped with Windows since Windows XP. We have published a [known good copy here](https://legoisland.org/download/d3drm.zip) that works with LEGO Island.
|
The build folder contains a copy of `d3drm.dll`, `D3D8.dll`, `D3DImm.dll`, `DDraw.dll`, and `dgVoodoo.conf` files, DO NOT REMOVE ANY OF THEM.
|
||||||
|
|
||||||
## Contributing
|
NOTE: Due to Github size limitations, and the fact this is a repo just for me to keep mirrored across devices, this repo does not include anything within the Lego folder. For the time being, access to the Lego folder cannot be requested, but may be able to in the future.
|
||||||
|
|
||||||
If you're interested in helping or contributing to this project, check out the [CONTRIBUTING](/CONTRIBUTING.md) page.
|
### What does what?
|
||||||
|
|
||||||
## Additional Information
|
`TMA_LAUNCHER.EXE` - The wrapper for `B_TMA.DLL`, launches the game - the equivalant to `ISLE.EXE`
|
||||||
|
|
||||||
### Which version of LEGO Island do I have?
|
`B_TMA.DLL` - The primary dll, containing all game logic - the equivalant to `LEGO1.DLL`
|
||||||
|
|
||||||
Right click on `LEGO1.DLL`, select `Properties`, and switch to the `Details` tab. Under `Version` you should either see `1.0.0.0` (1.0) or `1.1.0.0` (1.1). Additionally, you can look at the game disc files; 1.0's files will all say August 8, 1997, and 1.1's files will all say September 8, 1997. Version 1.1 is by far the most common, especially if you're not using the English or Japanese versions, so that's most likely the version you have.
|
`CONFIGURE.EXE` - Primary configuration application, contains some options - the equivalant to `CONFIG.EXE`
|
||||||
|
For more advanced configurations, you will need to acquire `dgVoodooSetup.exe`, and place it in the same folder as `dgVoodoo.conf`
|
||||||
Please note that some localized versions of LEGO Island were recompiled with small changes despite maintaining a version number parallel with other versions; this decompilation specifically targets the English release of version 1.1 of LEGO Island. You can verify you have the correct version using the checksums below:
|
|
||||||
|
|
||||||
* ISLE.EXE `md5: f6da12249e03eed1c74810cd23beb9f5`
|
|
||||||
* LEGO1.DLL `md5: 4e2f6d969ea2ef8655ba3fc221a0c8fe`
|
|
||||||
* CONFIG.EXE `md5: 92d958a64a273662c591c88b09100f4a`
|
|
||||||
BIN
build/D3D8.dll
Normal file
BIN
build/D3DImm.dll
Normal file
BIN
build/DDraw.dll
Normal file
38
build/README.txt
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
Welcome to LEGO Island: The Modder's Arrival, the first (as far as I am aware) Mod for LEGO Island!
|
||||||
|
|
||||||
|
This mod was made using MattKC's LEGO Island Decompilation.
|
||||||
|
|
||||||
|
--BEFORE PLAYING - INTRO--
|
||||||
|
|
||||||
|
Before playing, please create the diskpath registry key.
|
||||||
|
|
||||||
|
--BEFORE PLAYING - REGISTRY--
|
||||||
|
|
||||||
|
The diskpath registry key should be created in "HKEY_LOCAL_MACHINE\SOFTWARE\ActionSoft\LEGO Island TMA" or "HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\ActionSoft\LEGO Island TMA", and should contain the working directory of the TMA_LAUNCHER application.
|
||||||
|
|
||||||
|
--BEFORE PLAYING - CONFIGURATION--
|
||||||
|
|
||||||
|
Please run the configuration application at least once before running the game for the first time.
|
||||||
|
|
||||||
|
NOTICE: Because this mod uses dgVoodoo, Flip Video Memory Pages must be enabled when running the game in fullscreen. Otherwise, you will only see a black screen.
|
||||||
|
|
||||||
|
--EXTRA - CONFIGURATION--
|
||||||
|
|
||||||
|
This Mod uses dgVoodoo to fix some framerate issues and upscale the game window among other fixes. To change any settings, simply open the dgVoodooSetup application and click the '.\' button. If you do not press the '.\' button, dgVoodoo will not register your changes.
|
||||||
|
|
||||||
|
--FREQUENTLY ASKED QUESTIONS--
|
||||||
|
|
||||||
|
none yet :sadface:
|
||||||
|
|
||||||
|
--KNOWN ISSUES--
|
||||||
|
"Out of Memory" error when loading save file - Corrupt save file caused by attempting to enter build segment with incomplete code (originaly caused crash on score cube screen).
|
||||||
|
|
||||||
|
Game stuck in full-screen even with full-screen unchecked - caused by running the application as administrator. | OR | caused by full-screen being unchecked in dgVoodoo config, please use the mod's configuration application for this.
|
||||||
|
|
||||||
|
No bitmaps or animated videos in windowed mode - enable "reduced colour mode" in compatibility settings
|
||||||
|
|
||||||
|
Game lagging in windowed mode - no fix currently
|
||||||
|
|
||||||
|
Weird refresh issues in windowed mode - no fix currently
|
||||||
|
|
||||||
|
Thanks, and enjoy!
|
||||||
5
build/compile.bat
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
@echo off
|
||||||
|
nmake
|
||||||
|
echo Compolation complete! Check above for errors.
|
||||||
|
echo Press any key to quit.
|
||||||
|
pause > NUL
|
||||||
1448
build/compile_commands.json
Normal file
BIN
build/d3drm.dll
Normal file
BIN
build/dgVoodoo.conf
Normal file
9
compile.bat
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
@echo off
|
||||||
|
cd build
|
||||||
|
echo Pass #1
|
||||||
|
nmake
|
||||||
|
echo Pass #2
|
||||||
|
nmake
|
||||||
|
echo Compolation complete! Check above for errors.
|
||||||
|
echo Press any key to quit.
|
||||||
|
pause > NUL
|
||||||
3
reconfigureCMake.bat
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
cd build
|
||||||
|
cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||||
|
cd..
|
||||||
371
tools/datacmp.py
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
# (New) Data comparison.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Iterable, List, NamedTuple, Optional, Tuple
|
||||||
|
from struct import unpack
|
||||||
|
from isledecomp.compare import Compare as IsleCompare
|
||||||
|
from isledecomp.compare.db import MatchInfo
|
||||||
|
from isledecomp.cvdump import Cvdump
|
||||||
|
from isledecomp.cvdump.types import (
|
||||||
|
CvdumpKeyError,
|
||||||
|
CvdumpIntegrityError,
|
||||||
|
)
|
||||||
|
from isledecomp.bin import Bin as IsleBin
|
||||||
|
import colorama
|
||||||
|
|
||||||
|
colorama.just_fix_windows_console()
|
||||||
|
|
||||||
|
|
||||||
|
# Ignore all compare-db messages.
|
||||||
|
logging.getLogger("isledecomp.compare").addHandler(logging.NullHandler())
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(description="Comparing data values.")
|
||||||
|
parser.add_argument(
|
||||||
|
"original", metavar="original-binary", help="The original binary"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"recompiled", metavar="recompiled-binary", help="The recompiled binary"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"pdb", metavar="recompiled-pdb", help="The PDB of the recompiled binary"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"decomp_dir", metavar="decomp-dir", help="The decompiled source tree"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-v",
|
||||||
|
"--verbose",
|
||||||
|
action=argparse.BooleanOptionalAction,
|
||||||
|
default=False,
|
||||||
|
help="",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-color", "-n", action="store_true", help="Do not color the output"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--all",
|
||||||
|
"-a",
|
||||||
|
dest="show_all",
|
||||||
|
action="store_true",
|
||||||
|
help="Only show variables with a problem",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--print-rec-addr",
|
||||||
|
action="store_true",
|
||||||
|
help="Print addresses of recompiled functions too",
|
||||||
|
)
|
||||||
|
|
||||||
|
(args, _) = parser.parse_known_args()
|
||||||
|
|
||||||
|
if not os.path.isfile(args.original):
|
||||||
|
parser.error(f"Original binary {args.original} does not exist")
|
||||||
|
|
||||||
|
if not os.path.isfile(args.recompiled):
|
||||||
|
parser.error(f"Recompiled binary {args.recompiled} does not exist")
|
||||||
|
|
||||||
|
if not os.path.isfile(args.pdb):
|
||||||
|
parser.error(f"Symbols PDB {args.pdb} does not exist")
|
||||||
|
|
||||||
|
if not os.path.isdir(args.decomp_dir):
|
||||||
|
parser.error(f"Source directory {args.decomp_dir} does not exist")
|
||||||
|
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
class CompareResult(Enum):
|
||||||
|
MATCH = 1
|
||||||
|
DIFF = 2
|
||||||
|
ERROR = 3
|
||||||
|
WARN = 4
|
||||||
|
|
||||||
|
|
||||||
|
class ComparedOffset(NamedTuple):
|
||||||
|
offset: int
|
||||||
|
# name is None for scalar types
|
||||||
|
name: Optional[str]
|
||||||
|
match: bool
|
||||||
|
values: Tuple[str, str]
|
||||||
|
|
||||||
|
|
||||||
|
class ComparisonItem(NamedTuple):
|
||||||
|
"""Each variable that was compared"""
|
||||||
|
|
||||||
|
orig_addr: int
|
||||||
|
recomp_addr: int
|
||||||
|
name: str
|
||||||
|
|
||||||
|
# The list of items that were compared.
|
||||||
|
# For a complex type, these are the members.
|
||||||
|
# For a scalar type, this is a list of size one.
|
||||||
|
# If we could not retrieve type information, this is
|
||||||
|
# a list of size one but without any specific type.
|
||||||
|
compared: List[ComparedOffset]
|
||||||
|
|
||||||
|
# If present, the error message from the types parser.
|
||||||
|
error: Optional[str] = None
|
||||||
|
|
||||||
|
# If true, there is no type specified for this variable. (i.e. non-public)
|
||||||
|
# In this case, we can only compare the raw bytes.
|
||||||
|
# This is different from the situation where a type id _is_ given, but
|
||||||
|
# we could not retrieve it for some reason. (This is an error.)
|
||||||
|
raw_only: bool = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def result(self) -> CompareResult:
|
||||||
|
if self.error is not None:
|
||||||
|
return CompareResult.ERROR
|
||||||
|
|
||||||
|
if all(c.match for c in self.compared):
|
||||||
|
return CompareResult.MATCH
|
||||||
|
|
||||||
|
# Prefer WARN for a diff without complete type information.
|
||||||
|
return CompareResult.WARN if self.raw_only else CompareResult.DIFF
|
||||||
|
|
||||||
|
|
||||||
|
def create_comparison_item(
|
||||||
|
var: MatchInfo,
|
||||||
|
compared: Optional[List[ComparedOffset]] = None,
|
||||||
|
error: Optional[str] = None,
|
||||||
|
raw_only: bool = False,
|
||||||
|
) -> ComparisonItem:
|
||||||
|
"""Helper to create the ComparisonItem from the fields in MatchInfo."""
|
||||||
|
if compared is None:
|
||||||
|
compared = []
|
||||||
|
|
||||||
|
return ComparisonItem(
|
||||||
|
orig_addr=var.orig_addr,
|
||||||
|
recomp_addr=var.recomp_addr,
|
||||||
|
name=var.name,
|
||||||
|
compared=compared,
|
||||||
|
error=error,
|
||||||
|
raw_only=raw_only,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def do_the_comparison(args: argparse.Namespace) -> Iterable[ComparisonItem]:
|
||||||
|
"""Run through each variable in our compare DB, then do the comparison
|
||||||
|
according to the variable's type. Emit the result."""
|
||||||
|
with IsleBin(args.original, find_str=True) as origfile, IsleBin(
|
||||||
|
args.recompiled
|
||||||
|
) as recompfile:
|
||||||
|
isle_compare = IsleCompare(origfile, recompfile, args.pdb, args.decomp_dir)
|
||||||
|
|
||||||
|
# TODO: We don't currently retain the type information of each variable
|
||||||
|
# in our compare DB. To get those, we build this mini-lookup table that
|
||||||
|
# maps recomp addresses to their type.
|
||||||
|
# We still need to build the full compare DB though, because we may
|
||||||
|
# need the matched symbols to compare pointers (e.g. on strings)
|
||||||
|
mini_cvdump = Cvdump(args.pdb).globals().types().run()
|
||||||
|
|
||||||
|
recomp_type_reference = {
|
||||||
|
recompfile.get_abs_addr(g.section, g.offset): g.type
|
||||||
|
for g in mini_cvdump.globals
|
||||||
|
if recompfile.is_valid_section(g.section)
|
||||||
|
}
|
||||||
|
|
||||||
|
for var in isle_compare.get_variables():
|
||||||
|
type_name = recomp_type_reference.get(var.recomp_addr)
|
||||||
|
|
||||||
|
# Start by assuming we can only compare the raw bytes
|
||||||
|
data_size = var.size
|
||||||
|
is_type_aware = type_name is not None
|
||||||
|
|
||||||
|
if is_type_aware:
|
||||||
|
try:
|
||||||
|
# If we are type-aware, we can get the precise
|
||||||
|
# data size for the variable.
|
||||||
|
data_type = mini_cvdump.types.get(type_name)
|
||||||
|
data_size = data_type.size
|
||||||
|
except (CvdumpKeyError, CvdumpIntegrityError) as ex:
|
||||||
|
yield create_comparison_item(var, error=repr(ex))
|
||||||
|
continue
|
||||||
|
|
||||||
|
orig_raw = origfile.read(var.orig_addr, data_size)
|
||||||
|
recomp_raw = recompfile.read(var.recomp_addr, data_size)
|
||||||
|
|
||||||
|
# The IMAGE_SECTION_HEADER defines the SizeOfRawData and VirtualSize for the section.
|
||||||
|
# If VirtualSize > SizeOfRawData, the section is comprised of the initialized data
|
||||||
|
# corresponding to bytes in the file, and the rest is padded with zeroes when
|
||||||
|
# Windows loads the image.
|
||||||
|
# The linker might place variables initialized to zero on the threshold between
|
||||||
|
# physical data and the virtual (uninitialized) data.
|
||||||
|
# If this happens (i.e. we get an incomplete read) we just do the same padding
|
||||||
|
# to prepare for the comparison.
|
||||||
|
if orig_raw is not None and len(orig_raw) < data_size:
|
||||||
|
orig_raw = orig_raw.ljust(data_size, b"\x00")
|
||||||
|
|
||||||
|
if recomp_raw is not None and len(recomp_raw) < data_size:
|
||||||
|
recomp_raw = recomp_raw.ljust(data_size, b"\x00")
|
||||||
|
|
||||||
|
# If one or both variables are entirely uninitialized
|
||||||
|
if orig_raw is None or recomp_raw is None:
|
||||||
|
# If both variables are uninitialized, we consider them equal.
|
||||||
|
match = orig_raw is None and recomp_raw is None
|
||||||
|
|
||||||
|
# We can match a variable initialized to all zeroes with
|
||||||
|
# an uninitialized variable, but this may or may not actually
|
||||||
|
# be correct, so we flag it for the user.
|
||||||
|
uninit_force_match = not match and (
|
||||||
|
(orig_raw is None and all(b == 0 for b in recomp_raw))
|
||||||
|
or (recomp_raw is None and all(b == 0 for b in orig_raw))
|
||||||
|
)
|
||||||
|
|
||||||
|
orig_value = "(uninitialized)" if orig_raw is None else "(initialized)"
|
||||||
|
recomp_value = (
|
||||||
|
"(uninitialized)" if recomp_raw is None else "(initialized)"
|
||||||
|
)
|
||||||
|
yield create_comparison_item(
|
||||||
|
var,
|
||||||
|
compared=[
|
||||||
|
ComparedOffset(
|
||||||
|
offset=0,
|
||||||
|
name=None,
|
||||||
|
match=match,
|
||||||
|
values=(orig_value, recomp_value),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
raw_only=uninit_force_match,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not is_type_aware:
|
||||||
|
# If there is no specific type information available
|
||||||
|
# (i.e. if this is a static or non-public variable)
|
||||||
|
# then we can only compare the raw bytes.
|
||||||
|
yield create_comparison_item(
|
||||||
|
var,
|
||||||
|
compared=[
|
||||||
|
ComparedOffset(
|
||||||
|
offset=0,
|
||||||
|
name="(raw)",
|
||||||
|
match=orig_raw == recomp_raw,
|
||||||
|
values=(orig_raw, recomp_raw),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
raw_only=True,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# If we are here, we can do the type-aware comparison.
|
||||||
|
compared = []
|
||||||
|
compare_items = mini_cvdump.types.get_scalars_gapless(type_name)
|
||||||
|
format_str = mini_cvdump.types.get_format_string(type_name)
|
||||||
|
|
||||||
|
orig_data = unpack(format_str, orig_raw)
|
||||||
|
recomp_data = unpack(format_str, recomp_raw)
|
||||||
|
|
||||||
|
def pointer_display(addr: int, is_orig: bool) -> str:
|
||||||
|
"""Helper to streamline pointer textual display."""
|
||||||
|
if addr == 0:
|
||||||
|
return "nullptr"
|
||||||
|
|
||||||
|
ptr_match = (
|
||||||
|
isle_compare.get_by_orig(addr)
|
||||||
|
if is_orig
|
||||||
|
else isle_compare.get_by_recomp(addr)
|
||||||
|
)
|
||||||
|
|
||||||
|
if ptr_match is not None:
|
||||||
|
return f"Pointer to {ptr_match.match_name()}"
|
||||||
|
|
||||||
|
# This variable did not match if we do not have
|
||||||
|
# the pointer target in our DB.
|
||||||
|
return f"Unknown pointer 0x{addr:x}"
|
||||||
|
|
||||||
|
# Could zip here
|
||||||
|
for i, member in enumerate(compare_items):
|
||||||
|
if member.is_pointer:
|
||||||
|
match = isle_compare.is_pointer_match(orig_data[i], recomp_data[i])
|
||||||
|
|
||||||
|
value_a = pointer_display(orig_data[i], True)
|
||||||
|
value_b = pointer_display(recomp_data[i], False)
|
||||||
|
|
||||||
|
values = (value_a, value_b)
|
||||||
|
else:
|
||||||
|
match = orig_data[i] == recomp_data[i]
|
||||||
|
values = (orig_data[i], recomp_data[i])
|
||||||
|
|
||||||
|
compared.append(
|
||||||
|
ComparedOffset(
|
||||||
|
offset=member.offset,
|
||||||
|
name=member.name,
|
||||||
|
match=match,
|
||||||
|
values=values,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
yield create_comparison_item(var, compared=compared)
|
||||||
|
|
||||||
|
|
||||||
|
def value_get(value: Optional[str], default: str):
|
||||||
|
return value if value is not None else default
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
def display_match(result: CompareResult) -> str:
|
||||||
|
"""Helper to return color string or not, depending on user preference"""
|
||||||
|
if args.no_color:
|
||||||
|
return result.name
|
||||||
|
|
||||||
|
match_color = (
|
||||||
|
colorama.Fore.GREEN
|
||||||
|
if result == CompareResult.MATCH
|
||||||
|
else (
|
||||||
|
colorama.Fore.YELLOW
|
||||||
|
if result == CompareResult.WARN
|
||||||
|
else colorama.Fore.RED
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return f"{match_color}{result.name}{colorama.Style.RESET_ALL}"
|
||||||
|
|
||||||
|
var_count = 0
|
||||||
|
problems = 0
|
||||||
|
|
||||||
|
for item in do_the_comparison(args):
|
||||||
|
var_count += 1
|
||||||
|
if item.result in (CompareResult.DIFF, CompareResult.ERROR):
|
||||||
|
problems += 1
|
||||||
|
|
||||||
|
if not args.show_all and item.result == CompareResult.MATCH:
|
||||||
|
continue
|
||||||
|
|
||||||
|
address_display = (
|
||||||
|
f"0x{item.orig_addr:x} / 0x{item.recomp_addr:x}"
|
||||||
|
if args.print_rec_addr
|
||||||
|
else f"0x{item.orig_addr:x}"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"{item.name[:80]} ({address_display}) ... {display_match(item.result)} ")
|
||||||
|
if item.error is not None:
|
||||||
|
print(f" {item.error}")
|
||||||
|
|
||||||
|
for c in item.compared:
|
||||||
|
if not args.verbose and c.match:
|
||||||
|
continue
|
||||||
|
|
||||||
|
(value_a, value_b) = c.values
|
||||||
|
if c.match:
|
||||||
|
print(f" {c.offset:5} {value_get(c.name, '(value)'):30} {value_a}")
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
f" {c.offset:5} {value_get(c.name, '(value)'):30} {value_a} : {value_b}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if args.verbose:
|
||||||
|
print()
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"{os.path.basename(args.original)} - Variables: {var_count}. Issues: {problems}"
|
||||||
|
)
|
||||||
|
return 0 if problems == 0 else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
103
tools/decomplint/decomplint.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import colorama
|
||||||
|
from isledecomp.dir import walk_source_dir, is_file_cpp
|
||||||
|
from isledecomp.parser import DecompLinter
|
||||||
|
|
||||||
|
colorama.just_fix_windows_console()
|
||||||
|
|
||||||
|
|
||||||
|
def display_errors(alerts, filename):
|
||||||
|
sorted_alerts = sorted(alerts, key=lambda a: a.line_number)
|
||||||
|
|
||||||
|
for alert in sorted_alerts:
|
||||||
|
error_type = (
|
||||||
|
f"{colorama.Fore.RED}error: "
|
||||||
|
if alert.is_error()
|
||||||
|
else f"{colorama.Fore.YELLOW}warning: "
|
||||||
|
)
|
||||||
|
components = [
|
||||||
|
colorama.Fore.LIGHTWHITE_EX,
|
||||||
|
filename,
|
||||||
|
":",
|
||||||
|
str(alert.line_number),
|
||||||
|
" : ",
|
||||||
|
error_type,
|
||||||
|
colorama.Fore.LIGHTWHITE_EX,
|
||||||
|
alert.code.name.lower(),
|
||||||
|
]
|
||||||
|
print("".join(components))
|
||||||
|
|
||||||
|
if alert.line is not None:
|
||||||
|
print(f"{colorama.Fore.WHITE} {alert.line}")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
p = argparse.ArgumentParser(
|
||||||
|
description="Syntax checking and linting for decomp annotation markers."
|
||||||
|
)
|
||||||
|
p.add_argument("target", help="The file or directory to check.")
|
||||||
|
p.add_argument(
|
||||||
|
"--module",
|
||||||
|
required=False,
|
||||||
|
type=str,
|
||||||
|
help="If present, run targeted checks for markers from the given module.",
|
||||||
|
)
|
||||||
|
p.add_argument(
|
||||||
|
"--warnfail",
|
||||||
|
action=argparse.BooleanOptionalAction,
|
||||||
|
default=False,
|
||||||
|
help="Fail if syntax warnings are found.",
|
||||||
|
)
|
||||||
|
|
||||||
|
(args, _) = p.parse_known_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def process_files(files, module=None):
|
||||||
|
warning_count = 0
|
||||||
|
error_count = 0
|
||||||
|
|
||||||
|
linter = DecompLinter()
|
||||||
|
for filename in files:
|
||||||
|
success = linter.check_file(filename, module)
|
||||||
|
|
||||||
|
warnings = [a for a in linter.alerts if a.is_warning()]
|
||||||
|
errors = [a for a in linter.alerts if a.is_error()]
|
||||||
|
|
||||||
|
error_count += len(errors)
|
||||||
|
warning_count += len(warnings)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
display_errors(linter.alerts, filename)
|
||||||
|
print()
|
||||||
|
|
||||||
|
return (warning_count, error_count)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
files_to_check = []
|
||||||
|
if os.path.isdir(args.target):
|
||||||
|
files_to_check = list(walk_source_dir(args.target))
|
||||||
|
elif os.path.isfile(args.target) and is_file_cpp(args.target):
|
||||||
|
files_to_check = [args.target]
|
||||||
|
else:
|
||||||
|
sys.exit("Invalid target")
|
||||||
|
|
||||||
|
(warning_count, error_count) = process_files(files_to_check, module=args.module)
|
||||||
|
|
||||||
|
print(colorama.Style.RESET_ALL, end="")
|
||||||
|
|
||||||
|
would_fail = error_count > 0 or (warning_count > 0 and args.warnfail)
|
||||||
|
if would_fail:
|
||||||
|
return 1
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
25
tools/ghidra_scripts/README.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Ghidra Scripts
|
||||||
|
|
||||||
|
The scripts in this directory provide additional functionality in Ghidra, e.g. imports of symbols and types from the PDB debug symbol file.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Ghidrathon
|
||||||
|
Since these scripts and its dependencies are written in Python 3, [Ghidrathon](https://github.com/mandiant/Ghidrathon) must be installed first. Follow the instructions and install a recent build (these scripts were tested with Python 3.12 and Ghidrathon v4.0.0).
|
||||||
|
|
||||||
|
### Script Directory
|
||||||
|
- In Ghidra, _Open Window -> Script Manager_.
|
||||||
|
- Click the _Manage Script Directories_ button on the top right.
|
||||||
|
- Click the _Add_ (Plus icon) button and select this file's parent directory.
|
||||||
|
- Close the window and click the _Refresh_ button.
|
||||||
|
- This script should now be available under the folder _LEGO1_.
|
||||||
|
|
||||||
|
### Virtual environment
|
||||||
|
As of now, there must be a Python virtual environment set up under `$REPOSITORY_ROOT/.venv`, and the dependencies of `isledecomp` must be installed there, see [here](../README.md#tooling).
|
||||||
|
|
||||||
|
## Development
|
||||||
|
- Type hints for Ghidra (optional): Download a recent release from https://github.com/VDOO-Connected-Trust/ghidra-pyi-generator,
|
||||||
|
unpack it somewhere, and `pip install` that directory in this virtual environment. This provides types and headers for Python.
|
||||||
|
Be aware that some of these files contain errors - in particular, `from typing import overload` seems to be missing everywhere, leading to spurious type errors.
|
||||||
|
- Note that the imported modules persist across multiple runs of the script (see [here](https://github.com/mandiant/Ghidrathon/issues/103)).
|
||||||
|
If you indend to modify an imported library, you have to use `import importlib; importlib.reload(${library})` or restart Ghidra for your changes to have any effect. Unfortunately, even that is not perfectly reliable, so you may still have to restart Ghidra for some changes in `isledecomp` to be applied.
|
||||||
322
tools/ghidra_scripts/import_functions_and_types_from_pdb.py
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
# Imports types and function signatures from debug symbols (PDB file) of the recompilation.
|
||||||
|
#
|
||||||
|
# This script uses Python 3 and therefore requires Ghidrathon to be installed in Ghidra (see https://github.com/mandiant/Ghidrathon).
|
||||||
|
# Furthermore, the virtual environment must be set up beforehand under $REPOSITORY_ROOT/.venv, and all required packages must be installed
|
||||||
|
# (see $REPOSITORY_ROOT/tools/README.md).
|
||||||
|
# Also, the Python version of the virtual environment must probably match the Python version used for Ghidrathon.
|
||||||
|
|
||||||
|
# @author J. Schulz
|
||||||
|
# @category LEGO1
|
||||||
|
# @keybinding
|
||||||
|
# @menupath
|
||||||
|
# @toolbar
|
||||||
|
|
||||||
|
|
||||||
|
# In order to make this code run both within and outside of Ghidra, the import order is rather unorthodox in this file.
|
||||||
|
# That is why some of the lints below are disabled.
|
||||||
|
|
||||||
|
# pylint: disable=wrong-import-position,ungrouped-imports
|
||||||
|
# pylint: disable=undefined-variable # need to disable this one globally because pylint does not understand e.g. `askYesNo()``
|
||||||
|
|
||||||
|
# Disable spurious warnings in vscode / pylance
|
||||||
|
# pyright: reportMissingModuleSource=false
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
import importlib
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
import logging.handlers
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
import traceback
|
||||||
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
import ghidra
|
||||||
|
from lego_util.headers import * # pylint: disable=wildcard-import # these are just for headers
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def reload_module(module: str):
|
||||||
|
"""
|
||||||
|
Due to a a quirk in Jep (used by Ghidrathon), imported modules persist for the lifetime of the Ghidra process
|
||||||
|
and are not reloaded when relaunching the script. Therefore, in order to facilitate development
|
||||||
|
we force reload all our own modules at startup. See also https://github.com/mandiant/Ghidrathon/issues/103.
|
||||||
|
|
||||||
|
Note that as of 2024-05-30, this remedy does not work perfectly (yet): Some changes in isledecomp are
|
||||||
|
still not detected correctly and require a Ghidra restart to be applied.
|
||||||
|
"""
|
||||||
|
importlib.reload(importlib.import_module(module))
|
||||||
|
|
||||||
|
|
||||||
|
reload_module("lego_util.statistics")
|
||||||
|
from lego_util.statistics import Statistics
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Globals:
|
||||||
|
verbose: bool
|
||||||
|
loglevel: int
|
||||||
|
running_from_ghidra: bool = False
|
||||||
|
# statistics
|
||||||
|
statistics: Statistics = field(default_factory=Statistics)
|
||||||
|
|
||||||
|
|
||||||
|
class SupportedModules(Enum):
|
||||||
|
LEGO1 = 1
|
||||||
|
BETA10 = 2
|
||||||
|
|
||||||
|
def orig_filename(self):
|
||||||
|
if self == self.LEGO1:
|
||||||
|
return "LEGO1.DLL"
|
||||||
|
return "BETA10.DLL"
|
||||||
|
|
||||||
|
def recomp_filename_without_extension(self):
|
||||||
|
# in case we want to support more functions
|
||||||
|
return "LEGO1"
|
||||||
|
|
||||||
|
def build_dir_name(self):
|
||||||
|
if self == self.BETA10:
|
||||||
|
return "build_debug"
|
||||||
|
return "build"
|
||||||
|
|
||||||
|
|
||||||
|
# hard-coded settings that we don't want to prompt in Ghidra every time
|
||||||
|
GLOBALS = Globals(
|
||||||
|
verbose=False,
|
||||||
|
# loglevel=logging.INFO,
|
||||||
|
loglevel=logging.DEBUG,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logging():
|
||||||
|
logging.root.handlers.clear()
|
||||||
|
formatter = logging.Formatter("%(levelname)-8s %(message)s")
|
||||||
|
# formatter = logging.Formatter("%(name)s %(levelname)-8s %(message)s") # use this to identify loggers
|
||||||
|
stdout_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
stdout_handler.setFormatter(formatter)
|
||||||
|
file_handler = logging.FileHandler(
|
||||||
|
Path(__file__).absolute().parent.joinpath("import.log"), mode="w"
|
||||||
|
)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
logging.root.setLevel(GLOBALS.loglevel)
|
||||||
|
logging.root.addHandler(stdout_handler)
|
||||||
|
logging.root.addHandler(file_handler)
|
||||||
|
logger.info("Starting import...")
|
||||||
|
|
||||||
|
|
||||||
|
# This script can be run both from Ghidra and as a standalone.
|
||||||
|
# In the latter case, only the PDB parser will be used.
|
||||||
|
setup_logging()
|
||||||
|
try:
|
||||||
|
from ghidra.program.flatapi import FlatProgramAPI
|
||||||
|
from ghidra.util.exception import CancelledException
|
||||||
|
|
||||||
|
GLOBALS.running_from_ghidra = True
|
||||||
|
except ImportError as importError:
|
||||||
|
logger.error(
|
||||||
|
"Failed to import Ghidra functions, doing a dry run for the source code parser. "
|
||||||
|
"Has this script been launched from Ghidra?"
|
||||||
|
)
|
||||||
|
logger.debug("Precise import error:", exc_info=importError)
|
||||||
|
|
||||||
|
GLOBALS.running_from_ghidra = False
|
||||||
|
CancelledException = None
|
||||||
|
|
||||||
|
|
||||||
|
def get_repository_root():
|
||||||
|
return Path(__file__).absolute().parent.parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
def add_python_path(path: str):
|
||||||
|
"""
|
||||||
|
Scripts in Ghidra are executed from the tools/ghidra_scripts directory. We need to add
|
||||||
|
a few more paths to the Python path so we can import the other libraries.
|
||||||
|
"""
|
||||||
|
venv_path = get_repository_root().joinpath(path)
|
||||||
|
logger.info("Adding %s to Python Path", venv_path)
|
||||||
|
assert venv_path.exists()
|
||||||
|
sys.path.insert(1, str(venv_path))
|
||||||
|
|
||||||
|
|
||||||
|
# We need to quote the types here because they might not exist when running without Ghidra
|
||||||
|
def import_function_into_ghidra(
|
||||||
|
api: "FlatProgramAPI",
|
||||||
|
pdb_function: "PdbFunction",
|
||||||
|
type_importer: "PdbTypeImporter",
|
||||||
|
):
|
||||||
|
hex_original_address = f"{pdb_function.match_info.orig_addr:x}"
|
||||||
|
|
||||||
|
# Find the Ghidra function at that address
|
||||||
|
ghidra_address = getAddressFactory().getAddress(hex_original_address)
|
||||||
|
# pylint: disable=possibly-used-before-assignment
|
||||||
|
function_importer = PdbFunctionImporter.build(api, pdb_function, type_importer)
|
||||||
|
|
||||||
|
ghidra_function = getFunctionAt(ghidra_address)
|
||||||
|
if ghidra_function is None:
|
||||||
|
ghidra_function = createFunction(ghidra_address, "temp")
|
||||||
|
assert (
|
||||||
|
ghidra_function is not None
|
||||||
|
), f"Failed to create function at {ghidra_address}"
|
||||||
|
logger.info("Created new function at %s", ghidra_address)
|
||||||
|
|
||||||
|
logger.debug("Start handling function '%s'", function_importer.get_full_name())
|
||||||
|
|
||||||
|
if function_importer.matches_ghidra_function(ghidra_function):
|
||||||
|
logger.info(
|
||||||
|
"Skipping function '%s', matches already",
|
||||||
|
function_importer.get_full_name(),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"Modifying function %s at 0x%s",
|
||||||
|
function_importer.get_full_name(),
|
||||||
|
hex_original_address,
|
||||||
|
)
|
||||||
|
|
||||||
|
function_importer.overwrite_ghidra_function(ghidra_function)
|
||||||
|
|
||||||
|
GLOBALS.statistics.functions_changed += 1
|
||||||
|
|
||||||
|
|
||||||
|
def process_functions(extraction: "PdbFunctionExtractor"):
|
||||||
|
pdb_functions = extraction.get_function_list()
|
||||||
|
|
||||||
|
if not GLOBALS.running_from_ghidra:
|
||||||
|
logger.info("Completed the dry run outside Ghidra.")
|
||||||
|
return
|
||||||
|
|
||||||
|
api = FlatProgramAPI(currentProgram())
|
||||||
|
# pylint: disable=possibly-used-before-assignment
|
||||||
|
type_importer = PdbTypeImporter(api, extraction)
|
||||||
|
|
||||||
|
for pdb_func in pdb_functions:
|
||||||
|
func_name = pdb_func.match_info.name
|
||||||
|
try:
|
||||||
|
import_function_into_ghidra(api, pdb_func, type_importer)
|
||||||
|
GLOBALS.statistics.successes += 1
|
||||||
|
except Lego1Exception as e:
|
||||||
|
log_and_track_failure(func_name, e)
|
||||||
|
except RuntimeError as e:
|
||||||
|
cause = e.args[0]
|
||||||
|
if CancelledException is not None and isinstance(cause, CancelledException):
|
||||||
|
# let Ghidra's CancelledException pass through
|
||||||
|
logging.critical("Import aborted by the user.")
|
||||||
|
return
|
||||||
|
|
||||||
|
log_and_track_failure(func_name, cause, unexpected=True)
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
except Exception as e: # pylint: disable=broad-exception-caught
|
||||||
|
log_and_track_failure(func_name, e, unexpected=True)
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
|
def log_and_track_failure(
|
||||||
|
function_name: Optional[str], error: Exception, unexpected: bool = False
|
||||||
|
):
|
||||||
|
if GLOBALS.statistics.track_failure_and_tell_if_new(error):
|
||||||
|
logger.error(
|
||||||
|
"%s(): %s%s",
|
||||||
|
function_name,
|
||||||
|
"Unexpected error: " if unexpected else "",
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if GLOBALS.running_from_ghidra:
|
||||||
|
origfile_name = getProgramFile().getName()
|
||||||
|
|
||||||
|
if origfile_name == "LEGO1.DLL":
|
||||||
|
module = SupportedModules.LEGO1
|
||||||
|
elif origfile_name in ["LEGO1D.DLL", "BETA10.DLL"]:
|
||||||
|
module = SupportedModules.BETA10
|
||||||
|
else:
|
||||||
|
raise Lego1Exception(
|
||||||
|
f"Unsupported file name in import script: {origfile_name}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
module = SupportedModules.LEGO1
|
||||||
|
|
||||||
|
logger.info("Importing file: %s", module.orig_filename())
|
||||||
|
|
||||||
|
repo_root = get_repository_root()
|
||||||
|
origfile_path = repo_root.joinpath("legobin").joinpath(module.orig_filename())
|
||||||
|
build_directory = repo_root.joinpath(module.build_dir_name())
|
||||||
|
recompiledfile_name = f"{module.recomp_filename_without_extension()}.DLL"
|
||||||
|
recompiledfile_path = build_directory.joinpath(recompiledfile_name)
|
||||||
|
pdbfile_name = f"{module.recomp_filename_without_extension()}.PDB"
|
||||||
|
pdbfile_path = build_directory.joinpath(pdbfile_name)
|
||||||
|
|
||||||
|
if not GLOBALS.verbose:
|
||||||
|
logging.getLogger("isledecomp.bin").setLevel(logging.WARNING)
|
||||||
|
logging.getLogger("isledecomp.compare.core").setLevel(logging.WARNING)
|
||||||
|
logging.getLogger("isledecomp.compare.db").setLevel(logging.WARNING)
|
||||||
|
logging.getLogger("isledecomp.compare.lines").setLevel(logging.WARNING)
|
||||||
|
logging.getLogger("isledecomp.cvdump.symbols").setLevel(logging.WARNING)
|
||||||
|
|
||||||
|
logger.info("Starting comparison")
|
||||||
|
with Bin(str(origfile_path), find_str=True) as origfile, Bin(
|
||||||
|
str(recompiledfile_path)
|
||||||
|
) as recompfile:
|
||||||
|
isle_compare = IsleCompare(
|
||||||
|
origfile, recompfile, str(pdbfile_path), str(repo_root)
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("Comparison complete.")
|
||||||
|
|
||||||
|
# try to acquire matched functions
|
||||||
|
migration = PdbFunctionExtractor(isle_compare)
|
||||||
|
try:
|
||||||
|
process_functions(migration)
|
||||||
|
finally:
|
||||||
|
if GLOBALS.running_from_ghidra:
|
||||||
|
GLOBALS.statistics.log()
|
||||||
|
|
||||||
|
logger.info("Done")
|
||||||
|
|
||||||
|
|
||||||
|
# sys.path is not reset after running the script, so we should restore it
|
||||||
|
sys_path_backup = sys.path.copy()
|
||||||
|
try:
|
||||||
|
# make modules installed in the venv available in Ghidra
|
||||||
|
add_python_path(".venv/Lib/site-packages")
|
||||||
|
# This one is needed when isledecomp is installed in editable mode in the venv
|
||||||
|
add_python_path("tools/isledecomp")
|
||||||
|
|
||||||
|
import setuptools # pylint: disable=unused-import # required to fix a distutils issue in Python 3.12
|
||||||
|
|
||||||
|
reload_module("isledecomp")
|
||||||
|
from isledecomp import Bin
|
||||||
|
|
||||||
|
reload_module("isledecomp.compare")
|
||||||
|
from isledecomp.compare import Compare as IsleCompare
|
||||||
|
|
||||||
|
reload_module("isledecomp.compare.db")
|
||||||
|
|
||||||
|
reload_module("lego_util.exceptions")
|
||||||
|
from lego_util.exceptions import Lego1Exception
|
||||||
|
|
||||||
|
reload_module("lego_util.pdb_extraction")
|
||||||
|
from lego_util.pdb_extraction import (
|
||||||
|
PdbFunctionExtractor,
|
||||||
|
PdbFunction,
|
||||||
|
)
|
||||||
|
|
||||||
|
if GLOBALS.running_from_ghidra:
|
||||||
|
reload_module("lego_util.ghidra_helper")
|
||||||
|
|
||||||
|
reload_module("lego_util.function_importer")
|
||||||
|
from lego_util.function_importer import PdbFunctionImporter
|
||||||
|
|
||||||
|
reload_module("lego_util.type_importer")
|
||||||
|
from lego_util.type_importer import PdbTypeImporter
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
finally:
|
||||||
|
sys.path = sys_path_backup
|
||||||
0
tools/ghidra_scripts/lego_util/__init__.py
Normal file
47
tools/ghidra_scripts/lego_util/exceptions.py
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
class Lego1Exception(Exception):
|
||||||
|
"""
|
||||||
|
Our own base class for exceptions.
|
||||||
|
Makes it easier to distinguish expected and unexpected errors.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class TypeNotFoundError(Lego1Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return f"Type not found in PDB: {self.args[0]}"
|
||||||
|
|
||||||
|
|
||||||
|
class TypeNotFoundInGhidraError(Lego1Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return f"Type not found in Ghidra: {self.args[0]}"
|
||||||
|
|
||||||
|
|
||||||
|
class TypeNotImplementedError(Lego1Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return f"Import not implemented for type: {self.args[0]}"
|
||||||
|
|
||||||
|
|
||||||
|
class ClassOrNamespaceNotFoundInGhidraError(Lego1Exception):
|
||||||
|
def __init__(self, namespaceHierachy: list[str]):
|
||||||
|
super().__init__(namespaceHierachy)
|
||||||
|
|
||||||
|
def get_namespace_str(self) -> str:
|
||||||
|
return "::".join(self.args[0])
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Class or namespace not found in Ghidra: {self.get_namespace_str()}"
|
||||||
|
|
||||||
|
|
||||||
|
class MultipleTypesFoundInGhidraError(Lego1Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return (
|
||||||
|
f"Found multiple types matching '{self.args[0]}' in Ghidra: {self.args[1]}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class StackOffsetMismatchError(Lego1Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class StructModificationError(Lego1Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return f"Failed to modify struct in Ghidra: '{self.args[0]}'\nDetailed error: {self.__cause__}"
|
||||||
421
tools/ghidra_scripts/lego_util/function_importer.py
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
# This file can only be imported successfully when run from Ghidra using Ghidrathon.
|
||||||
|
|
||||||
|
# Disable spurious warnings in vscode / pylance
|
||||||
|
# pyright: reportMissingModuleSource=false
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
from ghidra.program.model.listing import Function, Parameter
|
||||||
|
from ghidra.program.flatapi import FlatProgramAPI
|
||||||
|
from ghidra.program.model.listing import ParameterImpl
|
||||||
|
from ghidra.program.model.symbol import SourceType
|
||||||
|
from ghidra.program.model.data import (
|
||||||
|
TypeDef,
|
||||||
|
TypedefDataType,
|
||||||
|
Pointer,
|
||||||
|
ComponentOffsetSettingsDefinition,
|
||||||
|
)
|
||||||
|
|
||||||
|
from lego_util.pdb_extraction import (
|
||||||
|
PdbFunction,
|
||||||
|
CppRegisterSymbol,
|
||||||
|
CppStackSymbol,
|
||||||
|
)
|
||||||
|
from lego_util.ghidra_helper import (
|
||||||
|
add_data_type_or_reuse_existing,
|
||||||
|
create_ghidra_namespace,
|
||||||
|
get_or_add_pointer_type,
|
||||||
|
get_ghidra_namespace,
|
||||||
|
sanitize_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
from lego_util.exceptions import StackOffsetMismatchError, Lego1Exception
|
||||||
|
from lego_util.type_importer import PdbTypeImporter
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class PdbFunctionImporter(ABC):
|
||||||
|
"""A representation of a function from the PDB with each type replaced by a Ghidra type instance."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
api: FlatProgramAPI,
|
||||||
|
func: PdbFunction,
|
||||||
|
type_importer: "PdbTypeImporter",
|
||||||
|
):
|
||||||
|
self.api = api
|
||||||
|
self.match_info = func.match_info
|
||||||
|
self.type_importer = type_importer
|
||||||
|
|
||||||
|
assert self.match_info.name is not None
|
||||||
|
|
||||||
|
colon_split = sanitize_name(self.match_info.name).split("::")
|
||||||
|
self.name = colon_split.pop()
|
||||||
|
namespace_hierachy = colon_split
|
||||||
|
self.namespace = self._do_get_namespace(namespace_hierachy)
|
||||||
|
|
||||||
|
def _do_get_namespace(self, namespace_hierarchy: list[str]):
|
||||||
|
return get_ghidra_namespace(self.api, namespace_hierarchy)
|
||||||
|
|
||||||
|
def get_full_name(self) -> str:
|
||||||
|
return f"{self.namespace.getName()}::{self.name}"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build(api: FlatProgramAPI, func: PdbFunction, type_importer: "PdbTypeImporter"):
|
||||||
|
return (
|
||||||
|
ThunkPdbFunctionImport(api, func, type_importer)
|
||||||
|
if func.signature is None
|
||||||
|
else FullPdbFunctionImporter(api, func, type_importer)
|
||||||
|
)
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def matches_ghidra_function(self, ghidra_function: Function) -> bool:
|
||||||
|
...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def overwrite_ghidra_function(self, ghidra_function: Function):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class ThunkPdbFunctionImport(PdbFunctionImporter):
|
||||||
|
"""For importing thunk functions (like vtordisp or debug build thunks) into Ghidra.
|
||||||
|
Only the name of the function will be imported."""
|
||||||
|
|
||||||
|
def _do_get_namespace(self, namespace_hierarchy: list[str]):
|
||||||
|
"""We need to create the namespace because we don't import the return type here"""
|
||||||
|
return create_ghidra_namespace(self.api, namespace_hierarchy)
|
||||||
|
|
||||||
|
def matches_ghidra_function(self, ghidra_function: Function) -> bool:
|
||||||
|
name_match = self.name == ghidra_function.getName(False)
|
||||||
|
namespace_match = self.namespace == ghidra_function.getParentNamespace()
|
||||||
|
|
||||||
|
logger.debug("Matches: namespace=%s name=%s", namespace_match, name_match)
|
||||||
|
|
||||||
|
return name_match and namespace_match
|
||||||
|
|
||||||
|
def overwrite_ghidra_function(self, ghidra_function: Function):
|
||||||
|
ghidra_function.setName(self.name, SourceType.USER_DEFINED)
|
||||||
|
ghidra_function.setParentNamespace(self.namespace)
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-instance-attributes
|
||||||
|
class FullPdbFunctionImporter(PdbFunctionImporter):
|
||||||
|
"""For importing functions into Ghidra where all information are available."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
api: FlatProgramAPI,
|
||||||
|
func: PdbFunction,
|
||||||
|
type_importer: "PdbTypeImporter",
|
||||||
|
):
|
||||||
|
super().__init__(api, func, type_importer)
|
||||||
|
|
||||||
|
assert func.signature is not None
|
||||||
|
self.signature = func.signature
|
||||||
|
|
||||||
|
self.is_stub = func.is_stub
|
||||||
|
|
||||||
|
if self.signature.class_type is not None:
|
||||||
|
# Import the base class so the namespace exists
|
||||||
|
self.type_importer.import_pdb_type_into_ghidra(self.signature.class_type)
|
||||||
|
|
||||||
|
self.return_type = type_importer.import_pdb_type_into_ghidra(
|
||||||
|
self.signature.return_type
|
||||||
|
)
|
||||||
|
self.arguments = [
|
||||||
|
ParameterImpl(
|
||||||
|
f"param{index}",
|
||||||
|
type_importer.import_pdb_type_into_ghidra(type_name),
|
||||||
|
api.getCurrentProgram(),
|
||||||
|
)
|
||||||
|
for (index, type_name) in enumerate(self.signature.arglist)
|
||||||
|
]
|
||||||
|
|
||||||
|
def matches_ghidra_function(self, ghidra_function: Function) -> bool:
|
||||||
|
"""Checks whether this function declaration already matches the description in Ghidra"""
|
||||||
|
name_match = self.name == ghidra_function.getName(False)
|
||||||
|
namespace_match = self.namespace == ghidra_function.getParentNamespace()
|
||||||
|
ghidra_return_type = ghidra_function.getReturnType()
|
||||||
|
return_type_match = self.return_type == ghidra_return_type
|
||||||
|
|
||||||
|
# Handle edge case: Return type X that is larger than the return register.
|
||||||
|
# In that case, the function returns `X*` and has another argument `X* __return_storage_ptr`.
|
||||||
|
if (
|
||||||
|
(not return_type_match)
|
||||||
|
and (self.return_type.getLength() > 4)
|
||||||
|
and (
|
||||||
|
get_or_add_pointer_type(self.api, self.return_type)
|
||||||
|
== ghidra_return_type
|
||||||
|
)
|
||||||
|
and any(
|
||||||
|
param
|
||||||
|
for param in ghidra_function.getParameters()
|
||||||
|
if param.getName() == "__return_storage_ptr__"
|
||||||
|
)
|
||||||
|
):
|
||||||
|
logger.debug(
|
||||||
|
"%s has a return type larger than 4 bytes", self.get_full_name()
|
||||||
|
)
|
||||||
|
return_type_match = True
|
||||||
|
|
||||||
|
# match arguments: decide if thiscall or not, and whether the `this` type matches
|
||||||
|
calling_convention_match = (
|
||||||
|
self.signature.call_type == ghidra_function.getCallingConventionName()
|
||||||
|
)
|
||||||
|
|
||||||
|
ghidra_params_without_this = list(ghidra_function.getParameters())
|
||||||
|
|
||||||
|
if calling_convention_match and self.signature.call_type == "__thiscall":
|
||||||
|
this_argument = ghidra_params_without_this.pop(0)
|
||||||
|
calling_convention_match = self._this_type_match(this_argument)
|
||||||
|
|
||||||
|
if self.is_stub:
|
||||||
|
# We do not import the argument list for stubs, so it should be excluded in matches
|
||||||
|
args_match = True
|
||||||
|
elif calling_convention_match:
|
||||||
|
args_match = self._parameter_lists_match(ghidra_params_without_this)
|
||||||
|
else:
|
||||||
|
args_match = False
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"Matches: namespace=%s name=%s return_type=%s calling_convention=%s args=%s",
|
||||||
|
namespace_match,
|
||||||
|
name_match,
|
||||||
|
return_type_match,
|
||||||
|
calling_convention_match,
|
||||||
|
"ignored" if self.is_stub else args_match,
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
name_match
|
||||||
|
and namespace_match
|
||||||
|
and return_type_match
|
||||||
|
and calling_convention_match
|
||||||
|
and args_match
|
||||||
|
)
|
||||||
|
|
||||||
|
def _this_type_match(self, this_parameter: Parameter) -> bool:
|
||||||
|
if this_parameter.getName() != "this":
|
||||||
|
logger.info("Expected first argument to be `this` in __thiscall")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.signature.this_adjust != 0:
|
||||||
|
# In this case, the `this` argument should be custom defined
|
||||||
|
if not isinstance(this_parameter.getDataType(), TypeDef):
|
||||||
|
logger.info(
|
||||||
|
"`this` argument is not a typedef while `this adjust` = %d",
|
||||||
|
self.signature.this_adjust,
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
# We are not checking for the _correct_ `this` type here, which we could do in the future
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _parameter_lists_match(self, ghidra_params: "list[Parameter]") -> bool:
|
||||||
|
# Remove return storage pointer from comparison if present.
|
||||||
|
# This is relevant to returning values larger than 4 bytes, and is not mentioned in the PDB
|
||||||
|
ghidra_params = [
|
||||||
|
param
|
||||||
|
for param in ghidra_params
|
||||||
|
if param.getName() != "__return_storage_ptr__"
|
||||||
|
]
|
||||||
|
|
||||||
|
if len(self.arguments) != len(ghidra_params):
|
||||||
|
logger.info("Mismatching argument count")
|
||||||
|
return False
|
||||||
|
|
||||||
|
for this_arg, ghidra_arg in zip(self.arguments, ghidra_params):
|
||||||
|
# compare argument types
|
||||||
|
if this_arg.getDataType() != ghidra_arg.getDataType():
|
||||||
|
logger.debug(
|
||||||
|
"Mismatching arg type: expected %s, found %s",
|
||||||
|
this_arg.getDataType(),
|
||||||
|
ghidra_arg.getDataType(),
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
# compare argument names
|
||||||
|
stack_match = self.get_matching_stack_symbol(ghidra_arg.getStackOffset())
|
||||||
|
if stack_match is None:
|
||||||
|
logger.debug("Not found on stack: %s", ghidra_arg)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if stack_match.name.startswith("__formal"):
|
||||||
|
# "__formal" is the placeholder for arguments without a name
|
||||||
|
continue
|
||||||
|
|
||||||
|
if stack_match.name == "__$ReturnUdt":
|
||||||
|
# These appear in templates and cannot be set automatically, as they are a NOTYPE
|
||||||
|
continue
|
||||||
|
|
||||||
|
if stack_match.name != ghidra_arg.getName():
|
||||||
|
logger.debug(
|
||||||
|
"Argument name mismatch: expected %s, found %s",
|
||||||
|
stack_match.name,
|
||||||
|
ghidra_arg.getName(),
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def overwrite_ghidra_function(self, ghidra_function: Function):
|
||||||
|
"""Replace the function declaration in Ghidra by the one derived from C++."""
|
||||||
|
|
||||||
|
if ghidra_function.hasCustomVariableStorage():
|
||||||
|
# Unfortunately, calling `ghidra_function.setCustomVariableStorage(False)`
|
||||||
|
# leads to two `this` parameters. Therefore, we first need to remove all `this` parameters
|
||||||
|
# and then re-generate a new one
|
||||||
|
ghidra_function.replaceParameters(
|
||||||
|
Function.FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, # this implicitly sets custom variable storage to False
|
||||||
|
True,
|
||||||
|
SourceType.USER_DEFINED,
|
||||||
|
[
|
||||||
|
param
|
||||||
|
for param in ghidra_function.getParameters()
|
||||||
|
if param.getName() != "this"
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
if ghidra_function.hasCustomVariableStorage():
|
||||||
|
raise Lego1Exception("Failed to disable custom variable storage.")
|
||||||
|
|
||||||
|
ghidra_function.setName(self.name, SourceType.USER_DEFINED)
|
||||||
|
ghidra_function.setParentNamespace(self.namespace)
|
||||||
|
ghidra_function.setReturnType(self.return_type, SourceType.USER_DEFINED)
|
||||||
|
ghidra_function.setCallingConvention(self.signature.call_type)
|
||||||
|
|
||||||
|
if self.is_stub:
|
||||||
|
logger.debug(
|
||||||
|
"%s is a stub, skipping parameter import", self.get_full_name()
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
ghidra_function.replaceParameters(
|
||||||
|
Function.FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS,
|
||||||
|
True, # force
|
||||||
|
SourceType.USER_DEFINED,
|
||||||
|
self.arguments,
|
||||||
|
)
|
||||||
|
self._import_parameter_names(ghidra_function)
|
||||||
|
|
||||||
|
# Special handling for `this adjust` and virtual inheritance
|
||||||
|
if self.signature.this_adjust != 0:
|
||||||
|
self._set_this_adjust(ghidra_function)
|
||||||
|
|
||||||
|
def _import_parameter_names(self, ghidra_function: Function):
|
||||||
|
# When we call `ghidra_function.replaceParameters`, Ghidra will generate the layout.
|
||||||
|
# Now we read the parameters again and match them against the stack layout in the PDB,
|
||||||
|
# both to verify the layout and to set the parameter names.
|
||||||
|
ghidra_parameters: list[Parameter] = ghidra_function.getParameters()
|
||||||
|
|
||||||
|
# Try to add Ghidra function names
|
||||||
|
for index, param in enumerate(ghidra_parameters):
|
||||||
|
if param.isStackVariable():
|
||||||
|
self._rename_stack_parameter(index, param)
|
||||||
|
else:
|
||||||
|
if param.getName() == "this":
|
||||||
|
# 'this' parameters are auto-generated and cannot be changed
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Appears to never happen - could in theory be relevant to __fastcall__ functions,
|
||||||
|
# which we haven't seen yet
|
||||||
|
logger.warning(
|
||||||
|
"Unhandled register variable in %s", self.get_full_name()
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
def _rename_stack_parameter(self, index: int, param: Parameter):
|
||||||
|
match = self.get_matching_stack_symbol(param.getStackOffset())
|
||||||
|
if match is None:
|
||||||
|
raise StackOffsetMismatchError(
|
||||||
|
f"Could not find a matching symbol at offset {param.getStackOffset()} in {self.get_full_name()}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if match.data_type == "T_NOTYPE(0000)":
|
||||||
|
logger.warning("Skipping stack parameter of type NOTYPE")
|
||||||
|
return
|
||||||
|
|
||||||
|
if param.getDataType() != self.type_importer.import_pdb_type_into_ghidra(
|
||||||
|
match.data_type
|
||||||
|
):
|
||||||
|
logger.error(
|
||||||
|
"Type mismatch for parameter: %s in Ghidra, %s in PDB", param, match
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
name = match.name
|
||||||
|
if name == "__formal":
|
||||||
|
# these can cause name collisions if multiple ones are present
|
||||||
|
name = f"__formal_{index}"
|
||||||
|
|
||||||
|
param.setName(name, SourceType.USER_DEFINED)
|
||||||
|
|
||||||
|
def get_matching_stack_symbol(self, stack_offset: int) -> Optional[CppStackSymbol]:
|
||||||
|
return next(
|
||||||
|
(
|
||||||
|
symbol
|
||||||
|
for symbol in self.signature.stack_symbols
|
||||||
|
if isinstance(symbol, CppStackSymbol)
|
||||||
|
and symbol.stack_offset == stack_offset
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_matching_register_symbol(
|
||||||
|
self, register: str
|
||||||
|
) -> Optional[CppRegisterSymbol]:
|
||||||
|
return next(
|
||||||
|
(
|
||||||
|
symbol
|
||||||
|
for symbol in self.signature.stack_symbols
|
||||||
|
if isinstance(symbol, CppRegisterSymbol) and symbol.register == register
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _set_this_adjust(
|
||||||
|
self,
|
||||||
|
ghidra_function: Function,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
When `this adjust` is non-zero, the pointer type of `this` needs to be replaced by an offset version.
|
||||||
|
The offset can only be set on a typedef on the pointer. We also must enable custom storage so we can modify
|
||||||
|
the auto-generated `this` parameter.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Necessary in order to overwite the auto-generated `this`
|
||||||
|
ghidra_function.setCustomVariableStorage(True)
|
||||||
|
|
||||||
|
this_parameter = next(
|
||||||
|
(
|
||||||
|
param
|
||||||
|
for param in ghidra_function.getParameters()
|
||||||
|
if param.isRegisterVariable() and param.getName() == "this"
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
|
||||||
|
if this_parameter is None:
|
||||||
|
logger.error(
|
||||||
|
"Failed to find `this` parameter in a function with `this adjust = %d`",
|
||||||
|
self.signature.this_adjust,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
current_ghidra_type = this_parameter.getDataType()
|
||||||
|
assert isinstance(current_ghidra_type, Pointer)
|
||||||
|
class_name = current_ghidra_type.getDataType().getName()
|
||||||
|
typedef_name = f"{class_name}PtrOffset0x{self.signature.this_adjust:x}"
|
||||||
|
|
||||||
|
typedef_ghidra_type = TypedefDataType(
|
||||||
|
current_ghidra_type.getCategoryPath(),
|
||||||
|
typedef_name,
|
||||||
|
current_ghidra_type,
|
||||||
|
)
|
||||||
|
ComponentOffsetSettingsDefinition.DEF.setValue(
|
||||||
|
typedef_ghidra_type.getDefaultSettings(), self.signature.this_adjust
|
||||||
|
)
|
||||||
|
typedef_ghidra_type = add_data_type_or_reuse_existing(
|
||||||
|
self.api, typedef_ghidra_type
|
||||||
|
)
|
||||||
|
|
||||||
|
this_parameter.setDataType(typedef_ghidra_type, SourceType.USER_DEFINED)
|
||||||
122
tools/ghidra_scripts/lego_util/ghidra_helper.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
"""A collection of helper functions for the interaction with Ghidra."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
from lego_util.exceptions import (
|
||||||
|
ClassOrNamespaceNotFoundInGhidraError,
|
||||||
|
TypeNotFoundInGhidraError,
|
||||||
|
MultipleTypesFoundInGhidraError,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Disable spurious warnings in vscode / pylance
|
||||||
|
# pyright: reportMissingModuleSource=false
|
||||||
|
|
||||||
|
from ghidra.program.flatapi import FlatProgramAPI
|
||||||
|
from ghidra.program.model.data import DataType, DataTypeConflictHandler, PointerDataType
|
||||||
|
from ghidra.program.model.symbol import Namespace
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def get_ghidra_type(api: FlatProgramAPI, type_name: str):
|
||||||
|
"""
|
||||||
|
Searches for the type named `typeName` in Ghidra.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
- NotFoundInGhidraError
|
||||||
|
- MultipleTypesFoundInGhidraError
|
||||||
|
"""
|
||||||
|
result = api.getDataTypes(type_name)
|
||||||
|
if len(result) == 0:
|
||||||
|
raise TypeNotFoundInGhidraError(type_name)
|
||||||
|
if len(result) == 1:
|
||||||
|
return result[0]
|
||||||
|
|
||||||
|
raise MultipleTypesFoundInGhidraError(type_name, result)
|
||||||
|
|
||||||
|
|
||||||
|
def get_or_add_pointer_type(api: FlatProgramAPI, pointee: DataType) -> DataType:
|
||||||
|
new_pointer_data_type = PointerDataType(pointee)
|
||||||
|
new_pointer_data_type.setCategoryPath(pointee.getCategoryPath())
|
||||||
|
return add_data_type_or_reuse_existing(api, new_pointer_data_type)
|
||||||
|
|
||||||
|
|
||||||
|
def add_data_type_or_reuse_existing(
|
||||||
|
api: FlatProgramAPI, new_data_type: DataType
|
||||||
|
) -> DataType:
|
||||||
|
result_data_type = (
|
||||||
|
api.getCurrentProgram()
|
||||||
|
.getDataTypeManager()
|
||||||
|
.addDataType(new_data_type, DataTypeConflictHandler.KEEP_HANDLER)
|
||||||
|
)
|
||||||
|
if result_data_type is not new_data_type:
|
||||||
|
logger.debug(
|
||||||
|
"Reusing existing data type instead of new one: %s (class: %s)",
|
||||||
|
result_data_type,
|
||||||
|
result_data_type.__class__,
|
||||||
|
)
|
||||||
|
return result_data_type
|
||||||
|
|
||||||
|
|
||||||
|
def get_ghidra_namespace(
|
||||||
|
api: FlatProgramAPI, namespace_hierachy: list[str]
|
||||||
|
) -> Namespace:
|
||||||
|
namespace = api.getCurrentProgram().getGlobalNamespace()
|
||||||
|
for part in namespace_hierachy:
|
||||||
|
namespace = api.getNamespace(namespace, part)
|
||||||
|
if namespace is None:
|
||||||
|
raise ClassOrNamespaceNotFoundInGhidraError(namespace_hierachy)
|
||||||
|
return namespace
|
||||||
|
|
||||||
|
|
||||||
|
def create_ghidra_namespace(
|
||||||
|
api: FlatProgramAPI, namespace_hierachy: list[str]
|
||||||
|
) -> Namespace:
|
||||||
|
namespace = api.getCurrentProgram().getGlobalNamespace()
|
||||||
|
for part in namespace_hierachy:
|
||||||
|
namespace = api.getNamespace(namespace, part)
|
||||||
|
if namespace is None:
|
||||||
|
namespace = api.createNamespace(namespace, part)
|
||||||
|
return namespace
|
||||||
|
|
||||||
|
|
||||||
|
# These appear in debug builds
|
||||||
|
THUNK_OF_RE = re.compile(r"^Thunk of '(.*)'$")
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_name(name: str) -> str:
|
||||||
|
"""
|
||||||
|
Takes a full class or function name and replaces characters not accepted by Ghidra.
|
||||||
|
Applies mostly to templates, names like `vbase destructor`, and thunks in debug build.
|
||||||
|
"""
|
||||||
|
if (match := THUNK_OF_RE.fullmatch(name)) is not None:
|
||||||
|
is_thunk = True
|
||||||
|
name = match.group(1)
|
||||||
|
else:
|
||||||
|
is_thunk = False
|
||||||
|
|
||||||
|
# Replace characters forbidden in Ghidra
|
||||||
|
new_name = (
|
||||||
|
name.replace("<", "[")
|
||||||
|
.replace(">", "]")
|
||||||
|
.replace("*", "#")
|
||||||
|
.replace(" ", "_")
|
||||||
|
.replace("`", "'")
|
||||||
|
)
|
||||||
|
|
||||||
|
if "<" in name:
|
||||||
|
new_name = "_template_" + new_name
|
||||||
|
|
||||||
|
if is_thunk:
|
||||||
|
split = new_name.split("::")
|
||||||
|
split[-1] = "_thunk_" + split[-1]
|
||||||
|
new_name = "::".join(split)
|
||||||
|
|
||||||
|
if new_name != name:
|
||||||
|
logger.info(
|
||||||
|
"Changed class or function name from '%s' to '%s' to avoid Ghidra issues",
|
||||||
|
name,
|
||||||
|
new_name,
|
||||||
|
)
|
||||||
|
return new_name
|
||||||
20
tools/ghidra_scripts/lego_util/headers.pyi
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from typing import TypeVar, Any
|
||||||
|
import ghidra
|
||||||
|
|
||||||
|
# pylint: disable=invalid-name,unused-argument
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
# from ghidra.app.script.GhidraScript
|
||||||
|
def currentProgram() -> "ghidra.program.model.listing.Program": ...
|
||||||
|
def getAddressFactory() -> " ghidra.program.model.address.AddressFactory": ...
|
||||||
|
def state() -> "ghidra.app.script.GhidraState": ...
|
||||||
|
def askChoice(title: str, message: str, choices: list[T], defaultValue: T) -> T: ...
|
||||||
|
def askYesNo(title: str, question: str) -> bool: ...
|
||||||
|
def getFunctionAt(
|
||||||
|
entryPoint: ghidra.program.model.address.Address,
|
||||||
|
) -> ghidra.program.model.listing.Function: ...
|
||||||
|
def createFunction(
|
||||||
|
entryPoint: ghidra.program.model.address.Address, name: str
|
||||||
|
) -> ghidra.program.model.listing.Function: ...
|
||||||
|
def getProgramFile() -> Any: ... # actually java.io.File
|
||||||
182
tools/ghidra_scripts/lego_util/pdb_extraction.py
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
import re
|
||||||
|
from typing import Any, Optional
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from isledecomp.bin import InvalidVirtualAddressError
|
||||||
|
from isledecomp.cvdump.symbols import SymbolsEntry
|
||||||
|
from isledecomp.compare import Compare as IsleCompare
|
||||||
|
from isledecomp.compare.db import MatchInfo
|
||||||
|
|
||||||
|
logger = logging.getLogger(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CppStackOrRegisterSymbol:
|
||||||
|
name: str
|
||||||
|
data_type: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CppStackSymbol(CppStackOrRegisterSymbol):
|
||||||
|
stack_offset: int
|
||||||
|
"""Should have a value iff `symbol_type=='S_BPREL32'."""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CppRegisterSymbol(CppStackOrRegisterSymbol):
|
||||||
|
register: str
|
||||||
|
"""Should have a value iff `symbol_type=='S_REGISTER'.` Should always be set/converted to lowercase."""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FunctionSignature:
|
||||||
|
original_function_symbol: SymbolsEntry
|
||||||
|
call_type: str
|
||||||
|
arglist: list[str]
|
||||||
|
return_type: str
|
||||||
|
class_type: Optional[str]
|
||||||
|
stack_symbols: list[CppStackOrRegisterSymbol]
|
||||||
|
# if non-zero: an offset to the `this` parameter in a __thiscall
|
||||||
|
this_adjust: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PdbFunction:
|
||||||
|
match_info: MatchInfo
|
||||||
|
signature: Optional[FunctionSignature]
|
||||||
|
is_stub: bool
|
||||||
|
|
||||||
|
|
||||||
|
class PdbFunctionExtractor:
|
||||||
|
"""
|
||||||
|
Extracts all information on a given function from the parsed PDB
|
||||||
|
and prepares the data for the import in Ghidra.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, compare: IsleCompare):
|
||||||
|
self.compare = compare
|
||||||
|
|
||||||
|
scalar_type_regex = re.compile(r"t_(?P<typename>\w+)(?:\((?P<type_id>\d+)\))?")
|
||||||
|
|
||||||
|
_call_type_map = {
|
||||||
|
"ThisCall": "__thiscall",
|
||||||
|
"C Near": "default",
|
||||||
|
"STD Near": "__stdcall",
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_cvdump_type(self, type_name: Optional[str]) -> Optional[dict[str, Any]]:
|
||||||
|
return (
|
||||||
|
None
|
||||||
|
if type_name is None
|
||||||
|
else self.compare.cv.types.keys.get(type_name.lower())
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_func_signature(self, fn: SymbolsEntry) -> Optional[FunctionSignature]:
|
||||||
|
function_type_str = fn.func_type
|
||||||
|
if function_type_str == "T_NOTYPE(0000)":
|
||||||
|
logger.debug("Treating NOTYPE function as thunk: %s", fn.name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# get corresponding function type
|
||||||
|
|
||||||
|
function_type = self.compare.cv.types.keys.get(function_type_str.lower())
|
||||||
|
if function_type is None:
|
||||||
|
logger.error(
|
||||||
|
"Could not find function type %s for function %s", fn.func_type, fn.name
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
class_type = function_type.get("class_type")
|
||||||
|
|
||||||
|
arg_list_type = self._get_cvdump_type(function_type.get("arg_list_type"))
|
||||||
|
assert arg_list_type is not None
|
||||||
|
arg_list_pdb_types = arg_list_type.get("args", [])
|
||||||
|
assert arg_list_type["argcount"] == len(arg_list_pdb_types)
|
||||||
|
|
||||||
|
stack_symbols: list[CppStackOrRegisterSymbol] = []
|
||||||
|
|
||||||
|
# for some unexplained reason, the reported stack is offset by 4 when this flag is set
|
||||||
|
stack_offset_delta = -4 if fn.frame_pointer_present else 0
|
||||||
|
|
||||||
|
for symbol in fn.stack_symbols:
|
||||||
|
if symbol.symbol_type == "S_REGISTER":
|
||||||
|
stack_symbols.append(
|
||||||
|
CppRegisterSymbol(
|
||||||
|
symbol.name,
|
||||||
|
symbol.data_type,
|
||||||
|
symbol.location,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif symbol.symbol_type == "S_BPREL32":
|
||||||
|
stack_offset = int(symbol.location[1:-1], 16)
|
||||||
|
stack_symbols.append(
|
||||||
|
CppStackSymbol(
|
||||||
|
symbol.name,
|
||||||
|
symbol.data_type,
|
||||||
|
stack_offset + stack_offset_delta,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
call_type = self._call_type_map[function_type["call_type"]]
|
||||||
|
|
||||||
|
# parse as hex number, default to 0
|
||||||
|
this_adjust = int(function_type.get("this_adjust", "0"), 16)
|
||||||
|
|
||||||
|
return FunctionSignature(
|
||||||
|
original_function_symbol=fn,
|
||||||
|
call_type=call_type,
|
||||||
|
arglist=arg_list_pdb_types,
|
||||||
|
return_type=function_type["return_type"],
|
||||||
|
class_type=class_type,
|
||||||
|
stack_symbols=stack_symbols,
|
||||||
|
this_adjust=this_adjust,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_function_list(self) -> list[PdbFunction]:
|
||||||
|
handled = (
|
||||||
|
self.handle_matched_function(match)
|
||||||
|
for match in self.compare.get_functions()
|
||||||
|
)
|
||||||
|
return [signature for signature in handled if signature is not None]
|
||||||
|
|
||||||
|
def handle_matched_function(self, match_info: MatchInfo) -> Optional[PdbFunction]:
|
||||||
|
assert match_info.orig_addr is not None
|
||||||
|
match_options = self.compare.get_match_options(match_info.orig_addr)
|
||||||
|
assert match_options is not None
|
||||||
|
|
||||||
|
function_data = next(
|
||||||
|
(
|
||||||
|
y
|
||||||
|
for y in self.compare.cvdump_analysis.nodes
|
||||||
|
if y.addr == match_info.recomp_addr
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
if function_data is None:
|
||||||
|
try:
|
||||||
|
# this can be either a thunk (which we want) or an external function
|
||||||
|
# (which we don't want), so we tell them apart based on the validity of their address.
|
||||||
|
self.compare.orig_bin.get_relative_addr(match_info.orig_addr)
|
||||||
|
return PdbFunction(match_info, None, False)
|
||||||
|
except InvalidVirtualAddressError:
|
||||||
|
logger.debug(
|
||||||
|
"Skipping external function %s (address 0x%x not in original binary)",
|
||||||
|
match_info.name,
|
||||||
|
match_info.orig_addr,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
function_symbol = function_data.symbol_entry
|
||||||
|
if function_symbol is None:
|
||||||
|
logger.debug(
|
||||||
|
"Could not find function symbol (likely a PUBLICS entry): %s",
|
||||||
|
match_info.name,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
function_signature = self.get_func_signature(function_symbol)
|
||||||
|
|
||||||
|
is_stub = match_options.get("stub", False)
|
||||||
|
|
||||||
|
return PdbFunction(match_info, function_signature, is_stub)
|
||||||
68
tools/ghidra_scripts/lego_util/statistics.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from lego_util.exceptions import (
|
||||||
|
TypeNotFoundInGhidraError,
|
||||||
|
ClassOrNamespaceNotFoundInGhidraError,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Statistics:
|
||||||
|
functions_changed: int = 0
|
||||||
|
successes: int = 0
|
||||||
|
failures: dict[str, int] = field(default_factory=dict)
|
||||||
|
known_missing_types: dict[str, int] = field(default_factory=dict)
|
||||||
|
known_missing_namespaces: dict[str, int] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def track_failure_and_tell_if_new(self, error: Exception) -> bool:
|
||||||
|
"""
|
||||||
|
Adds the error to the statistics. Returns `False` if logging the error would be redundant
|
||||||
|
(e.g. because it is a `TypeNotFoundInGhidraError` with a type that has been logged before).
|
||||||
|
"""
|
||||||
|
error_type_name = error.__class__.__name__
|
||||||
|
self.failures[error_type_name] = (
|
||||||
|
self.failures.setdefault(error_type_name, 0) + 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(error, TypeNotFoundInGhidraError):
|
||||||
|
return self._add_occurence_and_check_if_new(
|
||||||
|
self.known_missing_types, error.args[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(error, ClassOrNamespaceNotFoundInGhidraError):
|
||||||
|
return self._add_occurence_and_check_if_new(
|
||||||
|
self.known_missing_namespaces, error.get_namespace_str()
|
||||||
|
)
|
||||||
|
|
||||||
|
# We do not have detailed tracking for other errors, so we want to log them every time
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _add_occurence_and_check_if_new(self, target: dict[str, int], key: str) -> bool:
|
||||||
|
old_count = target.setdefault(key, 0)
|
||||||
|
target[key] = old_count + 1
|
||||||
|
return old_count == 0
|
||||||
|
|
||||||
|
def log(self):
|
||||||
|
logger.info("Statistics:\n~~~~~")
|
||||||
|
logger.info(
|
||||||
|
"Missing types (with number of occurences): %s\n~~~~~",
|
||||||
|
self.format_statistics(self.known_missing_types),
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"Missing classes/namespaces (with number of occurences): %s\n~~~~~",
|
||||||
|
self.format_statistics(self.known_missing_namespaces),
|
||||||
|
)
|
||||||
|
logger.info("Successes: %d", self.successes)
|
||||||
|
logger.info("Failures: %s", self.failures)
|
||||||
|
logger.info("Functions changed: %d", self.functions_changed)
|
||||||
|
|
||||||
|
def format_statistics(self, stats: dict[str, int]) -> str:
|
||||||
|
if len(stats) == 0:
|
||||||
|
return "<none>"
|
||||||
|
return ", ".join(
|
||||||
|
f"{entry[0]} ({entry[1]})"
|
||||||
|
for entry in sorted(stats.items(), key=lambda x: x[1], reverse=True)
|
||||||
|
)
|
||||||
541
tools/ghidra_scripts/lego_util/type_importer.py
Normal file
@ -0,0 +1,541 @@
|
|||||||
|
import logging
|
||||||
|
from typing import Any, Callable, Iterator, Optional, TypeVar
|
||||||
|
|
||||||
|
# Disable spurious warnings in vscode / pylance
|
||||||
|
# pyright: reportMissingModuleSource=false
|
||||||
|
|
||||||
|
# pylint: disable=too-many-return-statements # a `match` would be better, but for now we are stuck with Python 3.9
|
||||||
|
# pylint: disable=no-else-return # Not sure why this rule even is a thing, this is great for checking exhaustiveness
|
||||||
|
|
||||||
|
from isledecomp.cvdump.types import VirtualBasePointer
|
||||||
|
from lego_util.exceptions import (
|
||||||
|
ClassOrNamespaceNotFoundInGhidraError,
|
||||||
|
TypeNotFoundError,
|
||||||
|
TypeNotFoundInGhidraError,
|
||||||
|
TypeNotImplementedError,
|
||||||
|
StructModificationError,
|
||||||
|
)
|
||||||
|
from lego_util.ghidra_helper import (
|
||||||
|
add_data_type_or_reuse_existing,
|
||||||
|
get_or_add_pointer_type,
|
||||||
|
create_ghidra_namespace,
|
||||||
|
get_ghidra_namespace,
|
||||||
|
get_ghidra_type,
|
||||||
|
sanitize_name,
|
||||||
|
)
|
||||||
|
from lego_util.pdb_extraction import PdbFunctionExtractor
|
||||||
|
|
||||||
|
from ghidra.program.flatapi import FlatProgramAPI
|
||||||
|
from ghidra.program.model.data import (
|
||||||
|
ArrayDataType,
|
||||||
|
CategoryPath,
|
||||||
|
DataType,
|
||||||
|
DataTypeConflictHandler,
|
||||||
|
Enum,
|
||||||
|
EnumDataType,
|
||||||
|
StructureDataType,
|
||||||
|
StructureInternal,
|
||||||
|
TypedefDataType,
|
||||||
|
ComponentOffsetSettingsDefinition,
|
||||||
|
)
|
||||||
|
from ghidra.util.task import ConsoleTaskMonitor
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class PdbTypeImporter:
|
||||||
|
"""Allows PDB types to be imported into Ghidra."""
|
||||||
|
|
||||||
|
def __init__(self, api: FlatProgramAPI, extraction: PdbFunctionExtractor):
|
||||||
|
self.api = api
|
||||||
|
self.extraction = extraction
|
||||||
|
# tracks the structs/classes we have already started to import, otherwise we run into infinite recursion
|
||||||
|
self.handled_structs: set[str] = set()
|
||||||
|
|
||||||
|
# tracks the enums we have already handled for the sake of efficiency
|
||||||
|
self.handled_enums: dict[str, Enum] = {}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def types(self):
|
||||||
|
return self.extraction.compare.cv.types
|
||||||
|
|
||||||
|
def import_pdb_type_into_ghidra(
|
||||||
|
self, type_index: str, slim_for_vbase: bool = False
|
||||||
|
) -> DataType:
|
||||||
|
"""
|
||||||
|
Recursively imports a type from the PDB into Ghidra.
|
||||||
|
@param type_index Either a scalar type like `T_INT4(...)` or a PDB reference like `0x10ba`
|
||||||
|
@param slim_for_vbase If true, the current invocation
|
||||||
|
imports a superclass of some class where virtual inheritance is involved (directly or indirectly).
|
||||||
|
This case requires special handling: Let's say we have `class C: B` and `class B: virtual A`. Then cvdump
|
||||||
|
reports a size for B that includes both B's fields as well as the A contained at an offset within B,
|
||||||
|
which is not the correct structure to be contained in C. Therefore, we need to create a "slim" version of B
|
||||||
|
that fits inside C.
|
||||||
|
This value should always be `False` when the referenced type is not (a pointer to) a class.
|
||||||
|
"""
|
||||||
|
type_index_lower = type_index.lower()
|
||||||
|
if type_index_lower.startswith("t_"):
|
||||||
|
return self._import_scalar_type(type_index_lower)
|
||||||
|
|
||||||
|
try:
|
||||||
|
type_pdb = self.extraction.compare.cv.types.keys[type_index_lower]
|
||||||
|
except KeyError as e:
|
||||||
|
raise TypeNotFoundError(
|
||||||
|
f"Failed to find referenced type '{type_index_lower}'"
|
||||||
|
) from e
|
||||||
|
|
||||||
|
type_category = type_pdb["type"]
|
||||||
|
|
||||||
|
# follow forward reference (class, struct, union)
|
||||||
|
if type_pdb.get("is_forward_ref", False):
|
||||||
|
return self._import_forward_ref_type(
|
||||||
|
type_index_lower, type_pdb, slim_for_vbase
|
||||||
|
)
|
||||||
|
|
||||||
|
if type_category == "LF_POINTER":
|
||||||
|
return get_or_add_pointer_type(
|
||||||
|
self.api,
|
||||||
|
self.import_pdb_type_into_ghidra(
|
||||||
|
type_pdb["element_type"], slim_for_vbase
|
||||||
|
),
|
||||||
|
)
|
||||||
|
elif type_category in ["LF_CLASS", "LF_STRUCTURE"]:
|
||||||
|
return self._import_class_or_struct(type_pdb, slim_for_vbase)
|
||||||
|
elif type_category == "LF_ARRAY":
|
||||||
|
return self._import_array(type_pdb)
|
||||||
|
elif type_category == "LF_ENUM":
|
||||||
|
return self._import_enum(type_pdb)
|
||||||
|
elif type_category == "LF_PROCEDURE":
|
||||||
|
logger.warning(
|
||||||
|
"Not implemented: Function-valued argument or return type will be replaced by void pointer: %s",
|
||||||
|
type_pdb,
|
||||||
|
)
|
||||||
|
return get_ghidra_type(self.api, "void")
|
||||||
|
elif type_category == "LF_UNION":
|
||||||
|
return self._import_union(type_pdb)
|
||||||
|
else:
|
||||||
|
raise TypeNotImplementedError(type_pdb)
|
||||||
|
|
||||||
|
_scalar_type_map = {
|
||||||
|
"rchar": "char",
|
||||||
|
"int4": "int",
|
||||||
|
"uint4": "uint",
|
||||||
|
"real32": "float",
|
||||||
|
"real64": "double",
|
||||||
|
}
|
||||||
|
|
||||||
|
def _scalar_type_to_cpp(self, scalar_type: str) -> str:
|
||||||
|
if scalar_type.startswith("32p"):
|
||||||
|
return f"{self._scalar_type_to_cpp(scalar_type[3:])} *"
|
||||||
|
return self._scalar_type_map.get(scalar_type, scalar_type)
|
||||||
|
|
||||||
|
def _import_scalar_type(self, type_index_lower: str) -> DataType:
|
||||||
|
if (match := self.extraction.scalar_type_regex.match(type_index_lower)) is None:
|
||||||
|
raise TypeNotFoundError(f"Type has unexpected format: {type_index_lower}")
|
||||||
|
|
||||||
|
scalar_cpp_type = self._scalar_type_to_cpp(match.group("typename"))
|
||||||
|
return get_ghidra_type(self.api, scalar_cpp_type)
|
||||||
|
|
||||||
|
def _import_forward_ref_type(
|
||||||
|
self,
|
||||||
|
type_index,
|
||||||
|
type_pdb: dict[str, Any],
|
||||||
|
slim_for_vbase: bool = False,
|
||||||
|
) -> DataType:
|
||||||
|
referenced_type = type_pdb.get("udt") or type_pdb.get("modifies")
|
||||||
|
if referenced_type is None:
|
||||||
|
try:
|
||||||
|
# Example: HWND__, needs to be created manually
|
||||||
|
return get_ghidra_type(self.api, type_pdb["name"])
|
||||||
|
except TypeNotFoundInGhidraError as e:
|
||||||
|
raise TypeNotImplementedError(
|
||||||
|
f"{type_index}: forward ref without target, needs to be created manually: {type_pdb}"
|
||||||
|
) from e
|
||||||
|
logger.debug(
|
||||||
|
"Following forward reference from %s to %s",
|
||||||
|
type_index,
|
||||||
|
referenced_type,
|
||||||
|
)
|
||||||
|
return self.import_pdb_type_into_ghidra(referenced_type, slim_for_vbase)
|
||||||
|
|
||||||
|
def _import_array(self, type_pdb: dict[str, Any]) -> DataType:
|
||||||
|
inner_type = self.import_pdb_type_into_ghidra(type_pdb["array_type"])
|
||||||
|
|
||||||
|
array_total_bytes: int = type_pdb["size"]
|
||||||
|
data_type_size = inner_type.getLength()
|
||||||
|
array_length, modulus = divmod(array_total_bytes, data_type_size)
|
||||||
|
assert (
|
||||||
|
modulus == 0
|
||||||
|
), f"Data type size {data_type_size} does not divide array size {array_total_bytes}"
|
||||||
|
|
||||||
|
return ArrayDataType(inner_type, array_length, 0)
|
||||||
|
|
||||||
|
def _import_union(self, type_pdb: dict[str, Any]) -> DataType:
|
||||||
|
try:
|
||||||
|
logger.debug("Dereferencing union %s", type_pdb)
|
||||||
|
union_type = get_ghidra_type(self.api, type_pdb["name"])
|
||||||
|
assert (
|
||||||
|
union_type.getLength() == type_pdb["size"]
|
||||||
|
), f"Wrong size of existing union type '{type_pdb['name']}': expected {type_pdb['size']}, got {union_type.getLength()}"
|
||||||
|
return union_type
|
||||||
|
except TypeNotFoundInGhidraError as e:
|
||||||
|
# We have so few instances, it is not worth implementing this
|
||||||
|
raise TypeNotImplementedError(
|
||||||
|
f"Writing union types is not supported. Please add by hand: {type_pdb}"
|
||||||
|
) from e
|
||||||
|
|
||||||
|
def _import_enum(self, type_pdb: dict[str, Any]) -> DataType:
|
||||||
|
underlying_type = self.import_pdb_type_into_ghidra(type_pdb["underlying_type"])
|
||||||
|
field_list = self.extraction.compare.cv.types.keys.get(type_pdb["field_type"])
|
||||||
|
assert field_list is not None, f"Failed to find field list for enum {type_pdb}"
|
||||||
|
|
||||||
|
result = self._get_or_create_enum_data_type(
|
||||||
|
type_pdb["name"], underlying_type.getLength()
|
||||||
|
)
|
||||||
|
# clear existing variant if there are any
|
||||||
|
for existing_variant in result.getNames():
|
||||||
|
result.remove(existing_variant)
|
||||||
|
|
||||||
|
variants: list[dict[str, Any]] = field_list["variants"]
|
||||||
|
for variant in variants:
|
||||||
|
result.add(variant["name"], variant["value"])
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _import_class_or_struct(
|
||||||
|
self,
|
||||||
|
type_in_pdb: dict[str, Any],
|
||||||
|
slim_for_vbase: bool = False,
|
||||||
|
) -> DataType:
|
||||||
|
field_list_type: str = type_in_pdb["field_list_type"]
|
||||||
|
field_list = self.types.keys[field_list_type.lower()]
|
||||||
|
|
||||||
|
class_size: int = type_in_pdb["size"]
|
||||||
|
class_name_with_namespace: str = sanitize_name(type_in_pdb["name"])
|
||||||
|
if slim_for_vbase:
|
||||||
|
class_name_with_namespace += "_vbase_slim"
|
||||||
|
|
||||||
|
if class_name_with_namespace in self.handled_structs:
|
||||||
|
logger.debug(
|
||||||
|
"Class has been handled or is being handled: %s",
|
||||||
|
class_name_with_namespace,
|
||||||
|
)
|
||||||
|
return get_ghidra_type(self.api, class_name_with_namespace)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
"--- Beginning to import class/struct '%s'", class_name_with_namespace
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add as soon as we start to avoid infinite recursion
|
||||||
|
self.handled_structs.add(class_name_with_namespace)
|
||||||
|
|
||||||
|
self._get_or_create_namespace(class_name_with_namespace)
|
||||||
|
|
||||||
|
new_ghidra_struct = self._get_or_create_struct_data_type(
|
||||||
|
class_name_with_namespace, class_size
|
||||||
|
)
|
||||||
|
|
||||||
|
if (old_size := new_ghidra_struct.getLength()) != class_size:
|
||||||
|
logger.warning(
|
||||||
|
"Existing class %s had incorrect size %d. Setting to %d...",
|
||||||
|
class_name_with_namespace,
|
||||||
|
old_size,
|
||||||
|
class_size,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("Adding class data type %s", class_name_with_namespace)
|
||||||
|
logger.debug("Class information: %s", type_in_pdb)
|
||||||
|
|
||||||
|
components: list[dict[str, Any]] = []
|
||||||
|
components.extend(self._get_components_from_base_classes(field_list))
|
||||||
|
# can be missing when no new fields are declared
|
||||||
|
components.extend(self._get_components_from_members(field_list))
|
||||||
|
components.extend(
|
||||||
|
self._get_components_from_vbase(
|
||||||
|
field_list, class_name_with_namespace, new_ghidra_struct
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
components.sort(key=lambda c: c["offset"])
|
||||||
|
|
||||||
|
if slim_for_vbase:
|
||||||
|
# Make a "slim" version: shrink the size to the fields that are actually present.
|
||||||
|
# This makes a difference when the current class uses virtual inheritance
|
||||||
|
assert (
|
||||||
|
len(components) > 0
|
||||||
|
), f"Error: {class_name_with_namespace} should not be empty. There must be at least one direct or indirect vbase pointer."
|
||||||
|
last_component = components[-1]
|
||||||
|
class_size = last_component["offset"] + last_component["type"].getLength()
|
||||||
|
|
||||||
|
self._overwrite_struct(
|
||||||
|
class_name_with_namespace,
|
||||||
|
new_ghidra_struct,
|
||||||
|
class_size,
|
||||||
|
components,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("Finished importing class %s", class_name_with_namespace)
|
||||||
|
|
||||||
|
return new_ghidra_struct
|
||||||
|
|
||||||
|
def _get_components_from_base_classes(self, field_list) -> Iterator[dict[str, Any]]:
|
||||||
|
non_virtual_base_classes: dict[str, int] = field_list.get("super", {})
|
||||||
|
|
||||||
|
for super_type, offset in non_virtual_base_classes.items():
|
||||||
|
# If we have virtual inheritance _and_ a non-virtual base class here, we play safe and import slim version.
|
||||||
|
# This is technically not needed if only one of the superclasses uses virtual inheritance, but I am not aware of any instance.
|
||||||
|
import_slim_vbase_version_of_superclass = "vbase" in field_list
|
||||||
|
ghidra_type = self.import_pdb_type_into_ghidra(
|
||||||
|
super_type, slim_for_vbase=import_slim_vbase_version_of_superclass
|
||||||
|
)
|
||||||
|
|
||||||
|
yield {
|
||||||
|
"type": ghidra_type,
|
||||||
|
"offset": offset,
|
||||||
|
"name": "base" if offset == 0 else f"base_{ghidra_type.getName()}",
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_components_from_members(self, field_list: dict[str, Any]):
|
||||||
|
members: list[dict[str, Any]] = field_list.get("members") or []
|
||||||
|
for member in members:
|
||||||
|
yield member | {"type": self.import_pdb_type_into_ghidra(member["type"])}
|
||||||
|
|
||||||
|
def _get_components_from_vbase(
|
||||||
|
self,
|
||||||
|
field_list: dict[str, Any],
|
||||||
|
class_name_with_namespace: str,
|
||||||
|
current_type: StructureInternal,
|
||||||
|
) -> Iterator[dict[str, Any]]:
|
||||||
|
vbasepointer: Optional[VirtualBasePointer] = field_list.get("vbase", None)
|
||||||
|
|
||||||
|
if vbasepointer is not None and any(x.direct for x in vbasepointer.bases):
|
||||||
|
vbaseptr_type = get_or_add_pointer_type(
|
||||||
|
self.api,
|
||||||
|
self._import_vbaseptr(
|
||||||
|
current_type, class_name_with_namespace, vbasepointer
|
||||||
|
),
|
||||||
|
)
|
||||||
|
yield {
|
||||||
|
"type": vbaseptr_type,
|
||||||
|
"offset": vbasepointer.vboffset,
|
||||||
|
"name": "vbase_offset",
|
||||||
|
}
|
||||||
|
|
||||||
|
def _import_vbaseptr(
|
||||||
|
self,
|
||||||
|
current_type: StructureInternal,
|
||||||
|
class_name_with_namespace: str,
|
||||||
|
vbasepointer: VirtualBasePointer,
|
||||||
|
) -> StructureInternal:
|
||||||
|
pointer_size = 4 # hard-code to 4 because of 32 bit
|
||||||
|
|
||||||
|
components = [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"type": get_or_add_pointer_type(self.api, current_type),
|
||||||
|
"name": "o_self",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
for vbase in vbasepointer.bases:
|
||||||
|
vbase_ghidra_type = self.import_pdb_type_into_ghidra(vbase.type)
|
||||||
|
|
||||||
|
type_name = vbase_ghidra_type.getName()
|
||||||
|
|
||||||
|
vbase_ghidra_pointer = get_or_add_pointer_type(self.api, vbase_ghidra_type)
|
||||||
|
vbase_ghidra_pointer_typedef = TypedefDataType(
|
||||||
|
vbase_ghidra_pointer.getCategoryPath(),
|
||||||
|
f"{type_name}PtrOffset",
|
||||||
|
vbase_ghidra_pointer,
|
||||||
|
)
|
||||||
|
# Set a default value of -4 for the pointer offset. While this appears to be correct in many cases,
|
||||||
|
# it does not always lead to the best decompile. It can be fine-tuned by hand; the next function call
|
||||||
|
# makes sure that we don't overwrite this value on re-running the import.
|
||||||
|
ComponentOffsetSettingsDefinition.DEF.setValue(
|
||||||
|
vbase_ghidra_pointer_typedef.getDefaultSettings(), -4
|
||||||
|
)
|
||||||
|
|
||||||
|
vbase_ghidra_pointer_typedef = add_data_type_or_reuse_existing(
|
||||||
|
self.api, vbase_ghidra_pointer_typedef
|
||||||
|
)
|
||||||
|
|
||||||
|
components.append(
|
||||||
|
{
|
||||||
|
"offset": vbase.index * pointer_size,
|
||||||
|
"type": vbase_ghidra_pointer_typedef,
|
||||||
|
"name": f"o_{type_name}",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
size = len(components) * pointer_size
|
||||||
|
|
||||||
|
new_ghidra_struct = self._get_or_create_struct_data_type(
|
||||||
|
f"{class_name_with_namespace}::VBasePtr", size
|
||||||
|
)
|
||||||
|
|
||||||
|
self._overwrite_struct(
|
||||||
|
f"{class_name_with_namespace}::VBasePtr",
|
||||||
|
new_ghidra_struct,
|
||||||
|
size,
|
||||||
|
components,
|
||||||
|
)
|
||||||
|
|
||||||
|
return new_ghidra_struct
|
||||||
|
|
||||||
|
def _overwrite_struct(
|
||||||
|
self,
|
||||||
|
class_name_with_namespace: str,
|
||||||
|
new_ghidra_struct: StructureInternal,
|
||||||
|
class_size: int,
|
||||||
|
components: list[dict[str, Any]],
|
||||||
|
):
|
||||||
|
new_ghidra_struct.deleteAll()
|
||||||
|
new_ghidra_struct.growStructure(class_size)
|
||||||
|
|
||||||
|
# this case happened e.g. for IUnknown, which linked to an (incorrect) existing library, and some other types as well.
|
||||||
|
# Unfortunately, we don't get proper error handling for read-only types.
|
||||||
|
# However, we really do NOT want to do this every time because the type might be self-referential and partially imported.
|
||||||
|
if new_ghidra_struct.getLength() != class_size:
|
||||||
|
new_ghidra_struct = self._delete_and_recreate_struct_data_type(
|
||||||
|
class_name_with_namespace, class_size, new_ghidra_struct
|
||||||
|
)
|
||||||
|
|
||||||
|
for component in components:
|
||||||
|
offset: int = component["offset"]
|
||||||
|
logger.debug(
|
||||||
|
"Adding component %s to class: %s", component, class_name_with_namespace
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Make sure there is room for the new structure and that we have no collision.
|
||||||
|
existing_type = new_ghidra_struct.getComponentAt(offset)
|
||||||
|
assert (
|
||||||
|
existing_type is not None
|
||||||
|
), f"Struct collision: Offset {offset} in {class_name_with_namespace} is overlapped by another component"
|
||||||
|
|
||||||
|
if existing_type.getDataType().getName() != "undefined":
|
||||||
|
# collision of structs beginning in the same place -> likely due to unions
|
||||||
|
logger.warning(
|
||||||
|
"Struct collision: Offset %d of %s already has a field (likely an inline union)",
|
||||||
|
offset,
|
||||||
|
class_name_with_namespace,
|
||||||
|
)
|
||||||
|
|
||||||
|
new_ghidra_struct.replaceAtOffset(
|
||||||
|
offset,
|
||||||
|
component["type"],
|
||||||
|
-1, # set to -1 for fixed-size components
|
||||||
|
component["name"], # name
|
||||||
|
None, # comment
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
raise StructModificationError(class_name_with_namespace) from e
|
||||||
|
|
||||||
|
def _get_or_create_namespace(self, class_name_with_namespace: str):
|
||||||
|
colon_split = class_name_with_namespace.split("::")
|
||||||
|
class_name = colon_split[-1]
|
||||||
|
try:
|
||||||
|
get_ghidra_namespace(self.api, colon_split)
|
||||||
|
logger.debug("Found existing class/namespace %s", class_name_with_namespace)
|
||||||
|
except ClassOrNamespaceNotFoundInGhidraError:
|
||||||
|
logger.info("Creating class/namespace %s", class_name_with_namespace)
|
||||||
|
class_name = colon_split.pop()
|
||||||
|
parent_namespace = create_ghidra_namespace(self.api, colon_split)
|
||||||
|
self.api.createClass(parent_namespace, class_name)
|
||||||
|
|
||||||
|
def _get_or_create_enum_data_type(
|
||||||
|
self, enum_type_name: str, enum_type_size: int
|
||||||
|
) -> Enum:
|
||||||
|
if (known_enum := self.handled_enums.get(enum_type_name, None)) is not None:
|
||||||
|
return known_enum
|
||||||
|
|
||||||
|
result = self._get_or_create_data_type(
|
||||||
|
enum_type_name,
|
||||||
|
"enum",
|
||||||
|
Enum,
|
||||||
|
lambda: EnumDataType(
|
||||||
|
CategoryPath("/imported"), enum_type_name, enum_type_size
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self.handled_enums[enum_type_name] = result
|
||||||
|
return result
|
||||||
|
|
||||||
|
def _get_or_create_struct_data_type(
|
||||||
|
self, class_name_with_namespace: str, class_size: int
|
||||||
|
) -> StructureInternal:
|
||||||
|
return self._get_or_create_data_type(
|
||||||
|
class_name_with_namespace,
|
||||||
|
"class/struct",
|
||||||
|
StructureInternal,
|
||||||
|
lambda: StructureDataType(
|
||||||
|
CategoryPath("/imported"), class_name_with_namespace, class_size
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
T = TypeVar("T", bound=DataType)
|
||||||
|
|
||||||
|
def _get_or_create_data_type(
|
||||||
|
self,
|
||||||
|
type_name: str,
|
||||||
|
readable_name_of_type_category: str,
|
||||||
|
expected_type: type[T],
|
||||||
|
new_instance_callback: Callable[[], T],
|
||||||
|
) -> T:
|
||||||
|
"""
|
||||||
|
Checks if a data type provided under the given name exists in Ghidra.
|
||||||
|
Creates one using `new_instance_callback` if there is not.
|
||||||
|
Also verifies the data type.
|
||||||
|
|
||||||
|
Note that the return value of `addDataType()` is not the same instance as the input
|
||||||
|
even if there is no name collision.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
data_type = get_ghidra_type(self.api, type_name)
|
||||||
|
logger.debug(
|
||||||
|
"Found existing %s type %s under category path %s",
|
||||||
|
readable_name_of_type_category,
|
||||||
|
type_name,
|
||||||
|
data_type.getCategoryPath(),
|
||||||
|
)
|
||||||
|
except TypeNotFoundInGhidraError:
|
||||||
|
data_type = (
|
||||||
|
self.api.getCurrentProgram()
|
||||||
|
.getDataTypeManager()
|
||||||
|
.addDataType(
|
||||||
|
new_instance_callback(), DataTypeConflictHandler.KEEP_HANDLER
|
||||||
|
)
|
||||||
|
)
|
||||||
|
logger.info(
|
||||||
|
"Created new %s data type %s", readable_name_of_type_category, type_name
|
||||||
|
)
|
||||||
|
assert isinstance(
|
||||||
|
data_type, expected_type
|
||||||
|
), f"Found existing type named {type_name} that is not a {readable_name_of_type_category}"
|
||||||
|
return data_type
|
||||||
|
|
||||||
|
def _delete_and_recreate_struct_data_type(
|
||||||
|
self,
|
||||||
|
class_name_with_namespace: str,
|
||||||
|
class_size: int,
|
||||||
|
existing_data_type: DataType,
|
||||||
|
) -> StructureInternal:
|
||||||
|
logger.warning(
|
||||||
|
"Failed to modify data type %s. Will try to delete the existing one and re-create the imported one.",
|
||||||
|
class_name_with_namespace,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
self.api.getCurrentProgram()
|
||||||
|
.getDataTypeManager()
|
||||||
|
.remove(existing_data_type, ConsoleTaskMonitor())
|
||||||
|
), f"Failed to delete and re-create data type {class_name_with_namespace}"
|
||||||
|
data_type = StructureDataType(
|
||||||
|
CategoryPath("/imported"), class_name_with_namespace, class_size
|
||||||
|
)
|
||||||
|
data_type = (
|
||||||
|
self.api.getCurrentProgram()
|
||||||
|
.getDataTypeManager()
|
||||||
|
.addDataType(data_type, DataTypeConflictHandler.KEEP_HANDLER)
|
||||||
|
)
|
||||||
|
assert isinstance(data_type, StructureInternal) # for type checking
|
||||||
|
return data_type
|
||||||