please fucking work

This commit is contained in:
funniman.exe 2025-01-11 14:02:05 -08:00
parent ac3a551bcc
commit 29000061a5
177 changed files with 19223 additions and 241 deletions

24
.gitignore vendored
View File

@ -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

View File

@ -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()

View File

@ -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();
}

View File

@ -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()
}; };

View File

@ -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;

View File

@ -55,22 +55,22 @@ class CConfigApp : public CWinApp {
public: public:
LegoDeviceEnumerate* m_device_enumerator; // 0x0c4 LegoDeviceEnumerate* m_device_enumerator; // 0x0c4
MxDriver* m_driver; // 0x0c8 MxDriver* m_driver; // 0x0c8
Direct3DDeviceInfo* m_device; // 0x0cc Direct3DDeviceInfo* m_device; // 0x0cc
int m_display_bit_depth; // 0x0d0 int m_display_bit_depth; // 0x0d0
BOOL m_flip_surfaces; // 0x0d4 BOOL m_flip_surfaces; // 0x0d4
BOOL m_full_screen; // 0x0d8 BOOL m_full_screen; // 0x0d8
BOOL m_3d_video_ram; // 0x0dc BOOL m_3d_video_ram; // 0x0dc
BOOL m_wide_view_angle; // 0x0e0 BOOL m_wide_view_angle; // 0x0e0
BOOL m_3d_sound; // 0x0e4 BOOL m_3d_sound; // 0x0e4
BOOL m_draw_cursor; // 0x0e8 BOOL m_draw_cursor; // 0x0e8
BOOL m_use_joystick; // 0x0ec BOOL m_use_joystick; // 0x0ec
int m_joystick_index; // 0x0f0 int m_joystick_index; // 0x0f0
BOOL m_run_config_dialog; // 0x0f4 BOOL m_run_config_dialog; // 0x0f4
int m_model_quality; // 0x0f8 int m_model_quality; // 0x0f8
int m_texture_quality; // 0x0fc int m_texture_quality; // 0x0fc
undefined m_unk0x100[4]; // 0x100 undefined m_unk0x100[4]; // 0x100
BOOL m_music; // 0x104 BOOL m_music; // 0x104
}; };
// SYNTHETIC: CONFIG 0x00402cd0 // SYNTHETIC: CONFIG 0x00402cd0

BIN
CONFIG/res/CONFIG.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
CONFIG/res/config.aps Normal file

Binary file not shown.

View File

