2019-04-27 09:23:41 -04:00
using System ;
using System.Collections.Generic ;
using System.Text ;
using System.Windows.Forms ;
using System.IO ;
using System.Drawing ;
using System.Diagnostics ;
using Microsoft.Win32 ;
2019-05-11 22:14:58 -04:00
using System.Xml ;
2019-04-27 09:23:41 -04:00
namespace Rebuilder
{
class Rebuilder : Form
{
2019-04-29 00:56:06 -04:00
TableLayoutPanel advanced_grid ;
2019-04-27 09:23:41 -04:00
NumericUpDown turn_speed_control ;
2019-04-28 00:58:17 -04:00
NumericUpDown movement_speed_control ;
2019-04-27 09:23:41 -04:00
CheckBox redirect_saves ;
CheckBox run_fullscreen ;
2019-04-27 11:22:28 -04:00
CheckBox stay_active_when_window_is_defocused ;
2019-04-29 00:56:06 -04:00
CheckBox override_resolution ;
NumericUpDown res_width ;
NumericUpDown res_height ;
2019-05-19 22:12:05 -04:00
CheckBox upscale_bitmaps ;
2019-04-29 00:56:06 -04:00
2019-04-28 01:12:43 -04:00
Button run_button ;
2019-04-29 10:26:03 -04:00
CheckBox advanced_button ;
2019-04-28 01:12:43 -04:00
2019-05-19 22:12:05 -04:00
CheckBox multiple_instances ;
2019-05-11 05:34:13 -04:00
MusicInjector music_injector = new MusicInjector ( ) ;
Button music_replacement_btn = new Button ( ) ;
2019-05-14 03:41:31 -04:00
string jukebox_output ;
2019-04-28 01:12:43 -04:00
Process p = null ;
string run_button_run = "Run" ;
string run_button_kill = "Kill" ;
2019-04-27 09:23:41 -04:00
2019-05-12 00:30:10 -04:00
bool aug_build = false ;
2019-05-11 05:34:13 -04:00
public static string [ ] standard_hdd_dirs = {
"C:/Program Files (x86)/LEGO Island" ,
"C:/Program Files/LEGO Island" ,
"/Program Files (x86)/LEGO Island" ,
"/Program Files/LEGO Island"
} ;
2019-04-27 09:23:41 -04:00
Rebuilder ( ) {
2019-05-26 23:29:52 -04:00
2019-05-11 05:34:13 -04:00
Text = "LEGO Island Rebuilder" ;
2019-04-27 09:23:41 -04:00
MaximizeBox = false ;
2019-04-27 11:32:30 -04:00
Icon = Icon . ExtractAssociatedIcon ( System . Reflection . Assembly . GetExecutingAssembly ( ) . Location ) ;
2019-04-27 09:23:41 -04:00
TableLayoutPanel grid = new TableLayoutPanel ( ) ;
grid . Dock = DockStyle . Fill ;
grid . ColumnCount = 2 ;
grid . AutoSize = true ;
2019-04-29 00:56:06 -04:00
2019-04-27 09:23:41 -04:00
// Create automatic evenly spaced layout
2019-04-29 00:56:06 -04:00
float f = 100f / grid . ColumnCount ;
2019-04-27 09:23:41 -04:00
for ( int i = 0 ; i < grid . ColumnCount ; i + + )
{
grid . ColumnStyles . Add ( new ColumnStyle ( SizeType . Percent , f ) ) ;
}
2019-04-29 00:56:06 -04:00
// Build standard layout
2019-04-27 09:23:41 -04:00
grid . SuspendLayout ( ) ;
int row = 0 ;
2019-04-29 00:56:06 -04:00
2019-04-27 09:23:41 -04:00
Label title = new Label ( ) ;
title . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
2019-05-11 05:34:13 -04:00
title . Text = "LEGO Island Rebuilder" ;
2019-04-27 09:23:41 -04:00
title . Font = new Font ( title . Font , FontStyle . Bold ) ;
title . TextAlign = ContentAlignment . MiddleCenter ;
grid . Controls . Add ( title , 0 , row ) ;
grid . SetColumnSpan ( title , 2 ) ;
row + + ;
LinkLabel subtitle = new LinkLabel ( ) ;
subtitle . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
subtitle . Text = "by MattKC (itsmattkc.com)" ;
subtitle . TextAlign = ContentAlignment . MiddleCenter ;
2019-05-11 05:34:13 -04:00
subtitle . LinkClicked + = new LinkLabelLinkClickedEventHandler ( AuthorLinkClick ) ;
2019-04-27 09:23:41 -04:00
grid . Controls . Add ( subtitle , 0 , row ) ;
grid . SetColumnSpan ( subtitle , 2 ) ;
row + + ;
Label turn_speed_lbl = new Label ( ) ;
turn_speed_lbl . Text = "Turn Speed:" ;
turn_speed_lbl . Anchor = AnchorStyles . Left | AnchorStyles . Bottom ;
grid . Controls . Add ( turn_speed_lbl , 0 , row ) ;
turn_speed_control = new NumericUpDown ( ) ;
turn_speed_control . Minimum = 0 ;
2019-04-29 21:02:24 -04:00
turn_speed_control . Value = 1 ;
turn_speed_control . DecimalPlaces = 2 ;
2019-04-27 09:23:41 -04:00
turn_speed_control . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
grid . Controls . Add ( turn_speed_control , 1 , row ) ;
row + + ;
2019-04-28 00:58:17 -04:00
Label movement_speed_lbl = new Label ( ) ;
movement_speed_lbl . Text = "Movement Speed:" ;
movement_speed_lbl . Anchor = AnchorStyles . Left | AnchorStyles . Bottom ;
grid . Controls . Add ( movement_speed_lbl , 0 , row ) ;
movement_speed_control = new NumericUpDown ( ) ;
movement_speed_control . Minimum = 0 ;
2019-04-29 21:02:24 -04:00
movement_speed_control . Value = 1 ;
movement_speed_control . DecimalPlaces = 2 ;
2019-04-28 00:58:17 -04:00
movement_speed_control . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
grid . Controls . Add ( movement_speed_control , 1 , row ) ;
row + + ;
2019-05-11 05:34:13 -04:00
Label music_replacement_lbl = new Label ( ) ;
music_replacement_lbl . Text = "Music Injection:" ;
music_replacement_lbl . Anchor = AnchorStyles . Left | AnchorStyles . Bottom ;
grid . Controls . Add ( music_replacement_lbl , 0 , row ) ;
music_replacement_btn . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
UpdateMusicInjectorBtnText ( ) ;
music_replacement_btn . Click + = new EventHandler ( this . ShowMusicInjectorForm ) ;
grid . Controls . Add ( music_replacement_btn , 1 , row ) ;
row + + ;
2019-04-27 09:23:41 -04:00
run_fullscreen = new CheckBox ( ) ;
run_fullscreen . Checked = true ;
run_fullscreen . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
run_fullscreen . Text = "Run in full screen" ;
grid . Controls . Add ( run_fullscreen , 0 , row ) ;
grid . SetColumnSpan ( run_fullscreen , 2 ) ;
row + + ;
2019-05-19 22:12:05 -04:00
multiple_instances = new CheckBox ( ) ;
multiple_instances . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
multiple_instances . Text = "Allow multiple instances" ;
grid . Controls . Add ( multiple_instances , 0 , row ) ;
grid . SetColumnSpan ( multiple_instances , 2 ) ;
row + + ;
2019-04-27 11:22:28 -04:00
stay_active_when_window_is_defocused = new CheckBox ( ) ;
stay_active_when_window_is_defocused . Checked = false ;
stay_active_when_window_is_defocused . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
stay_active_when_window_is_defocused . Text = "Stay active when window is defocused" ;
grid . Controls . Add ( stay_active_when_window_is_defocused , 0 , row ) ;
grid . SetColumnSpan ( stay_active_when_window_is_defocused , 2 ) ;
row + + ;
2019-04-27 09:23:41 -04:00
redirect_saves = new CheckBox ( ) ;
redirect_saves . Checked = true ;
redirect_saves . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
redirect_saves . Text = "Redirect save files to %APPDATA%" ;
grid . Controls . Add ( redirect_saves , 0 , row ) ;
grid . SetColumnSpan ( redirect_saves , 2 ) ;
row + + ;
2019-04-28 01:12:43 -04:00
run_button = new Button ( ) ;
run_button . Text = run_button_run ;
2019-04-27 09:23:41 -04:00
run_button . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
run_button . Click + = new System . EventHandler ( this . Run ) ;
2019-04-29 10:26:03 -04:00
run_button . Font = new Font ( run_button . Font , FontStyle . Bold ) ;
2019-04-27 09:23:41 -04:00
grid . Controls . Add ( run_button , 0 , row ) ;
2019-04-29 10:26:03 -04:00
advanced_button = new CheckBox ( ) ;
2019-04-29 00:56:06 -04:00
advanced_button . Text = "Advanced" ;
2019-04-29 10:26:03 -04:00
advanced_button . TextAlign = ContentAlignment . MiddleCenter ;
advanced_button . Appearance = System . Windows . Forms . Appearance . Button ;
2019-04-29 00:56:06 -04:00
advanced_button . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
2019-04-29 10:26:03 -04:00
advanced_button . CheckedChanged + = new EventHandler ( ToggleAdvanced ) ;
2019-04-29 00:56:06 -04:00
grid . Controls . Add ( advanced_button , 1 , row ) ;
grid . ResumeLayout ( true ) ;
// Build advanced layout
// Create automatic evenly spaced layout
advanced_grid = new TableLayoutPanel ( ) ;
advanced_grid . Visible = false ;
advanced_grid . Dock = DockStyle . Right ;
advanced_grid . ColumnCount = 2 ;
advanced_grid . AutoSize = true ;
f = 100f / advanced_grid . ColumnCount ;
for ( int i = 0 ; i < advanced_grid . ColumnCount ; i + + )
{
advanced_grid . ColumnStyles . Add ( new ColumnStyle ( SizeType . Percent , f ) ) ;
}
advanced_grid . SuspendLayout ( ) ;
row = 0 ;
override_resolution = new CheckBox ( ) ;
override_resolution . Text = "Override Resolution" ;
override_resolution . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
override_resolution . CheckedChanged + = new EventHandler ( ToggleOverrideResolution ) ;
advanced_grid . Controls . Add ( override_resolution , 0 , row ) ;
row + + ;
Label res_width_lbl = new Label ( ) ;
res_width_lbl . Text = "Width:" ;
res_width_lbl . Anchor = AnchorStyles . Left | AnchorStyles . Bottom ;
advanced_grid . Controls . Add ( res_width_lbl , 0 , row ) ;
res_width = new NumericUpDown ( ) ;
res_width . Enabled = false ;
res_width . Minimum = 0 ;
res_width . Maximum = int . MaxValue ;
res_width . Value = 640 ;
res_width . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
advanced_grid . Controls . Add ( res_width , 1 , row ) ;
row + + ;
Label res_height_lbl = new Label ( ) ;
res_height_lbl . Text = "Height:" ;
res_height_lbl . Anchor = AnchorStyles . Left | AnchorStyles . Bottom ;
advanced_grid . Controls . Add ( res_height_lbl , 0 , row ) ;
res_height = new NumericUpDown ( ) ;
res_height . Enabled = false ;
res_height . Minimum = 0 ;
res_height . Maximum = int . MaxValue ;
res_height . Value = 480 ;
res_height . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
advanced_grid . Controls . Add ( res_height , 1 , row ) ;
row + + ;
2019-05-19 22:12:05 -04:00
upscale_bitmaps = new CheckBox ( ) ;
upscale_bitmaps . Enabled = false ;
upscale_bitmaps . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
upscale_bitmaps . Text = "Upscale Bitmaps" ;
advanced_grid . Controls . Add ( upscale_bitmaps , 1 , row ) ;
row + + ;
2019-04-29 00:56:06 -04:00
Label advanced_warning_lbl = new Label ( ) ;
advanced_warning_lbl . Text = "WARNING: These features are experimental and often incomplete. Use at your own risk." ;
advanced_warning_lbl . Anchor = AnchorStyles . Top | AnchorStyles . Bottom | AnchorStyles . Left | AnchorStyles . Right ;
advanced_warning_lbl . Font = new Font ( advanced_warning_lbl . Font , FontStyle . Bold ) ;
advanced_grid . Controls . Add ( advanced_warning_lbl , 0 , row ) ;
advanced_grid . SetColumnSpan ( advanced_warning_lbl , 2 ) ;
advanced_grid . ResumeLayout ( true ) ;
// Set up tooltips
2019-04-27 09:23:41 -04:00
ToolTip tip = new ToolTip ( ) ;
2019-05-11 05:34:13 -04:00
tip . SetToolTip ( turn_speed_control , "Set the turn speed multiplier. LEGO Island ties its turn speed to the frame rate which is too fast on modern PCs. Use this value to correct it.\n\n" +
2019-04-29 21:02:24 -04:00
"0.00 = No turning at all\n" +
"0.35 = Recommended for modern PCs\n" +
2019-05-11 05:34:13 -04:00
"1.00 = LEGO Island's default" ) ;
2019-04-28 00:58:17 -04:00
tip . SetToolTip ( movement_speed_control , "Set the movement speed multiplier. This value does not affect other racers so it can be used to cheat (or cripple) your chances in races.\n\n" +
2019-04-29 21:02:24 -04:00
"0.00 = No movement at all\n" +
2019-05-11 05:34:13 -04:00
"1.00 = LEGO Island's default" ) ;
tip . SetToolTip ( run_fullscreen , "Override the registry check and run LEGO Island either full screen or windowed. " +
2019-04-27 11:32:30 -04:00
"Allows you to change modes without administrator privileges and registry editing." ) ;
2019-04-27 12:03:27 -04:00
tip . SetToolTip ( redirect_saves , "Redirect save data to a folder that's writable so games can be saved without administrator privileges.\n\n" +
"Saves will be stored in: " + Environment . GetFolderPath ( Environment . SpecialFolder . ApplicationData ) + "\\LEGO Island" ) ;
2019-05-11 05:34:13 -04:00
tip . SetToolTip ( stay_active_when_window_is_defocused , "LEGO Island's default behavior is to pause all operations when defocused. " +
"This setting overrides that behavior and keeps LEGO Island active even when unfocused.\n\n" +
2019-04-27 12:14:33 -04:00
"NOTE: This currently only works in windowed mode." ) ;
2019-05-19 22:12:05 -04:00
tip . SetToolTip ( upscale_bitmaps , "WARNING: This doesn't upscale the bitmaps' hitboxes yet and can make 2D areas like the Information Center difficult to navigate" ) ;
2019-04-27 09:23:41 -04:00
Controls . Add ( grid ) ;
2019-04-29 00:56:06 -04:00
Controls . Add ( advanced_grid ) ;
2019-04-27 09:23:41 -04:00
AutoSize = true ;
AutoSizeMode = AutoSizeMode . GrowAndShrink ;
ResumeLayout ( true ) ;
CenterToScreen ( ) ;
2019-05-11 22:14:58 -04:00
Shown + = new EventHandler ( OnStartup ) ;
FormClosing + = new FormClosingEventHandler ( OnClosing ) ;
2019-04-27 09:23:41 -04:00
}
private void Write ( FileStream fs , byte [ ] bytes , long pos = - 1 )
{
if ( pos > - 1 )
{
fs . Position = pos ;
}
fs . Write ( bytes , 0 , bytes . Length ) ;
}
2019-04-27 11:22:28 -04:00
private void WriteByte ( FileStream fs , byte b , long pos = - 1 )
{
if ( pos > - 1 )
{
fs . Position = pos ;
}
fs . WriteByte ( b ) ;
}
2019-05-19 22:12:05 -04:00
private void WriteManyBytes ( FileStream fs , byte b , int count , long pos = - 1 )
{
if ( pos > - 1 )
{
fs . Position = pos ;
}
for ( int i = 0 ; i < count ; i + + )
fs . WriteByte ( b ) ;
}
2019-04-27 09:23:41 -04:00
private void WriteInt32 ( FileStream fs , Int32 integer , long pos = - 1 )
{
byte [ ] int_bytes = BitConverter . GetBytes ( integer ) ;
Write ( fs , int_bytes , pos ) ;
}
2019-04-29 21:02:24 -04:00
private void WriteFloat ( FileStream fs , float f , long pos = - 1 )
{
byte [ ] f_bytes = BitConverter . GetBytes ( f ) ;
Write ( fs , f_bytes , pos ) ;
}
2019-04-27 09:23:41 -04:00
private void WriteString ( FileStream fs , string s , long pos = - 1 )
{
byte [ ] str_bytes = System . Text . Encoding . ASCII . GetBytes ( s ) ;
Write ( fs , str_bytes , pos ) ;
}
2019-05-03 10:52:16 -04:00
private bool ApproxEqual ( float a , float b )
2019-04-27 09:23:41 -04:00
{
2019-05-03 10:52:16 -04:00
return Math . Abs ( a - b ) < 0.0001 ;
}
private bool IncompatibleBuildMessage ( string incompatibilities )
{
2019-05-03 11:43:59 -04:00
return ( MessageBox . Show ( "The following patches you've chosen are not compatible with this version of LEGO Island:\n\n" + incompatibilities + "\nContinue without them?" , "Compatibility" , MessageBoxButtons . YesNo , MessageBoxIcon . Warning ) = = DialogResult . Yes ) ;
2019-05-03 10:52:16 -04:00
}
2019-05-11 05:34:13 -04:00
private bool Patch ( string source_dir , string dir )
2019-05-03 10:52:16 -04:00
{
string incompatibilities = "" ;
using ( FileStream lego1dll = File . Open ( dir + "/LEGO1.DLL" , FileMode . Open , FileAccess . ReadWrite ) )
using ( FileStream isleexe = File . Open ( dir + "/ISLE.EXE" , FileMode . Open , FileAccess . ReadWrite ) )
2019-04-27 09:23:41 -04:00
{
2019-05-03 10:52:16 -04:00
// Crude check if the build is September or August
lego1dll . Position = 0x54083 ;
2019-05-12 00:30:10 -04:00
aug_build = ( lego1dll . ReadByte ( ) = = 0x7E ) ;
2019-05-03 11:43:59 -04:00
2019-04-28 00:58:17 -04:00
// Write turn/movement speed hack (this frees up 12 bytes of code)
2019-05-11 22:45:21 -04:00
long turn_speed_offset = aug_build ? 0x54083 : 0x54323 ;
2019-05-11 22:58:37 -04:00
Write ( lego1dll , new byte [ ] { 0x7E , 0x04 , 0x2B , 0xD1 , 0xEB , 0x0A , 0x89 , 0xC8 , 0xF7 , 0xD8 , 0x39 , 0xD0 , 0x7E , 0x44 , 0x01 , 0xCA , 0x29 , 0xCE , 0x89 , 0x54 , 0x24 , 0x04 , 0xDB , 0x44 , 0x24 , 0x04 , 0x89 , 0x74 , 0x24 , 0x04 , 0xDA , 0x74 , 0x24 , 0x04 , 0x3D , 0xF0 , 0x00 , 0x00 , 0x00 , 0x74 , 0x0A , 0xC7 , 0x44 , 0x24 , 0x04 } , turn_speed_offset ) ;
2019-04-29 21:02:24 -04:00
WriteFloat ( lego1dll , ( float ) turn_speed_control . Value ) ;
Write ( lego1dll , new byte [ ] { 0xEB , 0x08 , 0xC7 , 0x44 , 0x24 , 0x04 } ) ;
WriteFloat ( lego1dll , ( float ) movement_speed_control . Value ) ;
Write ( lego1dll , new byte [ ] { 0xD8 , 0x4C , 0x24 , 0x04 , 0xD8 , 0x4C , 0x24 , 0x14 , 0xD9 , 0x5C , 0x24 , 0x04 , 0xD9 , 0x44 , 0x24 , 0x04 , 0x5E , 0x83 , 0xC4 , 0x04 , 0xC2 , 0x0C , 0x00 , 0xC7 , 0x44 , 0x24 , 0x04 , 0x00 , 0x00 , 0x00 , 0x00 , 0xD9 , 0x44 , 0x24 , 0x04 , 0x5E , 0x83 , 0xC4 , 0x04 , 0xC2 , 0x0C , 0x00 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 } ) ;
2019-05-11 22:45:21 -04:00
2019-05-03 11:43:59 -04:00
// Patch EXE to read from HKCU instead of HKLM
WriteByte ( isleexe , 0x01 , 0x1B5F ) ;
2019-04-27 11:22:28 -04:00
if ( stay_active_when_window_is_defocused . Checked )
{
// Remove code that writes focus value to memory, effectively keeping it always true - frees up 3 bytes
Write ( isleexe , new byte [ ] { 0x90 , 0x90 , 0x90 } , 0x1363 ) ;
// Write DirectSound flags to allow audio to play while the window is defocused
2019-05-03 10:52:16 -04:00
WriteByte ( lego1dll , 0x80 , aug_build ? 0xB48FB : 0xB120B ) ;
2019-04-27 11:22:28 -04:00
WriteByte ( lego1dll , 0x80 , 0x5B96 ) ;
2019-05-03 10:52:16 -04:00
WriteByte ( lego1dll , 0x80 , aug_build ? 0xB48F1 : 0xB1201 ) ;
WriteByte ( lego1dll , 0x80 , aug_build ? 0xAD7D3 : 0xADD43 ) ;
2019-04-27 11:22:28 -04:00
}
2019-05-19 22:12:05 -04:00
if ( multiple_instances . Checked )
{
// LEGO Island uses FindWindowA in user32.dll to determine if it's already running, here we replace the call with moving 0x0 into EAX, simulating a NULL response from FindWindowA
WriteByte ( isleexe , 0xEB , 0x10B5 ) ;
}
2019-05-11 05:34:13 -04:00
// Redirect JUKEBOX.SI if we're inserting music
if ( music_injector . ReplaceCount ( ) > 0 )
{
2019-05-14 03:41:31 -04:00
Uri uri1 = new Uri ( jukebox_output . Substring ( 0 , jukebox_output . LastIndexOf ( "." ) ) ) ;
2019-05-11 05:34:13 -04:00
Uri uri2 = new Uri ( source_dir + "/ISLE.EXE" ) ;
Uri relative = uri2 . MakeRelativeUri ( uri1 ) ;
2019-05-26 23:29:52 -04:00
string jukebox_path = "\\" + Uri . UnescapeDataString ( relative . ToString ( ) ) . Replace ( "/" , "\\" ) ;
2019-05-11 05:34:13 -04:00
if ( aug_build )
{
WriteByte ( lego1dll , 0xF6 , 0x51EF5 ) ;
WriteByte ( lego1dll , 0x34 ) ;
WriteByte ( lego1dll , 0x0D ) ;
WriteByte ( lego1dll , 0x10 ) ;
}
else
{
WriteByte ( lego1dll , 0x66 , 0x52195 ) ;
WriteByte ( lego1dll , 0x3A ) ;
WriteByte ( lego1dll , 0x0D ) ;
WriteByte ( lego1dll , 0x10 ) ;
}
WriteString ( lego1dll , jukebox_path , aug_build ? 0xD28F6 : 0xD2E66 ) ;
}
2019-04-29 10:26:03 -04:00
// INCOMPLETE: Resolution hack:
if ( override_resolution . Checked )
{
// Changes window size
WriteInt32 ( isleexe , ( Int32 ) res_width . Value , 0xE848 ) ;
WriteInt32 ( isleexe , ( Int32 ) res_height . Value , 0xE84C ) ;
// Changes D3D render size
WriteInt32 ( isleexe , ( Int32 ) res_width . Value - 1 , 0x4D0 ) ;
WriteInt32 ( isleexe , ( Int32 ) res_height . Value - 1 , 0x4D7 ) ;
2019-05-19 22:12:05 -04:00
// Write code to upscale the bitmaps
if ( upscale_bitmaps . Checked )
{
Write ( lego1dll , new byte [ ] { 0xE9 , 0x2D , 0x01 , 0x00 , 0x00 , 0x8B , 0x56 , 0x1C , 0x6A , 0x00 , 0x8D , 0x45 , 0xE4 , 0xF6 , 0x42 , 0x30 , 0x08 , 0x74 , 0x07 , 0x68 , 0x00 , 0x80 , 0x00 , 0x00 , 0xEB , 0x02 , 0x6A , 0x00 , 0x8B , 0x3B , 0x50 , 0x51 , 0x8D , 0x4D , 0xD4 , 0x51 , 0x53 , 0x53 , 0x50 , 0x68 } , 0xB20E9 ) ;
WriteFloat ( lego1dll , ( float ) res_height . Value / 480.0f ) ;
Int32 x_offset = ( Int32 ) Math . Round ( ( res_width . Value - ( res_height . Value / 3 * 4 ) ) / 2 ) ;
Write ( lego1dll , new byte [ ] { 0xDB , 0x45 , 0xD4 , 0xD8 , 0x0C , 0x24 , 0xDB , 0x5D , 0xD4 , 0xDB , 0x45 , 0xD8 , 0xD8 , 0x0C , 0x24 , 0xDB , 0x5D , 0xD8 , 0xDB , 0x45 , 0xDC , 0xD8 , 0x0C , 0x24 , 0xDB , 0x5D , 0xDC , 0xDB , 0x45 , 0xE0 , 0xD8 , 0x0C , 0x24 , 0xDB , 0x5D , 0xE0 , 0x58 , 0x8B , 0x45 , 0xD4 , 0x05 } ) ;
WriteInt32 ( lego1dll , x_offset ) ;
Write ( lego1dll , new byte [ ] { 0x89 , 0x45 , 0xD4 , 0x8B , 0x45 , 0xDC , 0x05 } ) ;
WriteInt32 ( lego1dll , x_offset ) ;
Write ( lego1dll , new byte [ ] { 0x89 , 0x45 , 0xDC } ) ;
// Frees up 143 bytes of NOPs
WriteManyBytes ( lego1dll , 0x90 , 143 ) ;
Write ( lego1dll , new byte [ ] { 0x58 , 0x5B } ) ;
Write ( lego1dll , new byte [ ] { 0xE9 , 0xF6 , 0xFD , 0xFF , 0xFF } , 0xB22F3 ) ;
// Frees up 19 bytes of NOPs
WriteManyBytes ( lego1dll , 0x90 , 19 ) ;
}
2019-04-29 10:26:03 -04:00
}
2019-05-03 11:43:59 -04:00
if ( aug_build & & redirect_saves . Checked )
{
incompatibilities + = redirect_saves . Text + "\n" ;
}
}
2019-05-11 22:14:58 -04:00
RegistryKey src = Registry . LocalMachine . OpenSubKey ( "SOFTWARE\\Mindscape\\LEGO Island" , false ) ;
if ( src = = null )
{
src = Registry . LocalMachine . OpenSubKey ( "SOFTWARE\\WOW6432Node\\Mindscape\\LEGO Island" , false ) ;
}
2019-05-03 11:43:59 -04:00
2019-05-11 22:14:58 -04:00
if ( src = = null )
{
if ( MessageBox . Show ( "Failed to find LEGO Island's registry entries. Some patches may fail. Do you wish to continue?" ,
"Failed to find registry keys" ,
MessageBoxButtons . YesNo ,
MessageBoxIcon . Warning ) = = DialogResult . No )
2019-05-03 11:43:59 -04:00
{
2019-05-11 22:14:58 -04:00
return false ;
2019-05-03 11:43:59 -04:00
}
2019-04-27 09:23:41 -04:00
}
2019-05-11 22:14:58 -04:00
else
{
using ( RegistryKey dst = Registry . CurrentUser . CreateSubKey ( "Software\\Mindscape\\LEGO Island" ) )
{
// Copy config data from HKLM to HKCU
CopyRegistryKey ( src , dst ) ;
// Set full screen value
dst . SetValue ( "Full Screen" , run_fullscreen . Checked ? "YES" : "NO" ) ;
// Redirect save path
if ( redirect_saves . Checked )
{
string new_save_dir = Environment . GetFolderPath ( Environment . SpecialFolder . ApplicationData ) + "\\LEGO Island\\" ;
Directory . CreateDirectory ( new_save_dir ) ;
dst . SetValue ( "savepath" , new_save_dir ) ;
}
}
}
2019-05-03 10:52:16 -04:00
return string . IsNullOrEmpty ( incompatibilities ) | | IncompatibleBuildMessage ( incompatibilities ) ;
2019-04-27 09:23:41 -04:00
}
2019-04-27 11:51:27 -04:00
private bool IsValidDir ( string dir )
{
return ( File . Exists ( dir + "/ISLE.EXE" ) & & File . Exists ( dir + "/LEGO1.DLL" ) ) ;
}
2019-05-11 05:34:13 -04:00
private void ShowMusicInjectorForm ( object sender , EventArgs e )
{
//music_changed = true;
music_injector . ShowDialog ( ) ;
UpdateMusicInjectorBtnText ( ) ;
}
private void UpdateMusicInjectorBtnText ( )
{
int count = music_injector . ReplaceCount ( ) ;
string btn_text = count + " song" ;
if ( count ! = 1 )
{
btn_text + = "s" ;
}
btn_text + = " replaced" ;
music_replacement_btn . Text = btn_text ;
}
2019-04-29 00:56:06 -04:00
private void ToggleAdvanced ( object sender , EventArgs e )
{
2019-04-29 10:26:03 -04:00
advanced_grid . Visible = advanced_button . Checked ;
2019-05-03 10:52:16 -04:00
//CenterToScreen();
2019-04-29 00:56:06 -04:00
}
private void ToggleOverrideResolution ( Object sender , EventArgs e )
{
res_width . Enabled = override_resolution . Checked ;
res_height . Enabled = override_resolution . Checked ;
2019-05-19 22:12:05 -04:00
upscale_bitmaps . Enabled = override_resolution . Checked ;
2019-04-29 00:56:06 -04:00
}
2019-05-03 11:43:59 -04:00
private void CopyRegistryKey ( RegistryKey src , RegistryKey dst )
{
// copy the values
foreach ( var name in src . GetValueNames ( ) )
{
dst . SetValue ( name , src . GetValue ( name ) , src . GetValueKind ( name ) ) ;
}
// copy the subkeys
foreach ( var name in src . GetSubKeyNames ( ) )
{
using ( var srcSubKey = src . OpenSubKey ( name , false ) )
{
var dstSubKey = dst . CreateSubKey ( name ) ;
CopyRegistryKey ( srcSubKey , dstSubKey ) ;
}
}
}
2019-04-27 09:23:41 -04:00
private void Run ( object sender , EventArgs e )
{
2019-04-28 01:12:43 -04:00
if ( p ! = null )
{
p . Kill ( ) ;
return ;
}
2019-05-11 22:14:58 -04:00
string temp_path = Path . GetTempPath ( ) + "LEGOIslandRebuilder" ;
2019-04-27 09:23:41 -04:00
Directory . CreateDirectory ( temp_path ) ;
2019-04-27 11:51:27 -04:00
string dir = "" ;
2019-05-11 05:34:13 -04:00
for ( int i = 0 ; i < standard_hdd_dirs . Length ; i + + )
2019-04-27 11:51:27 -04:00
{
2019-05-11 05:34:13 -04:00
if ( IsValidDir ( standard_hdd_dirs [ i ] ) )
2019-04-27 11:51:27 -04:00
{
2019-05-11 05:34:13 -04:00
dir = standard_hdd_dirs [ i ] ;
2019-04-27 11:51:27 -04:00
break ;
}
}
if ( string . IsNullOrEmpty ( dir ) )
{
using ( OpenFileDialog ofd = new OpenFileDialog ( ) )
{
ofd . Filter = "ISLE.EXE|ISLE.EXE" ;
2019-05-11 05:34:13 -04:00
ofd . Title = "Where is LEGO Island installed?" ;
2019-04-27 11:51:27 -04:00
while ( true )
{
if ( ofd . ShowDialog ( ) = = DialogResult . OK )
{
dir = Path . GetDirectoryName ( ofd . FileName ) ;
if ( IsValidDir ( dir ) )
{
break ;
}
else
{
MessageBox . Show (
"This directory does not contain ISLE.EXE and LEGO1.DLL." ,
"Failed to find critical files" ,
MessageBoxButtons . OK ,
MessageBoxIcon . Error
) ;
}
}
else
{
return ;
}
}
}
}
2019-04-27 09:23:41 -04:00
try
{
2019-04-28 00:58:17 -04:00
string [ ] dest_files = Directory . GetFiles ( temp_path ) ;
for ( int i = 0 ; i < dest_files . Length ; i + + )
{
File . Delete ( dest_files [ i ] ) ;
}
string [ ] src_files = Directory . GetFiles ( dir ) ;
for ( int i = 0 ; i < src_files . Length ; i + + )
2019-04-27 23:40:29 -04:00
{
2019-04-28 00:58:17 -04:00
File . Copy ( src_files [ i ] , temp_path + "/" + Path . GetFileName ( src_files [ i ] ) , true ) ;
2019-04-27 23:40:29 -04:00
}
2019-04-27 09:23:41 -04:00
}
catch
{
MessageBox . Show ( "Failed to patch files" , "Error" , MessageBoxButtons . OK , MessageBoxIcon . Error ) ;
return ;
}
2019-05-11 05:34:13 -04:00
// Perform music insertion if necessary
if ( music_injector . ReplaceCount ( ) > 0 )
{
2019-05-14 03:42:58 -04:00
jukebox_output = dir + "/LEGO/Scripts/REJUKEBOX.SI" ;
2019-05-14 03:41:31 -04:00
try
{
using ( FileStream test_fs = new FileStream ( jukebox_output , FileMode . Create , FileAccess . Write ) )
{
}
}
catch
{
2019-05-14 03:42:58 -04:00
jukebox_output = Path . GetTempPath ( ) + "REJUKEBOX.SI" ;
2019-05-14 03:41:31 -04:00
}
music_injector . Insert ( jukebox_output ) ;
2019-05-11 05:34:13 -04:00
}
2019-04-27 09:23:41 -04:00
2019-05-14 03:41:31 -04:00
if ( ! Patch ( dir , temp_path ) ) return ;
2019-04-27 09:23:41 -04:00
// Set new EXE's compatibility mode to 256-colors
2019-04-29 10:26:03 -04:00
using ( RegistryKey key = Registry . CurrentUser . OpenSubKey ( "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers" , true ) )
2019-04-27 09:23:41 -04:00
{
2019-05-11 22:14:58 -04:00
if ( key ! = null )
2019-04-29 10:26:03 -04:00
{
2019-05-11 22:14:58 -04:00
key . CreateSubKey ( temp_path + "\\ISLE.EXE" ) ;
2019-05-19 22:12:05 -04:00
//string compat_string = "HIGHDPIAWARE";
2019-05-12 00:30:10 -04:00
string compat_string = "HIGHDPIAWARE DWM8And16BitMitigation" ;
2019-05-19 22:12:05 -04:00
2019-05-12 00:30:10 -04:00
if ( ! run_fullscreen . Checked )
2019-05-11 22:14:58 -04:00
{
2019-05-12 00:30:10 -04:00
compat_string + = " 256COLOR" ;
2019-05-11 22:14:58 -04:00
}
2019-05-12 00:30:10 -04:00
if ( ! redirect_saves . Checked | | aug_build )
2019-05-11 22:14:58 -04:00
{
2019-05-12 00:30:10 -04:00
compat_string + = " RUNASADMIN" ;
2019-05-11 22:14:58 -04:00
}
2019-05-12 00:30:10 -04:00
key . SetValue ( temp_path + "\\ISLE.EXE" , compat_string ) ;
2019-04-29 10:26:03 -04:00
}
2019-04-27 09:23:41 -04:00
}
ProcessStartInfo start_info = new ProcessStartInfo ( temp_path + "/ISLE.EXE" ) ;
2019-04-27 11:51:27 -04:00
start_info . WorkingDirectory = dir ;
2019-05-01 21:42:25 -04:00
try
{
p = Process . Start ( start_info ) ;
}
catch
{
p = null ;
return ;
}
2019-04-28 01:12:43 -04:00
p . EnableRaisingEvents = true ;
p . Exited + = new EventHandler ( ProcessExit ) ;
run_button . Text = run_button_kill ;
}
2019-04-27 09:23:41 -04:00
2019-04-28 01:12:43 -04:00
private void ProcessExit ( object sender , EventArgs e )
{
run_button . BeginInvoke ( ( MethodInvoker ) delegate ( ) { run_button . Text = run_button_run ; } ) ;
p = null ;
2019-04-27 09:23:41 -04:00
}
2019-05-11 05:34:13 -04:00
private void AuthorLinkClick ( object sender , LinkLabelLinkClickedEventArgs e )
2019-04-27 09:23:41 -04:00
{
Process . Start ( "http://www.itsmattkc.com/" ) ;
}
2019-05-11 22:14:58 -04:00
private string GetSettingsPath ( )
{
string settings_path = Environment . GetFolderPath ( Environment . SpecialFolder . ApplicationData ) + "/LEGOIslandRebuilder/settings.xml" ;
Directory . CreateDirectory ( Path . GetDirectoryName ( settings_path ) ) ;
return settings_path ;
}
private void OnStartup ( object sender , EventArgs e )
{
// Load settings
string settings_path = GetSettingsPath ( ) ;
if ( File . Exists ( settings_path ) )
{
XmlReader stream = XmlReader . Create ( settings_path ) ;
while ( stream . Read ( ) )
{
if ( stream . NodeType = = XmlNodeType . Element & & stream . IsStartElement ( ) )
{
if ( stream . Name = = "turnspeed" )
{
stream . Read ( ) ;
turn_speed_control . Value = decimal . Parse ( stream . Value ) ;
}
else if ( stream . Name = = "movespeed" )
{
stream . Read ( ) ;
movement_speed_control . Value = decimal . Parse ( stream . Value ) ;
}
else if ( stream . Name = = "fullscreen" )
{
stream . Read ( ) ;
run_fullscreen . Checked = bool . Parse ( stream . Value ) ;
}
else if ( stream . Name = = "stayactive" )
{
stream . Read ( ) ;
stay_active_when_window_is_defocused . Checked = bool . Parse ( stream . Value ) ;
}
else if ( stream . Name = = "redirecttoappdata" )
{
stream . Read ( ) ;
redirect_saves . Checked = bool . Parse ( stream . Value ) ;
}
else if ( stream . Name = = "showadvanced" )
{
stream . Read ( ) ;
advanced_button . Checked = bool . Parse ( stream . Value ) ;
}
else if ( stream . Name = = "overrideres" )
{
stream . Read ( ) ;
override_resolution . Checked = bool . Parse ( stream . Value ) ;
}
else if ( stream . Name = = "overridereswidth" )
{
stream . Read ( ) ;
res_width . Value = decimal . Parse ( stream . Value ) ;
}
else if ( stream . Name = = "overrideresheight" )
{
stream . Read ( ) ;
res_height . Value = decimal . Parse ( stream . Value ) ;
}
else if ( stream . Name = = "musicinjection" )
{
music_injector . LoadData ( stream ) ;
UpdateMusicInjectorBtnText ( ) ;
}
2019-05-19 22:12:05 -04:00
else if ( stream . Name = = "upscalebitmaps" )
{
stream . Read ( ) ;
upscale_bitmaps . Checked = bool . Parse ( stream . Value ) ;
}
2019-05-11 22:14:58 -04:00
}
}
stream . Close ( ) ;
}
}
private void OnClosing ( object sender , FormClosingEventArgs e )
{
// Save settings
string settings_path = GetSettingsPath ( ) ;
XmlWriter stream = XmlWriter . Create ( settings_path ) ;
stream . WriteStartDocument ( ) ;
stream . WriteStartElement ( "settings" ) ;
stream . WriteStartElement ( "turnspeed" ) ;
stream . WriteString ( turn_speed_control . Value . ToString ( ) ) ;
stream . WriteEndElement ( ) ; // turnspeed
stream . WriteStartElement ( "movespeed" ) ;
stream . WriteString ( movement_speed_control . Value . ToString ( ) ) ;
stream . WriteEndElement ( ) ; // movespeed
stream . WriteStartElement ( "fullscreen" ) ;
stream . WriteString ( run_fullscreen . Checked . ToString ( ) ) ;
stream . WriteEndElement ( ) ; // fullscreen
stream . WriteStartElement ( "stayactive" ) ;
stream . WriteString ( stay_active_when_window_is_defocused . Checked . ToString ( ) ) ;
stream . WriteEndElement ( ) ; // stayactive
stream . WriteStartElement ( "redirecttoappdata" ) ;
stream . WriteString ( redirect_saves . Checked . ToString ( ) ) ;
stream . WriteEndElement ( ) ; // redirecttoappdata
stream . WriteStartElement ( "showadvanced" ) ;
stream . WriteString ( advanced_button . Checked . ToString ( ) ) ;
stream . WriteEndElement ( ) ; // showadvanced
stream . WriteStartElement ( "overrideres" ) ;
stream . WriteString ( override_resolution . Checked . ToString ( ) ) ;
stream . WriteEndElement ( ) ; // overrideres
stream . WriteStartElement ( "overridereswidth" ) ;
stream . WriteString ( res_width . Value . ToString ( ) ) ;
stream . WriteEndElement ( ) ; // overridereswidth
stream . WriteStartElement ( "overrideresheight" ) ;
stream . WriteString ( res_height . Value . ToString ( ) ) ;
stream . WriteEndElement ( ) ; // overrideresheight
2019-05-19 22:12:05 -04:00
stream . WriteStartElement ( "upscalebitmaps" ) ;
stream . WriteString ( upscale_bitmaps . Checked . ToString ( ) ) ;
stream . WriteEndElement ( ) ;
2019-05-11 22:14:58 -04:00
stream . WriteStartElement ( "musicinjection" ) ;
music_injector . SaveData ( stream ) ;
stream . WriteEndElement ( ) ; // musicinjection
stream . WriteEndElement ( ) ; // settings
stream . WriteEndDocument ( ) ;
stream . Close ( ) ;
}
2019-04-27 09:23:41 -04:00
[STAThread]
static void Main ( )
{
Application . EnableVisualStyles ( ) ;
Application . SetCompatibleTextRenderingDefault ( false ) ;
Application . Run ( new Rebuilder ( ) ) ;
}
}
}