@ -1,82 +1,216 @@
#include "resource.h" //Microsoft Developer Studio generated resource script.
#include "afxres.h" //
#include "resrc1.h"
IDI_CONFIG ICON "lego.ico"
#define APSTUDIO_READONLY_SYMBOLS
IDB_SHARK BITMAP "shark.bmp" /////////////////////////////////////////////////////////////////////////////
//
IDD_ABOUT DIALOG 0, 0, 217, 55 // Generated from the TEXTINCLUDE 2 resource.
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU //
CAPTION "About Configure LEGO\xAE Island" #include "resource.h"
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #include "afxres.h"
FONT 8, "MS Sans Serif"
BEGIN /////////////////////////////////////////////////////////////////////////////
CONTROL IDI_CONFIG, -1, "STATIC", SS_ICON | WS_CHILD | WS_VISIBLE, 11, 17, 18, 20 #undef APSTUDIO_READONLY_SYMBOLS
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 // English (U.S.) resources
END
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
STRINGTABLE #ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
BEGIN #pragma code_page(1252)
IDS_ABOUT, "&About Config..." #endif //_WIN32
END
/////////////////////////////////////////////////////////////////////////////
IDD_MAIN DIALOGEX 0, 0, 289, 247 //
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU // Icon
EXSTYLE WS_EX_APPWINDOW //
CAPTION "Configure LEGO Island"
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // Icon with lowest ID value placed first to ensure application icon
FONT 8, "MS Sans Serif" // remains consistent on all systems.
BEGIN IDI_CONFIG ICON DISCARDABLE "CONFIG.ico"
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 // Bitmap
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 IDB_SHARK BITMAP MOVEABLE PURE "shark.bmp"
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 // Dialog
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 IDD_ABOUT DIALOG DISCARDABLE 0, 0, 217, 55
CONTROL "3D Sound", IDC_CHK_3DSOUND, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, 220, 210, 45, 10 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CONTROL "Island Model Quality", -1, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 107, 7, 175, 26 CAPTION "About Configuration Application"
CONTROL "Island Texture Quality", -1, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 107, 38, 175, 26 FONT 8, "MS Sans Serif"
CONTROL "Advanced Settings", IDC_GRP_ADVANCED, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 107, 147, 175, 93 BEGIN
CONTROL "Advanced", IDC_BTN_ADVANCED, "BUTTON", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 109, 124, 44, 14 ICON IDI_CONFIG,-1,11,17,18,20
CONTROL IDB_SHARK, IDC_BMP_SHARK, "STATIC", SS_BITMAP | WS_CHILD | WS_VISIBLE, 7, 7, 83, 249 LTEXT "Configuration settings for ",-1,40,10,119,8,SS_NOPREFIX
CONTROL "Draw Cursor", IDC_CHK_DRAW_CURSOR, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, 219, 227, 54, 10 LTEXT "LEGO Island: The Modder's Arrival",-1,40,20,119,8,
CONTROL "Music", IDC_CHK_MUSIC, "BUTTON", BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 124, 104, 35, 10 SS_NOPREFIX
END LTEXT "Copyright <20> 2025 i guess",-1,40,30,119,8
DEFPUSHBUTTON "OK",IDOK,178,7,32,14,WS_GROUP
1 VERSIONINFO END
FILEVERSION 1,1,0,0
PRODUCTVERSION 1,1,0,0 IDD_MAIN DIALOGEX 0, 0, 289, 250
FILEOS 0x4 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION |
FILETYPE 0x1 WS_SYSMENU
BEGIN EXSTYLE WS_EX_APPWINDOW
BLOCK "StringFileInfo" CAPTION "Configuration for LEGO Island: The Modder's Arrival"
BEGIN FONT 8, "MS Sans Serif"
BLOCK "040904b0" BEGIN
BEGIN PUSHBUTTON "Save && Exit",IDABORT,194,123,44,14
VALUE "CompanyName", "Mindscape, Inc." PUSHBUTTON "Cancel",IDCANCEL,238,123,44,14
VALUE "FileDescription", "LEGOIsland & Configuration application" CONTROL "Fast",IDC_RAD_MODEL_QUALITY_LOW,"Button",
VALUE "FileVersion", "1, 1, 0, 0" BS_AUTORADIOBUTTON,118,18,42,10
VALUE "InternalName", "LEGOISLE.EXE" CONTROL "High",IDC_RAD_MODEL_QUALITY_HIGH,"Button",
VALUE "LegalCopyright", "Copyright \xA9 1997" BS_AUTORADIOBUTTON,192,18,69,10
VALUE "OriginalFilename", "CONFIG.EXE" CONTROL "Fast",IDC_RAD_TEXTURE_QUALITY_LOW,"Button",
VALUE "ProductName", "LEGO Island" BS_AUTORADIOBUTTON,118,49,43,10
VALUE "ProductVersion", "1, 1, 0, 0" CONTROL "High",IDC_RAD_TEXTURE_QUALITY_HIGH,"Button",
END BS_AUTORADIOBUTTON,192,49,69,10
END CONTROL "8-bit (256) Colour",IDC_RAD_PALETTE_256,"Button",
BS_AUTORADIOBUTTON,118,80,69,10
BLOCK "VarFileInfo" CONTROL "16-bit (65536) Colour",IDC_RAD_PALETTE_16BIT,"Button",
BEGIN BS_AUTORADIOBUTTON,192,80,80,10
VALUE "Translation", 0x0409, 0x04B0 // U.S. English, Unicode CONTROL "Use Joystick",IDC_CHK_JOYSTICK,"Button",BS_AUTOCHECKBOX |
END WS_TABSTOP,219,104,54,10
END LISTBOX IDC_LIST_3DDEVICES,115,168,161,36,LBS_NOINTEGRALHEIGHT |
WS_VSCROLL,WS_EX_TRANSPARENT
CONTROL "Flip Video Memory Pages",IDC_CHK_FLIP_VIDEO_MEM_PAGES,
"Button",BS_AUTOCHECKBOX,116,224,96,15
CONTROL "Draw 3D to Video Memory",IDC_CHK_3D_VIDEO_MEMORY,
"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
#ifndef _MAC
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
1 VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
FILEFLAGSMASK 0x0L
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "FUNNIMAN.EXE"
VALUE "FileDescription", "b_TMAOmni Library Configurator"
VALUE "FileVersion", "1, 0, 0, 0"
VALUE "InternalName", "b_TMAOmni Library Configurator"
VALUE "LegalCopyright", "Copyright \xA9 2025"
VALUE "OriginalFilename", "CONFIGURE.EXE"
VALUE "ProductName", "LEGO Island: The Modder's Arrival"
VALUE "ProductVersion", "1, 0, 0, 0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
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

View File

@ -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
View 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
View 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
View 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)

View 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;
}
}
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
CONFIG_orig/res/config.aps Normal file

Binary file not shown.

82
CONFIG_orig/res/config.rc Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
ICON_RES_SOURCE/TMAraw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -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());
} //}
//if (!ReadReg("savepath", buffer, sizeof(buffer))) {
// if (!WriteReg("savepath", MxOmni::GetHD() + "\\SAV"))
// {
// 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]; //m_cdPath = new char[strlen(buffer) + 1];
strcpy(m_cdPath, buffer); //strcpy(m_cdPath, buffer);
MxOmni::SetCD(m_cdPath); //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;

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
ISLE/res/busy.ani Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 326 B

BIN
ISLE/res/exclaim.cur Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

View File

@ -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"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 766 B

View 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

View 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

View 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

View File

@ -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

View File

@ -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'}

View 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

View File

@ -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'

View 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;
}

View 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;
}

View 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
}

View File

@ -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()
{ {

View File

@ -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()

View File

@ -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();

View File

@ -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);
} }

View 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;
}

View File

@ -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);
@ -1041,4 +1041,4 @@ MxLong LegoNavController::Notify(MxParam& p_param)
} }
return 0; return 0;
} }

View File

@ -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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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

View 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

View 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;
}

View 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;
}

View 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)
{
}

View 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, &currentPalette[paletteIndex * 3], length * 3);
intoPalette += length * 3;
paletteIndex += length;
intoChunk++;
}
else {
if (*intoChunk & 0x40) {
MxU8 length = (*intoChunk & 0x3f) + 1;
memcpy(intoPalette, &currentPalette[*(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;
}

View File

@ -0,0 +1,5 @@
#include "matrix.h"
#include "decomp.h"
DECOMP_SIZE_ASSERT(Matrix4, 0x08);

View 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);

View File

@ -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
View 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
}
}

View File

@ -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

Binary file not shown.

BIN
build/D3DImm.dll Normal file

Binary file not shown.

BIN
build/DDraw.dll Normal file

Binary file not shown.

38
build/README.txt Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

BIN
build/d3drm.dll Normal file

Binary file not shown.

BIN
build/dgVoodoo.conf Normal file

Binary file not shown.

View File

@ -1,58 +1,58 @@
function(reccmp_find_project RESULT) function(reccmp_find_project RESULT)
set(curdir "${CMAKE_CURRENT_SOURCE_DIR}") set(curdir "${CMAKE_CURRENT_SOURCE_DIR}")
while(1) while(1)
if(EXISTS "${curdir}/reccmp-project.yml") if(EXISTS "${curdir}/reccmp-project.yml")
break() break()
endif() endif()
get_filename_component(nextdir "${curdir}" DIRECTORY) get_filename_component(nextdir "${curdir}" DIRECTORY)
if(nextdir STREQUAL curdir) if(nextdir STREQUAL curdir)
set(curdir "${RESULT}-NOTFOUND") set(curdir "${RESULT}-NOTFOUND")
break() break()
endif() endif()
set(curdir "${nextdir}") set(curdir "${nextdir}")
endwhile() endwhile()
set("${RESULT}" "${curdir}" PARENT_SCOPE) set("${RESULT}" "${curdir}" PARENT_SCOPE)
endfunction() endfunction()
function(reccmp_add_target TARGET) function(reccmp_add_target TARGET)
cmake_parse_arguments(ARGS "" "ID" "" ${ARGN}) cmake_parse_arguments(ARGS "" "ID" "" ${ARGN})
if(NOT ARGS_ID) if(NOT ARGS_ID)
message(FATAL_ERROR "Missing ID argument") message(FATAL_ERROR "Missing ID argument")
endif() endif()
set_property(TARGET ${TARGET} PROPERTY INTERFACE_RECCMP_ID "${ARGS_ID}") set_property(TARGET ${TARGET} PROPERTY INTERFACE_RECCMP_ID "${ARGS_ID}")
set_property(GLOBAL APPEND PROPERTY RECCMP_TARGETS ${TARGET}) set_property(GLOBAL APPEND PROPERTY RECCMP_TARGETS ${TARGET})
endfunction() endfunction()
function(reccmp_configure) function(reccmp_configure)
cmake_parse_arguments(ARGS "COPY_TO_SOURCE_FOLDER" "DIR" "" ${ARGN}) cmake_parse_arguments(ARGS "COPY_TO_SOURCE_FOLDER" "DIR" "" ${ARGN})
set(binary_dir "${CMAKE_BINARY_DIR}") set(binary_dir "${CMAKE_BINARY_DIR}")
if(ARGS_DIR) if(ARGS_DIR)
set(binary_dir "${ARGS_DIR}") set(binary_dir "${ARGS_DIR}")
endif() endif()
reccmp_find_project(reccmp_project_dir) reccmp_find_project(reccmp_project_dir)
if(NOT reccmp_project_dir) if(NOT reccmp_project_dir)
message(FATAL_ERROR "Cannot find reccmp-project.yml") message(FATAL_ERROR "Cannot find reccmp-project.yml")
endif() endif()
if(CMAKE_CONFIGURATION_TYPES) if(CMAKE_CONFIGURATION_TYPES)
set(outputdir "${binary_dir}/$<CONFIG>") set(outputdir "${binary_dir}/$<CONFIG>")
else() else()
set(outputdir "${binary_dir}") set(outputdir "${binary_dir}")
endif() endif()
set(build_yml_txt "project: '${reccmp_project_dir}'\ntargets:\n") set(build_yml_txt "project: '${reccmp_project_dir}'\ntargets:\n")
get_property(RECCMP_TARGETS GLOBAL PROPERTY RECCMP_TARGETS) get_property(RECCMP_TARGETS GLOBAL PROPERTY RECCMP_TARGETS)
foreach(target ${RECCMP_TARGETS}) foreach(target ${RECCMP_TARGETS})
get_property(id TARGET "${target}" PROPERTY INTERFACE_RECCMP_ID) get_property(id TARGET "${target}" PROPERTY INTERFACE_RECCMP_ID)
string(APPEND build_yml_txt " ${id}:\n") string(APPEND build_yml_txt " ${id}:\n")
string(APPEND build_yml_txt " path: '$<TARGET_FILE:${target}>'\n") string(APPEND build_yml_txt " path: '$<TARGET_FILE:${target}>'\n")
if(WIN32 AND MSVC) if(WIN32 AND MSVC)
string(APPEND build_yml_txt " pdb: '$<TARGET_PDB_FILE:${target}>'\n") string(APPEND build_yml_txt " pdb: '$<TARGET_PDB_FILE:${target}>'\n")
endif() endif()
endforeach() endforeach()
file(GENERATE OUTPUT "${outputdir}/reccmp-build.yml" CONTENT "${build_yml_txt}") file(GENERATE OUTPUT "${outputdir}/reccmp-build.yml" CONTENT "${build_yml_txt}")
if(ARGS_COPY_TO_SOURCE_FOLDER) if(ARGS_COPY_TO_SOURCE_FOLDER)
file(GENERATE OUTPUT "${CMAKE_SOURCE_DIR}/reccmp-build.yml" CONTENT "${build_yml_txt}" CONDITION $<CONFIG:Release>) file(GENERATE OUTPUT "${CMAKE_SOURCE_DIR}/reccmp-build.yml" CONTENT "${build_yml_txt}" CONDITION $<CONFIG:Release>)
endif() endif()
endfunction() endfunction()

9
compile.bat Normal file
View 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
View File

@ -0,0 +1,3 @@
cd build
cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo
cd..

371
tools/datacmp.py Normal file
View 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())

View 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())

View 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.

View 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

View 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__}"

View 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)

View 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

View 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

View 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)

Some files were not shown because too many files have changed in this diff Show More