2020-04-22 16:24:24 -04:00
using System ;
2019-04-27 09:23:41 -04:00
using System.Collections.Generic ;
using System.Text ;
using System.Windows.Forms ;
using System.IO ;
using System.Drawing ;
using System.Diagnostics ;
2019-08-06 06:44:26 -04:00
using System.ComponentModel ;
2019-04-27 09:23:41 -04:00
using Microsoft.Win32 ;
2019-05-11 22:14:58 -04:00
using System.Xml ;
2019-08-06 06:44:26 -04:00
using System.Xml.Serialization ;
2020-03-10 02:27:12 -04:00
using System.Security.Cryptography ;
2019-04-27 09:23:41 -04:00
namespace Rebuilder
{
2020-03-10 04:39:11 -04:00
public class Rebuilder
2019-04-27 09:23:41 -04:00
{
2019-05-11 05:34:13 -04:00
MusicInjector music_injector = new MusicInjector ( ) ;
2019-08-06 06:44:26 -04:00
2019-05-14 03:41:31 -04:00
string jukebox_output ;
2019-08-06 06:44:26 -04:00
List < Process > processes = new List < Process > ( ) ;
2019-04-28 01:12:43 -04:00
2020-03-10 02:27:12 -04:00
private enum Version
{
kUnknown = - 1 ,
2020-03-22 09:40:48 -04:00
kEnglish10 ,
kEnglish11 ,
kGerman11 ,
2020-03-23 03:13:20 -04:00
kDanish11 ,
kSpanish11
2020-03-10 02:27:12 -04:00
}
// These must correspond to the `Version` enum above
private static string [ ] VersionHashes = {
"58FCF0F6500614E9F743712D1DD4D340088123DE" ,
2020-03-22 09:26:51 -04:00
"BBE289E89E5A39949D272174162711EA5CFF522C" ,
2020-03-22 09:40:48 -04:00
"96A6BAE8345AA04C21F1B319A632CAECFEE22443" ,
2020-03-23 03:13:20 -04:00
"8DFD3E5FDDE8C95C61013069795171163C9A4821" ,
"47EE50FC1EC5F6C54F465EB296D2F1B7CA25D5D2"
2020-03-10 02:27:12 -04:00
} ;
2019-05-12 00:30:10 -04:00
2019-05-11 05:34:13 -04:00
public static string [ ] standard_hdd_dirs = {
2020-03-10 02:27:12 -04:00
"C:/Program Files (x86)/LEGO Island" ,
"C:/Program Files/LEGO Island" ,
"/Program Files (x86)/LEGO Island" ,
"/Program Files/LEGO Island"
} ;
2019-05-11 05:34:13 -04:00
2019-08-06 06:44:26 -04:00
public enum FPSLimitType
{
Default ,
Uncapped ,
Limited
} ;
2020-08-21 13:42:06 -04:00
public enum ModelQualityType
{
High ,
Medium ,
Low
}
2019-08-06 06:44:26 -04:00
public class PatchList {
2020-03-10 11:56:27 -04:00
float turn_max_speed = 20.0F ;
2020-03-10 02:27:12 -04:00
[Category("Controls")]
[DisplayName("Turning: Max Speed")]
[Description("Set the maximum turning speed. (Default = 20.0)")]
2020-03-10 11:56:27 -04:00
[DefaultValue(20.0F)]
public float TurnMaxSpeed
2020-03-10 02:27:12 -04:00
{
get { return turn_max_speed ; }
set { turn_max_speed = value ; }
}
2020-03-10 11:56:27 -04:00
float turn_max_acceleration = 30.0F ;
2020-03-10 02:27:12 -04:00
[Category("Controls")]
[DisplayName("Turning: Max Acceleration")]
[Description("Set the speed at which turning accelerates (requires 'Turning: Enable Velocity') (Default = 30.0)")]
2020-03-10 11:56:27 -04:00
[DefaultValue(30.0F)]
public float TurnMaxAcceleration
2020-03-10 02:27:12 -04:00
{
get { return turn_max_acceleration ; }
set { turn_max_acceleration = value ; }
}
2020-03-10 11:56:27 -04:00
float turn_min_acceleration = 15.0F ;
2020-03-10 02:27:12 -04:00
[Category("Controls")]
[DisplayName("Turning: Min Acceleration")]
2020-03-10 11:56:27 -04:00
[Description("Set the speed at which turning accelerates (requires 'Turning: Enable Velocity') (Default = 15.0)")]
[DefaultValue(15.0F)]
public float TurnMinAcceleration
2020-03-10 02:27:12 -04:00
{
get { return turn_min_acceleration ; }
set { turn_min_acceleration = value ; }
}
2020-03-10 11:56:27 -04:00
float turn_deceleration = 50.0F ;
2020-03-10 02:27:12 -04:00
[Category("Controls")]
[DisplayName("Turning: Deceleration")]
[Description("Set the speed at which turning decelerates (requires 'Turning: Enable Velocity') (Default = 50.0)")]
2020-03-10 11:56:27 -04:00
[DefaultValue(50.0F)]
public float TurnDeceleration
2020-03-10 02:27:12 -04:00
{
get { return turn_deceleration ; }
set { turn_deceleration = value ; }
}
bool turn_use_velocity = false ;
2019-08-06 06:44:26 -04:00
[Category("Controls")]
2020-03-10 02:27:12 -04:00
[DisplayName("Turning: Enable Velocity")]
[Description("By default, LEGO Island ignores the turning acceleration/deceleration values. Set this to TRUE to utilize them (Default = FALSE)")]
2020-03-10 11:56:27 -04:00
[DefaultValue(false)]
2020-03-10 02:27:12 -04:00
public bool TurnUseVelocity
2019-08-06 06:44:26 -04:00
{
2020-03-10 02:27:12 -04:00
get { return turn_use_velocity ; }
set { turn_use_velocity = value ; }
2019-08-06 06:44:26 -04:00
}
2020-03-10 11:56:27 -04:00
float movement_max_speed = 40.0F ;
2019-08-06 06:44:26 -04:00
[Category("Controls")]
2020-03-10 02:27:12 -04:00
[DisplayName("Movement: Max Speed")]
[Description("Set the movement maximum speed. (Default = 40.0)")]
2020-03-10 11:56:27 -04:00
[DefaultValue(40.0F)]
public float MovementMaxSpeed
2019-08-06 06:44:26 -04:00
{
2020-03-10 02:27:12 -04:00
get { return movement_max_speed ; }
set { movement_max_speed = value ; }
}
2020-03-10 11:56:27 -04:00
float movement_max_acceleration = 15.0F ;
2020-03-10 02:27:12 -04:00
[Category("Controls")]
[DisplayName("Movement: Max Acceleration")]
[Description("Set the movement acceleration speed (i.e. how long it takes to go from not moving to top speed) (Default = 15.0)")]
2020-03-10 11:56:27 -04:00
[DefaultValue(15.0F)]
public float MovementMaxAcceleration
2020-03-10 02:27:12 -04:00
{
get { return movement_max_acceleration ; }
set { movement_max_acceleration = value ; }
}
2020-03-10 11:56:27 -04:00
float movement_min_acceleration = 4.0F ;
2020-03-10 02:27:12 -04:00
[Category("Controls")]
[DisplayName("Movement: Min Acceleration")]
2020-03-10 11:56:27 -04:00
[Description("Set the movement acceleration speed (i.e. how long it takes to go from not moving to top speed) (Default = 4.0)")]
[DefaultValue(4.0F)]
public float MovementMinAcceleration
2020-03-10 02:27:12 -04:00
{
get { return movement_min_acceleration ; }
set { movement_min_acceleration = value ; }
}
2020-03-10 11:56:27 -04:00
float movement_deceleration = 50.0F ;
2020-03-10 02:27:12 -04:00
[Category("Controls")]
[DisplayName("Movement: Deceleration")]
[ Description ( "Set the movement deceleration speed (i.e. how long it takes to slow to a stop after releasing the controls). " +
"Increase this value to stop faster, decrease it to stop slower. " +
"Usually this is set to a very high value making deceleration almost instant. (Default = 50.0)" ) ]
2020-03-10 11:56:27 -04:00
[DefaultValue(50.0F)]
public float MovementDeceleration
2020-03-10 02:27:12 -04:00
{
get { return movement_deceleration ; }
set { movement_deceleration = value ; }
}
int mouse_deadzone = 40 ;
[Category("Controls")]
[DisplayName("Mouse Deadzone")]
[Description("Sets the radius from the center of the screen where the mouse will do nothing (40 = default).")]
2020-03-10 11:56:27 -04:00
[DefaultValue(40)]
2020-03-10 02:27:12 -04:00
public int MouseDeadzone
{
get { return mouse_deadzone ; }
set { mouse_deadzone = value ; }
}
bool unhook_turn_speed = false ;
[Category("Controls")]
[DisplayName("Turning: Unhook From Frame Rate")]
[Description("LEGO Island contains a bug where the turning speed is influenced by the frame rate. Enable this to make the turn speed independent of the frame rate.")]
2020-03-10 11:56:27 -04:00
[DefaultValue(false)]
2020-03-10 02:27:12 -04:00
public bool UnhookTurnSpeed
{
get { return unhook_turn_speed ; }
set { unhook_turn_speed = value ; }
2019-08-06 06:44:26 -04:00
}
2020-04-22 16:24:24 -04:00
bool use_joystick = false ;
[Category("Controls")]
[DisplayName("Use Joystick")]
[Description("Enables Joystick functionality.")]
[DefaultValue(false)]
public bool UseJoystick
{
get { return use_joystick ; }
set { use_joystick = value ; }
}
2019-08-06 06:44:26 -04:00
bool full_screen = true ;
[Category("Graphics")]
[DisplayName("Run in Full Screen")]
[Description("Allows you to change modes without administrator privileges and registry editing.")]
2020-03-10 11:56:27 -04:00
[DefaultValue(true)]
2019-08-06 06:44:26 -04:00
public bool FullScreen
{
get { return full_screen ; }
set { full_screen = value ; }
}
2020-04-22 16:24:24 -04:00
bool draw_cursor = false ;
[Category("Graphics")]
[DisplayName("Draw Cursor")]
[Description("Renders an in-game cursor, rather than a standard Windows pointer.")]
[DefaultValue(false)]
public bool DrawCursor
{
get { return draw_cursor ; }
set { draw_cursor = value ; }
}
2019-08-06 06:44:26 -04:00
bool multiple_instances = false ;
[Category("System")]
[DisplayName("Allow Multiple Instances")]
[ Description ( "By default, LEGO Island will allow only one instance of itself to run. " +
"This patch allows infinite instances of LEGO Island to run." ) ]
2020-03-10 11:56:27 -04:00
[DefaultValue(false)]
2019-08-06 06:44:26 -04:00
public bool MultipleInstances
{
get { return multiple_instances ; }
set { multiple_instances = value ; }
}
bool stay_active_when_defocused = false ;
[Category("System")]
[DisplayName("Stay Active When Defocused")]
[ Description ( "By default, LEGO Island pauses when it's not the active window. " +
"This patch prevents that behavior." ) ]
2020-03-10 11:56:27 -04:00
[DefaultValue(false)]
2019-08-06 06:44:26 -04:00
public bool StayActiveWhenDefocused
{
get { return stay_active_when_defocused ; }
set { stay_active_when_defocused = value ; }
}
bool redirect_save_data = true ;
[Category("System")]
[DisplayName("Redirect Save Files to %APPDATA%")]
[ Description ( "By default LEGO Island saves its game data in its Program Files folder. In newer versions of " +
"Windows, this folder is considered privileged access, necessitating running LEGO Island as administrator " +
"to save here. This patch sets LEGO Island's save location to %APPDATA% instead, which is an accessible and " +
"standard location that most modern games and apps save to." ) ]
2020-03-10 11:56:27 -04:00
[DefaultValue(true)]
2019-08-06 06:44:26 -04:00
public bool RedirectSaveData
{
get { return redirect_save_data ; }
set { redirect_save_data = value ; }
}
FPSLimitType fps_limit_type = FPSLimitType . Default ;
[Category("Graphics")]
[DisplayName("FPS Cap")]
[Description("Modify LEGO Island's frame rate cap")]
2020-03-10 11:56:27 -04:00
[DefaultValue(FPSLimitType.Default)]
2019-08-06 06:44:26 -04:00
public FPSLimitType FPSLimit
{
get { return fps_limit_type ; }
set { fps_limit_type = value ; }
}
2020-08-21 13:42:06 -04:00
ModelQualityType model_quality = ModelQualityType . Medium ;
[Category("Graphics")]
[DisplayName("Model Quality")]
[Description("Change LEGO Island's default model quality")]
[DefaultValue(ModelQualityType.Medium)]
public ModelQualityType ModelQuality
{
get { return model_quality ; }
set { model_quality = value ; }
}
2020-03-10 11:56:27 -04:00
float custom_fps_limit = 24.0F ;
2019-08-06 06:44:26 -04:00
[Category("Graphics")]
[DisplayName("FPS Cap - Custom Limit")]
[Description("Is 'FPS Cap' is set to 'Limited', this will be the frame rate used.")]
2020-03-10 11:56:27 -04:00
[DefaultValue(24.0F)]
public float CustomFPS
2019-08-06 06:44:26 -04:00
{
get { return custom_fps_limit ; }
set { custom_fps_limit = value ; }
}
bool override_resolution = false ;
[Category("Experimental (Use at your own risk)")]
[DisplayName("Override Resolution")]
[ Description ( "Override LEGO Island's hardcoded 640x480 resolution with a custom resolution. " +
"NOTE: This patch is currently incomplete and buggy." ) ]
2020-03-10 11:56:27 -04:00
[DefaultValue(false)]
2019-08-06 06:44:26 -04:00
public bool OverrideResolution
{
get { return override_resolution ; }
set { override_resolution = value ; }
}
int resolution_width = 640 ;
[Category("Experimental (Use at your own risk)")]
[DisplayName("Override Resolution - Width:")]
[Description("If 'Override Resolution' is enabled, this is the screen resolution width to use instead.")]
2020-03-10 11:56:27 -04:00
[DefaultValue(640)]
2019-08-06 06:44:26 -04:00
public int ResolutionWidth
{
get { return resolution_width ; }
set { resolution_width = value ; }
}
int resolution_height = 480 ;
[Category("Experimental (Use at your own risk)")]
[DisplayName("Override Resolution - Height:")]
[Description("If 'Override Resolution' is enabled, this is the screen resolution height to use instead.")]
2020-03-10 11:56:27 -04:00
[DefaultValue(480)]
2019-08-06 06:44:26 -04:00
public int ResolutionHeight
{
get { return resolution_height ; }
set { resolution_height = value ; }
}
bool upscale_bitmaps = false ;
[Category("Experimental (Use at your own risk)")]
[DisplayName("Override Resolution - Bitmap Upscale")]
[Description("WARNING: This doesn't upscale the bitmaps' hitboxes yet and can make 2D areas like the Information Center difficult to navigate.")]
2020-03-10 11:56:27 -04:00
[DefaultValue(false)]
2019-08-06 06:44:26 -04:00
public bool UpscaleBitmaps
{
get { return upscale_bitmaps ; }
set { upscale_bitmaps = value ; }
}
2020-01-17 21:57:08 -05:00
bool disable_autofinish_building = false ;
[Category("Gameplay")]
[DisplayName("Disable Auto-Finish Building Section")]
[ Description ( "In LEGO Island v1.1, placing the last block when building will automatically end the building section. While convenient, " +
2020-04-22 16:24:24 -04:00
"this prevents players from making any further changes after placing the last brick. It also notably defies what Bill Ding says - you " +
2020-01-17 21:57:08 -05:00
"don't hit the triangle when you're finished building.\n\nThis patch restores the functionality in v1.0 where placing the last block " +
"will not automatically finish the build section." ) ]
2020-03-10 11:56:27 -04:00
[DefaultValue(false)]
2020-01-17 21:57:08 -05:00
public bool DisableAutoFinishBuilding
{
get { return disable_autofinish_building ; }
set { disable_autofinish_building = value ; }
}
2020-01-17 22:46:05 -05:00
2020-04-22 16:24:24 -04:00
bool music_toggle = true ;
[Category("Gameplay")]
[DisplayName("Play Music")]
[Description("Turns in-game music on or off.")]
[DefaultValue(true)]
public bool MusicToggle
{
get { return music_toggle ; }
set { music_toggle = value ; }
}
2020-03-10 11:56:27 -04:00
float fov_multiplier = 0.1F ;
2020-01-17 22:46:05 -05:00
[Category("Graphics")]
[DisplayName("Field of View Adjustment")]
[ Description ( "Globally adjusts the field of view by a multiplier\n\n" +
"0.1 = Default (smaller than 0.1 is more zoomed in, larger than 0.1 is more zoomed out" ) ]
2020-03-10 11:56:27 -04:00
[DefaultValue(0.1F)]
public float FOVMultiplier
2020-01-17 22:46:05 -05:00
{
get { return fov_multiplier ; }
set { fov_multiplier = value ; }
}
2020-03-10 02:27:12 -04:00
}
2019-08-06 06:44:26 -04:00
2020-03-10 04:39:11 -04:00
private PatchList patch_config ;
2019-08-06 06:44:26 -04:00
2020-03-10 04:39:11 -04:00
public class ConfigForm : Form
{
private LinkLabel update ;
2020-01-17 21:57:08 -05:00
2020-03-10 04:39:11 -04:00
private TabControl tabs ;
private PropertyGrid patch_view ;
2019-04-27 09:23:41 -04:00
2020-03-10 04:39:11 -04:00
public Button run_button ;
public Button run_additional_button ;
2019-04-27 09:23:41 -04:00
2020-03-10 04:39:11 -04:00
public const string run_button_run = "Run" ;
public const string run_button_kill = "Kill" ;
public TabPage music_page ;
public ConfigForm ( PatchList working_config , MusicInjector music_injector )
{
Size = new Size ( 420 , 420 ) ;
Text = "LEGO Island Rebuilder" ;
Icon = Icon . ExtractAssociatedIcon ( System . Reflection . Assembly . GetExecutingAssembly ( ) . Location ) ;
TableLayoutPanel grid = new TableLayoutPanel ( ) ;
grid . Dock = DockStyle . Fill ;
// Build standard layout
grid . SuspendLayout ( ) ;
Label title = new Label ( ) ;
title . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
title . Text = "LEGO Island Rebuilder" ;
title . Font = new Font ( title . Font , FontStyle . Bold ) ;
title . TextAlign = ContentAlignment . MiddleCenter ;
grid . Controls . Add ( title , 0 , 0 ) ;
LinkLabel subtitle = new LinkLabel ( ) ;
subtitle . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
subtitle . Text = "by MattKC (www.legoisland.org)" ;
subtitle . TextAlign = ContentAlignment . MiddleCenter ;
subtitle . LinkClicked + = new LinkLabelLinkClickedEventHandler ( AuthorLinkClick ) ;
grid . Controls . Add ( subtitle , 0 , 1 ) ;
update = new LinkLabel ( ) ;
update . Visible = false ;
update . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
update . Text = "An update is available!" ;
update . TextAlign = ContentAlignment . MiddleCenter ;
update . LinkClicked + = new LinkLabelLinkClickedEventHandler ( AuthorLinkClick ) ;
grid . Controls . Add ( update , 0 , 2 ) ;
// Set up patch view
patch_view = new PropertyGrid ( ) ;
patch_view . Dock = DockStyle . Fill ;
patch_view . SelectedObject = working_config ;
// Set up tabs
tabs = new TabControl ( ) ;
tabs . Dock = DockStyle . Fill ;
TabPage patches_page = new TabPage ( "Patches" ) ;
patches_page . Controls . Add ( patch_view ) ;
tabs . Controls . Add ( patches_page ) ;
music_page = new TabPage ( "Music" ) ;
music_page . Controls . Add ( music_injector ) ;
tabs . Controls . Add ( music_page ) ;
grid . Controls . Add ( tabs , 0 , 3 ) ;
TableLayoutPanel run_btns = new TableLayoutPanel ( ) ;
run_btns . Dock = DockStyle . Fill ;
run_btns . Padding = new Padding ( 0 ) ;
run_btns . Margin = new Padding ( 0 ) ;
run_btns . ColumnStyles . Add ( new ColumnStyle ( SizeType . Percent , 0.5F ) ) ;
run_btns . ColumnStyles . Add ( new ColumnStyle ( SizeType . Percent , 0.5F ) ) ;
run_button = new Button ( ) ;
run_button . Text = run_button_run ;
run_button . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
run_button . Font = new Font ( run_button . Font , FontStyle . Bold ) ;
run_btns . Controls . Add ( run_button , 0 , 0 ) ;
run_additional_button = new Button ( ) ;
run_additional_button . Visible = false ;
run_additional_button . Text = "Run Additional" ;
run_additional_button . Anchor = AnchorStyles . Left | AnchorStyles . Right ;
run_btns . Controls . Add ( run_additional_button , 1 , 0 ) ;
grid . Controls . Add ( run_btns , 0 , 4 ) ;
grid . RowStyles . Clear ( ) ;
grid . RowStyles . Add ( new RowStyle ( SizeType . Absolute , title . Height ) ) ;
grid . RowStyles . Add ( new RowStyle ( SizeType . Absolute , subtitle . Height ) ) ;
grid . RowStyles . Add ( new RowStyle ( SizeType . Absolute , update . Height ) ) ;
grid . RowStyles . Add ( new RowStyle ( SizeType . Percent , 100 ) ) ;
grid . RowStyles . Add ( new RowStyle ( SizeType . Absolute , run_button . Height + run_button . Margin . Top + run_button . Margin . Bottom ) ) ;
grid . ResumeLayout ( true ) ;
Controls . Add ( grid ) ;
ResumeLayout ( true ) ;
CenterToScreen ( ) ;
}
private void AuthorLinkClick ( object sender , LinkLabelLinkClickedEventArgs e )
{
Process . Start ( "https://www.legoisland.org/" ) ;
}
private void UpdateLinkClick ( object sender , LinkLabelLinkClickedEventArgs e )
{
Process . Start ( "https://www.legoisland.org/rebuilder" ) ;
}
}
public ConfigForm form ;
Rebuilder ( bool enable_ui ) {
LoadConfig ( ) ;
if ( enable_ui )
{
form = new ConfigForm ( patch_config , music_injector ) ;
form . run_button . Click + = new System . EventHandler ( this . Run ) ;
form . run_additional_button . Click + = new System . EventHandler ( this . RunAdditional ) ;
form . music_page . Enter + = new EventHandler ( this . ShowMusicInjectorForm ) ;
form . FormClosing + = new FormClosingEventHandler ( this . 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-12-31 12:21:38 -05:00
private static string GetDisplayNameOfProperty ( string property )
{
2020-01-17 22:46:05 -05:00
return ( ( DisplayNameAttribute ) typeof ( PatchList ) . GetProperty ( property ) . GetCustomAttributes ( typeof ( DisplayNameAttribute ) , true ) [ 0 ] ) . DisplayName ;
2019-12-31 12:21:38 -05:00
}
2020-03-10 03:26:41 -04:00
public static RegistryKey GetGameRegistryKey ( )
{
RegistryKey src = Registry . LocalMachine . OpenSubKey ( "SOFTWARE\\Mindscape\\LEGO Island" , false ) ;
if ( src = = null )
{
src = Registry . LocalMachine . OpenSubKey ( "SOFTWARE\\WOW6432Node\\Mindscape\\LEGO Island" , false ) ;
}
return src ;
}
2020-03-10 04:39:11 -04:00
public static string GetRegistryEntry ( string key )
{
using ( RegistryKey reg = GetGameRegistryKey ( ) )
{
if ( reg ! = null )
{
object o = reg . GetValue ( key ) ;
if ( o ! = null )
{
return o . ToString ( ) ;
}
}
}
return null ;
}
2020-03-10 02:27:12 -04:00
private static Version DetermineVersion ( string lego1dll_url )
{
using ( FileStream fs = new FileStream ( lego1dll_url , FileMode . Open , FileAccess . Read ) )
using ( BufferedStream bs = new BufferedStream ( fs ) )
using ( SHA1Managed sha1 = new SHA1Managed ( ) )
{
byte [ ] hash = sha1 . ComputeHash ( bs ) ;
StringBuilder formatted = new StringBuilder ( 2 * hash . Length ) ;
foreach ( byte b in hash )
{
formatted . AppendFormat ( "{0:X2}" , b ) ;
}
string final_hash = formatted . ToString ( ) ;
Version v = ( Version ) Array . IndexOf ( VersionHashes , final_hash ) ;
if ( v = = Version . kUnknown ) {
2020-03-22 09:26:51 -04:00
Log ( "Unknown version: " + final_hash ) ;
2020-03-10 02:27:12 -04:00
if ( MessageBox . Show ( "The version of LEGO Island you have installed is unknown to Rebuilder. This may result in unpredictable behavior. Would you like to continue?\n\n"
+ "Your version is: " + final_hash ,
"Unknown Version" ,
MessageBoxButtons . YesNo ,
MessageBoxIcon . Warning ) = = DialogResult . Yes )
{
2020-03-22 09:40:48 -04:00
return Version . kEnglish11 ;
2020-03-10 02:27:12 -04:00
}
}
return v ;
}
}
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 = "" ;
2020-03-21 21:46:23 -04:00
string isleexe_url = Path . Combine ( dir , "ISLE.EXE" ) ;
string lego1dll_url = Path . Combine ( dir , "LEGO1.DLL" ) ;
2020-03-10 02:27:12 -04:00
Version version = DetermineVersion ( lego1dll_url ) ;
2020-03-22 09:40:48 -04:00
Log ( "Found version: " + version ) ;
2020-03-10 02:27:12 -04:00
if ( version = = Version . kUnknown )
{
return false ;
}
using ( FileStream lego1dll = File . Open ( lego1dll_url , FileMode . Open , FileAccess . ReadWrite ) )
using ( FileStream isleexe = File . Open ( isleexe_url , FileMode . Open , FileAccess . ReadWrite ) )
2019-04-27 09:23:41 -04:00
{
2020-08-21 13:42:06 -04:00
long nav_offset , fov_offset_1 , fov_offset_2 , turn_speed_routine_loc , dsoundoffs1 ,
dsoundoffs2 , dsoundoffs3 , remove_fps_limit , jukebox_path_offset , model_quality_offset ;
2020-03-10 02:27:12 -04:00
switch ( version ) {
2020-03-22 09:40:48 -04:00
case Version . kEnglish10 :
2020-03-10 02:27:12 -04:00
nav_offset = 0xF2C28 ;
fov_offset_1 = 0xA1D67 ;
fov_offset_2 = 0xA1D32 ;
2020-03-22 09:26:51 -04:00
turn_speed_routine_loc = 0x54258 ;
dsoundoffs1 = 0xB48FB ;
dsoundoffs2 = 0xB48F1 ;
dsoundoffs3 = 0xAD7D3 ;
remove_fps_limit = 0x7A68B ;
jukebox_path_offset = 0xD28F6 ;
2020-08-21 13:42:06 -04:00
model_quality_offset = 0xFF028 ;
2020-03-10 02:27:12 -04:00
break ;
2020-03-22 09:40:48 -04:00
case Version . kEnglish11 :
2020-03-10 02:27:12 -04:00
default :
nav_offset = 0xF3228 ;
fov_offset_1 = 0xA22D7 ;
fov_offset_2 = 0xA22A2 ;
2020-03-22 09:26:51 -04:00
turn_speed_routine_loc = 0x544F8 ;
dsoundoffs1 = 0xB120B ;
dsoundoffs2 = 0xB1201 ;
dsoundoffs3 = 0xADD43 ;
remove_fps_limit = 0x7ABAB ;
jukebox_path_offset = 0xD2E66 ;
2020-08-21 13:42:06 -04:00
model_quality_offset = 0xFF648 ;
2020-03-22 09:26:51 -04:00
break ;
2020-03-22 09:40:48 -04:00
case Version . kGerman11 :
2020-03-22 09:26:51 -04:00
nav_offset = 0xF3428 ;
fov_offset_1 = 0xA2517 ;
fov_offset_2 = 0xA24E2 ;
turn_speed_routine_loc = 0x544F8 ;
dsoundoffs1 = 0xB144B ;
dsoundoffs2 = 0xB1441 ;
dsoundoffs3 = 0xADF83 ;
remove_fps_limit = 0x7AD9B ;
jukebox_path_offset = 0xD30A6 ;
2020-08-21 13:42:06 -04:00
model_quality_offset = 0xFF878 ;
2020-03-10 02:27:12 -04:00
break ;
2020-03-22 09:40:48 -04:00
case Version . kDanish11 :
nav_offset = 0xF3428 ;
fov_offset_1 = 0xA24C7 ;
fov_offset_2 = 0xA2492 ;
turn_speed_routine_loc = 0x544F8 ;
dsoundoffs1 = 0xB13FB ;
dsoundoffs2 = 0xB13F1 ;
dsoundoffs3 = 0xADF33 ;
remove_fps_limit = 0x7AD5B ;
jukebox_path_offset = 0xD3056 ;
2020-08-21 13:42:06 -04:00
model_quality_offset = 0xFF868 ;
2020-03-22 09:40:48 -04:00
break ;
2020-03-23 03:13:20 -04:00
case Version . kSpanish11 :
nav_offset = 0xF3228 ;
fov_offset_1 = 0xA2407 ;
fov_offset_2 = 0xA23D2 ;
turn_speed_routine_loc = 0x544F8 ;
dsoundoffs1 = 0xB133B ;
dsoundoffs2 = 0xB1331 ;
dsoundoffs3 = 0xADE73 ;
remove_fps_limit = 0x7ACBB ;
jukebox_path_offset = 0xD2F96 ;
2020-08-21 13:42:06 -04:00
model_quality_offset = 0xFF658 ;
2020-03-23 03:13:20 -04:00
break ;
2020-03-10 02:27:12 -04:00
}
WriteInt32 ( lego1dll , ( Int32 ) patch_config . MouseDeadzone , nav_offset ) ;
// Skip zero threshold
lego1dll . Position + = 4 ;
WriteFloat ( lego1dll , ( float ) patch_config . MovementMaxSpeed ) ;
WriteFloat ( lego1dll , ( float ) patch_config . TurnMaxSpeed ) ;
WriteFloat ( lego1dll , ( float ) patch_config . MovementMaxAcceleration ) ;
WriteFloat ( lego1dll , ( float ) patch_config . TurnMaxAcceleration ) ;
2019-10-22 19:15:30 -04:00
2020-03-10 02:27:12 -04:00
WriteFloat ( lego1dll , ( float ) patch_config . MovementMinAcceleration ) ;
WriteFloat ( lego1dll , ( float ) patch_config . TurnMinAcceleration ) ;
WriteFloat ( lego1dll , ( float ) patch_config . MovementDeceleration ) ;
WriteFloat ( lego1dll , ( float ) patch_config . TurnDeceleration ) ;
// Skip 0.4 value that we don't know yet
lego1dll . Position + = 4 ;
WriteInt32 ( lego1dll , Convert . ToInt32 ( patch_config . TurnUseVelocity ) ) ;
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 ) ;
2020-03-10 02:27:12 -04:00
if ( patch_config . UnhookTurnSpeed )
{
// Write routine to use frame delta time to adjust the turn speed
Write ( lego1dll , new byte [ ] { 0xD9 , 0x46 , 0x24 , 0xD8 , 0x4C , 0x24 , 0x14 , 0xD8 , 0x4E , 0x34 } , turn_speed_routine_loc ) ;
// Frees up 26 bytes
WriteManyBytes ( lego1dll , 0x90 , 26 ) ;
}
2019-08-06 06:44:26 -04:00
if ( patch_config . StayActiveWhenDefocused )
2019-04-27 11:22:28 -04:00
{
// 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
2020-03-10 02:27:12 -04:00
WriteByte ( lego1dll , 0x80 , dsoundoffs1 ) ;
2019-04-27 11:22:28 -04:00
WriteByte ( lego1dll , 0x80 , 0x5B96 ) ;
2020-03-10 02:27:12 -04:00
WriteByte ( lego1dll , 0x80 , dsoundoffs2 ) ;
WriteByte ( lego1dll , 0x80 , dsoundoffs3 ) ;
2019-04-27 11:22:28 -04:00
}
2019-08-06 06:44:26 -04:00
if ( patch_config . MultipleInstances )
2019-05-19 22:12:05 -04:00
{
// 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 ( "." ) ) ) ;
2020-03-21 21:46:23 -04:00
Uri uri2 = new Uri ( Path . Combine ( source_dir , "ISLE.EXE" ) ) ;
2019-05-11 05:34:13 -04:00
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
2020-03-10 02:27:12 -04:00
switch ( version ) {
2020-03-22 09:40:48 -04:00
case Version . kEnglish10 :
2019-05-11 05:34:13 -04:00
WriteByte ( lego1dll , 0xF6 , 0x51EF5 ) ;
WriteByte ( lego1dll , 0x34 ) ;
WriteByte ( lego1dll , 0x0D ) ;
WriteByte ( lego1dll , 0x10 ) ;
2020-03-10 02:27:12 -04:00
break ;
2020-03-22 09:40:48 -04:00
case Version . kEnglish11 :
2020-03-10 02:27:12 -04:00
default :
2019-05-11 05:34:13 -04:00
WriteByte ( lego1dll , 0x66 , 0x52195 ) ;
WriteByte ( lego1dll , 0x3A ) ;
WriteByte ( lego1dll , 0x0D ) ;
WriteByte ( lego1dll , 0x10 ) ;
2020-03-10 02:27:12 -04:00
break ;
2020-04-25 05:48:06 -04:00
case Version . kGerman11 :
WriteByte ( lego1dll , 0xA6 , 0x52195 ) ;
WriteByte ( lego1dll , 0x3C ) ;
WriteByte ( lego1dll , 0x0D ) ;
WriteByte ( lego1dll , 0x10 ) ;
break ;
case Version . kDanish11 :
WriteByte ( lego1dll , 0x56 , 0x52195 ) ;
WriteByte ( lego1dll , 0x3C ) ;
WriteByte ( lego1dll , 0x0D ) ;
WriteByte ( lego1dll , 0x10 ) ;
break ;
case Version . kSpanish11 :
WriteByte ( lego1dll , 0x96 , 0x52195 ) ;
WriteByte ( lego1dll , 0x3B ) ;
WriteByte ( lego1dll , 0x0D ) ;
WriteByte ( lego1dll , 0x10 ) ;
break ;
2020-03-10 02:27:12 -04:00
}
2019-05-11 05:34:13 -04:00
2020-03-10 02:27:12 -04:00
WriteString ( lego1dll , jukebox_path , jukebox_path_offset ) ;
2019-05-11 05:34:13 -04:00
}
2020-01-17 22:46:05 -05:00
// FOV Patch
2020-03-10 02:27:12 -04:00
WriteByte ( lego1dll , 0xEB , fov_offset_1 ) ;
2020-01-17 22:46:05 -05:00
WriteByte ( lego1dll , 0xC9 ) ;
//WriteByte(lego1dll, 0x90);
//WriteByte(lego1dll, 0x90);
2020-03-10 02:27:12 -04:00
WriteByte ( lego1dll , 0x68 , fov_offset_2 ) ;
2020-01-17 22:46:05 -05:00
WriteFloat ( lego1dll , ( float ) patch_config . FOVMultiplier ) ;
WriteByte ( lego1dll , 0xD8 ) ;
WriteByte ( lego1dll , 0x0C ) ;
WriteByte ( lego1dll , 0x24 ) ;
WriteByte ( lego1dll , 0x5E ) ;
WriteByte ( lego1dll , 0xEB ) ;
WriteByte ( lego1dll , 0x2E ) ;
2019-08-05 22:59:41 -04:00
// FPS Patch
2020-01-17 22:46:05 -05:00
2019-08-06 06:44:26 -04:00
if ( patch_config . FPSLimit = = FPSLimitType . Uncapped )
2019-08-05 22:59:41 -04:00
{
2019-08-06 06:44:26 -04:00
// Write zero frame delay resulting in uncapped frame rate
2019-08-05 22:59:41 -04:00
WriteInt32 ( isleexe , 0 , 0x4B4 ) ;
}
2019-08-06 06:44:26 -04:00
else if ( patch_config . FPSLimit = = FPSLimitType . Limited )
2019-08-05 22:59:41 -04:00
{
2019-08-06 06:44:26 -04:00
// Calculate frame delay and write new limit
2020-03-10 11:56:27 -04:00
Int32 delay = ( Int32 ) Math . Round ( 1000.0F / patch_config . CustomFPS ) ;
2019-08-05 22:59:41 -04:00
WriteInt32 ( isleexe , delay , 0x4B4 ) ;
}
2019-08-06 06:44:26 -04:00
if ( patch_config . FPSLimit ! = FPSLimitType . Default )
2019-08-05 22:59:41 -04:00
{
2019-08-06 06:44:26 -04:00
// Disables 30 FPS limit in Information Center when using software mode
2020-03-10 02:27:12 -04:00
WriteManyBytes ( lego1dll , 0x90 , 8 , remove_fps_limit ) ;
2019-08-05 22:59:41 -04:00
}
2020-08-21 13:42:06 -04:00
switch ( patch_config . ModelQuality ) {
case ModelQualityType . Low :
WriteFloat ( lego1dll , 0.0f , model_quality_offset ) ;
break ;
case ModelQualityType . Medium :
WriteFloat ( lego1dll , 3.6f , model_quality_offset ) ;
break ;
case ModelQualityType . High :
WriteFloat ( lego1dll , 5.0f , model_quality_offset ) ;
break ;
}
2019-04-29 10:26:03 -04:00
// INCOMPLETE: Resolution hack:
2019-08-06 06:44:26 -04:00
if ( patch_config . OverrideResolution )
2019-04-29 10:26:03 -04:00
{
// Changes window size
2019-08-06 06:44:26 -04:00
WriteInt32 ( isleexe , ( Int32 ) patch_config . ResolutionWidth , 0xE848 ) ;
WriteInt32 ( isleexe , ( Int32 ) patch_config . ResolutionHeight , 0xE84C ) ;
2019-04-29 10:26:03 -04:00
// Changes D3D render size
2019-08-06 06:44:26 -04:00
WriteInt32 ( isleexe , ( Int32 ) patch_config . ResolutionWidth - 1 , 0x4D0 ) ;
WriteInt32 ( isleexe , ( Int32 ) patch_config . ResolutionHeight - 1 , 0x4D7 ) ;
2019-05-19 22:12:05 -04:00
// Write code to upscale the bitmaps
2019-08-06 06:44:26 -04:00
if ( patch_config . UpscaleBitmaps )
2019-05-19 22:12:05 -04:00
{
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 ) ;
2019-08-06 06:44:26 -04:00
WriteFloat ( lego1dll , ( float ) patch_config . ResolutionHeight / 480.0f ) ;
2019-05-19 22:12:05 -04:00
2019-08-06 06:44:26 -04:00
Int32 x_offset = ( Int32 ) Math . Round ( ( patch_config . ResolutionWidth - ( patch_config . ResolutionHeight / 3.0 * 4.0 ) ) / 2.0 ) ;
2019-05-19 22:12:05 -04:00
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
2020-03-22 09:40:48 -04:00
if ( version = = Version . kEnglish10 & & patch_config . RedirectSaveData )
2019-05-03 11:43:59 -04:00
{
2019-12-31 12:21:38 -05:00
incompatibilities + = "- " + GetDisplayNameOfProperty ( "RedirectSaveData" ) + "\n" ;
2020-03-10 02:27:12 -04:00
patch_config . RedirectSaveData = false ;
2019-05-03 11:43:59 -04:00
}
2020-01-17 21:57:08 -05:00
if ( patch_config . DisableAutoFinishBuilding )
{
2020-03-22 09:40:48 -04:00
if ( version = = Version . kEnglish10 )
2020-01-17 21:57:08 -05:00
{
incompatibilities + = "- " + GetDisplayNameOfProperty ( "DisableAutoFinishBuilding" ) + "\n" ;
}
else
{
// Disables cutscene/exit code
WriteManyBytes ( lego1dll , 0x90 , 5 , 0x22C0B ) ;
// Disables flag that freezes the UI on completion
WriteManyBytes ( lego1dll , 0x90 , 7 , 0x22C6A ) ;
}
}
2019-05-03 11:43:59 -04:00
}
2020-03-10 03:26:41 -04:00
using ( RegistryKey src = GetGameRegistryKey ( ) )
2019-05-11 22:14:58 -04:00
{
2020-03-10 03:26:41 -04:00
if ( src = = null )
2019-05-03 11:43:59 -04:00
{
2020-03-10 03:26:41 -04:00
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 )
{
return false ;
}
2019-05-03 11:43:59 -04:00
}
2020-03-10 03:26:41 -04:00
else
2019-05-11 22:14:58 -04:00
{
2020-03-10 03:26:41 -04:00
using ( RegistryKey dst = Registry . CurrentUser . CreateSubKey ( "Software\\Mindscape\\LEGO Island" ) )
{
// Copy config data from HKLM to HKCU
CopyRegistryKey ( src , dst ) ;
2019-05-11 22:14:58 -04:00
2020-03-10 03:26:41 -04:00
// Set full screen value
dst . SetValue ( "Full Screen" , patch_config . FullScreen ? "YES" : "NO" ) ;
2019-05-11 22:14:58 -04:00
2020-04-22 16:24:24 -04:00
// Set draw cursor value
dst . SetValue ( "Draw Cursor" , patch_config . DrawCursor ? "YES" : "NO" ) ;
// Set Joystick toggle
dst . SetValue ( "UseJoystick" , patch_config . UseJoystick ? "YES" : "NO" ) ;
// Toggle music on or off
dst . SetValue ( "Music" , patch_config . MusicToggle ? "YES" : "NO" ) ;
2020-03-10 03:26:41 -04:00
// Redirect save path
if ( patch_config . RedirectSaveData )
{
string new_save_dir = Environment . GetFolderPath ( Environment . SpecialFolder . ApplicationData ) + "\\LEGO Island\\" ;
Directory . CreateDirectory ( new_save_dir ) ;
dst . SetValue ( "savepath" , new_save_dir ) ;
}
2019-05-11 22:14:58 -04:00
}
}
}
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 )
{
2020-03-21 21:46:23 -04:00
return ( File . Exists ( Path . Combine ( dir , "ISLE.EXE" ) ) & & File . Exists ( Path . Combine ( dir , "LEGO1.DLL" ) ) ) ;
2019-04-27 11:51:27 -04:00
}
2019-05-11 05:34:13 -04:00
private void ShowMusicInjectorForm ( object sender , EventArgs e )
{
2019-08-06 06:44:26 -04:00
if ( ! music_injector . Prepare ( ) )
2019-05-11 05:34:13 -04:00
{
}
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-08-06 06:44:26 -04:00
private void RunAdditional ( object sender , EventArgs e )
2019-08-05 22:59:41 -04:00
{
2019-08-06 06:44:26 -04:00
Process p = Process . Start ( processes [ 0 ] . StartInfo ) ;
p . EnableRaisingEvents = true ;
p . Exited + = new EventHandler ( ProcessExit ) ;
processes . Add ( p ) ;
2019-08-05 22:59:41 -04:00
}
2020-03-21 21:46:23 -04:00
static public void Log ( string text )
{
string log_path = Path . Combine ( Path . GetTempPath ( ) , "legoislandrebuilder.log" ) ;
using ( StreamWriter log = new StreamWriter ( log_path , true ) )
{
log . WriteLine ( "[" + DateTime . Now + "] " + text ) ;
log . Close ( ) ;
}
}
2020-03-10 04:39:11 -04:00
public void LaunchGame ( )
2019-04-27 09:23:41 -04:00
{
2020-03-21 21:46:23 -04:00
// If the game is already running, we assume this button is a "Kill" button
2019-08-06 06:44:26 -04:00
if ( processes . Count > 0 )
2019-04-28 01:12:43 -04:00
{
2019-08-06 06:44:26 -04:00
foreach ( Process p in processes )
{
p . Kill ( ) ;
}
processes . Clear ( ) ;
2019-04-28 01:12:43 -04:00
return ;
}
2020-03-21 21:46:23 -04:00
// Create temp path
string temp_path = Path . Combine ( Path . GetTempPath ( ) , "LEGOIslandRebuilder" ) ;
Log ( "Using working directory: " + temp_path ) ;
if ( Directory . Exists ( temp_path ) )
{
Log ( "Working directory already exists, no need to create" ) ;
}
else
{
try
{
Directory . CreateDirectory ( temp_path ) ;
Log ( "Working directory created successfully" ) ;
}
catch ( Exception e )
{
Log ( "Failed to create working directory: " + e . ToString ( ) ) ;
MessageBox . Show (
"Failed to create temporary path: " + e . ToString ( ) ,
"Failed to patch files" ,
MessageBoxButtons . OK ,
MessageBoxIcon . Error
) ;
return ;
}
}
// Search for game directory
Log ( "Searching for game directory..." ) ;
2019-04-27 09:23:41 -04:00
2020-03-21 21:46:23 -04:00
// Check our list of "standard paths"
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 ;
}
}
2020-03-21 21:46:23 -04:00
// Check registry for disk path ()
2020-03-10 03:26:41 -04:00
if ( string . IsNullOrEmpty ( dir ) )
{
2020-03-10 04:39:11 -04:00
dir = GetRegistryEntry ( "diskpath" ) ;
2020-03-10 03:26:41 -04:00
}
2019-04-27 11:51:27 -04:00
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
{
2020-03-21 21:46:23 -04:00
Log ( "Failed to find game directory, user cancelled prompt." ) ;
2019-04-27 11:51:27 -04:00
return ;
}
}
}
}
2019-04-27 09:23:41 -04:00
2020-03-21 21:46:23 -04:00
Log ( "Found game directory: " + dir ) ;
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 + + )
{
2020-03-21 21:46:23 -04:00
Log ( "Deleting existing file: " + dest_files [ i ] ) ;
2020-03-23 04:22:48 -04:00
File . SetAttributes ( dest_files [ i ] , FileAttributes . Normal ) ;
2019-04-28 00:58:17 -04:00
File . Delete ( dest_files [ i ] ) ;
2020-03-21 21:46:23 -04:00
}
}
catch ( Exception e )
{
Log ( "Failed to delete old files: " + e . ToString ( ) ) ;
MessageBox . Show ( "Failed to delete old files: " + e . ToString ( ) , "Error" , MessageBoxButtons . OK , MessageBoxIcon . Error ) ;
return ;
}
2019-04-28 00:58:17 -04:00
2020-03-21 21:46:23 -04:00
try
{
2019-04-28 00:58:17 -04:00
string [ ] src_files = Directory . GetFiles ( dir ) ;
for ( int i = 0 ; i < src_files . Length ; i + + )
2019-04-27 23:40:29 -04:00
{
2020-03-21 21:46:23 -04:00
Log ( "Copying game file: " + Path . GetFileName ( src_files [ i ] ) ) ;
File . Copy ( src_files [ i ] , Path . Combine ( temp_path , Path . GetFileName ( src_files [ i ] ) ) , true ) ;
2019-04-27 23:40:29 -04:00
}
2019-04-27 09:23:41 -04:00
}
2020-03-21 21:46:23 -04:00
catch ( Exception e )
2019-04-27 09:23:41 -04:00
{
2020-03-21 21:46:23 -04:00
Log ( "Failed to copy files for patching: " + e . ToString ( ) ) ;
MessageBox . Show ( "Failed to copy files for patching: " + e . ToString ( ) , "Error" , MessageBoxButtons . OK , MessageBoxIcon . Error ) ;
2019-04-27 09:23:41 -04:00
return ;
}
2019-05-11 05:34:13 -04:00
// Perform music insertion if necessary
if ( music_injector . ReplaceCount ( ) > 0 )
{
2020-03-21 21:46:23 -04:00
jukebox_output = Path . Combine ( dir , "LEGO/Scripts/REJUKEBOX.SI" ) ;
Log ( "Attempting to create injected jukebox file: " + jukebox_output ) ;
2019-05-14 03:41:31 -04:00
try
{
using ( FileStream test_fs = new FileStream ( jukebox_output , FileMode . Create , FileAccess . Write ) )
{
2020-03-21 21:46:23 -04:00
test_fs . Close ( ) ;
2019-05-14 03:41:31 -04:00
}
}
catch
{
2020-03-21 21:46:23 -04:00
jukebox_output = Path . Combine ( Path . GetTempPath ( ) , "REJUKEBOX.SI" ) ;
Log ( "Unable to use previous jukebox path, using alternative: " + jukebox_output ) ;
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
2020-03-21 21:46:23 -04:00
Log ( "Patching files" ) ;
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
2020-03-21 21:46:23 -04:00
Log ( "Creating compatibility registry keys" ) ;
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
{
2020-08-21 13:42:06 -04:00
string compat_string = "DWM8And16BitMitigation" ;
2019-05-19 22:12:05 -04:00
2019-08-06 06:44:26 -04:00
if ( ! patch_config . FullScreen )
2019-05-11 22:14:58 -04:00
{
2019-08-05 22:59:41 -04:00
if ( System . Environment . OSVersion . Version . Major < 8 )
{
// Use 16-bit on Windows 8+
compat_string + = " 16BITCOLOR" ;
}
else
{
// Older versions only have 256 color support
compat_string + = " 256COLOR" ;
}
2019-05-11 22:14:58 -04:00
}
2019-05-12 00:30:10 -04:00
2020-03-10 02:27:12 -04:00
if ( ! patch_config . RedirectSaveData )
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
}
2020-03-21 21:46:23 -04:00
Log ( "Launching game..." ) ;
ProcessStartInfo start_info = new ProcessStartInfo ( Path . Combine ( 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
{
2019-08-06 06:44:26 -04:00
Process p = Process . Start ( start_info ) ;
p . EnableRaisingEvents = true ;
p . Exited + = new EventHandler ( ProcessExit ) ;
processes . Add ( p ) ;
2020-03-10 04:39:11 -04:00
form . run_button . Text = ConfigForm . run_button_kill ;
2019-08-06 06:44:26 -04:00
if ( patch_config . MultipleInstances )
{
2020-03-10 04:39:11 -04:00
form . run_additional_button . Visible = true ;
2019-08-06 06:44:26 -04:00
}
2020-03-21 21:46:23 -04:00
Log ( "Game launched successfully" ) ;
2019-05-01 21:42:25 -04:00
}
2020-03-21 21:46:23 -04:00
catch ( Exception e )
2019-05-01 21:42:25 -04:00
{
2020-03-21 21:46:23 -04:00
Log ( "Failed to launch LEGO Island: " + e . ToString ( ) ) ;
MessageBox . Show (
"Failed to launch LEGO Island: " + e . ToString ( ) ,
"Failed to launch" ,
MessageBoxButtons . OK ,
MessageBoxIcon . Error
) ;
2019-05-01 21:42:25 -04:00
return ;
}
2019-04-28 01:12:43 -04:00
}
2019-04-27 09:23:41 -04:00
2020-03-10 04:39:11 -04:00
private void Run ( object sender , EventArgs e )
{
LaunchGame ( ) ;
}
2019-04-28 01:12:43 -04:00
private void ProcessExit ( object sender , EventArgs e )
{
2020-03-10 04:39:11 -04:00
form . run_button . BeginInvoke ( ( MethodInvoker ) delegate ( ) {
form . run_button . Text = ConfigForm . run_button_run ;
form . run_additional_button . Visible = false ;
2019-08-06 06:44:26 -04:00
} ) ;
for ( int i = 0 ; i < processes . Count ; i + + )
{
if ( processes [ i ] = = sender )
{
processes . RemoveAt ( i ) ;
break ;
}
}
2019-04-27 09:23:41 -04:00
}
2019-08-06 06:44:26 -04:00
private string GetSettingsDir ( )
2019-05-11 22:14:58 -04:00
{
2020-03-21 21:46:23 -04:00
string settings_path = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ApplicationData ) , "LEGOIslandRebuilder" ) ;
2019-05-11 22:14:58 -04:00
2019-08-11 09:55:20 -04:00
Directory . CreateDirectory ( settings_path ) ;
2019-05-11 22:14:58 -04:00
return settings_path ;
}
2019-08-06 06:44:26 -04:00
private string GetSettingsPath ( )
{
2020-03-21 21:46:23 -04:00
return Path . Combine ( GetSettingsDir ( ) , "settings.xml" ) ;
2019-08-06 06:44:26 -04:00
}
private string GetMusicSettingsPath ( )
{
2020-03-21 21:46:23 -04:00
return Path . Combine ( GetSettingsDir ( ) , "music.xml" ) ;
2019-08-06 06:44:26 -04:00
}
2020-03-10 04:39:11 -04:00
private void LoadConfig ( )
2019-05-11 22:14:58 -04:00
{
string settings_path = GetSettingsPath ( ) ;
2020-03-10 03:26:41 -04:00
bool config_loaded = false ;
2019-08-06 06:44:26 -04:00
// Load patch data
if ( File . Exists ( settings_path ) )
{
2019-08-06 18:47:33 -04:00
try
{
XmlSerializer serializer = new XmlSerializer ( typeof ( PatchList ) ) ;
TextReader reader = new StreamReader ( settings_path ) ;
patch_config = ( PatchList ) serializer . Deserialize ( reader ) ;
2020-03-10 03:26:41 -04:00
config_loaded = true ;
2019-08-06 18:47:33 -04:00
reader . Close ( ) ;
}
catch ( InvalidOperationException ) { }
2019-08-06 06:44:26 -04:00
}
2020-03-10 03:26:41 -04:00
if ( ! config_loaded )
{
patch_config = new PatchList ( ) ;
}
2019-08-06 06:44:26 -04:00
settings_path = GetMusicSettingsPath ( ) ;
// Load music patch data
2019-05-11 22:14:58 -04:00
if ( File . Exists ( settings_path ) )
{
XmlReader stream = XmlReader . Create ( settings_path ) ;
while ( stream . Read ( ) )
{
2019-08-06 06:44:26 -04:00
if ( stream . NodeType = = XmlNodeType . Element & & stream . IsStartElement ( ) & & stream . Name = = "music" )
2019-05-11 22:14:58 -04:00
{
2019-08-06 06:44:26 -04:00
music_injector . LoadData ( stream ) ;
break ;
2019-05-11 22:14:58 -04:00
}
}
stream . Close ( ) ;
}
}
private void OnClosing ( object sender , FormClosingEventArgs e )
{
2019-08-06 06:44:26 -04:00
// Load patch data
XmlSerializer serializer = new XmlSerializer ( typeof ( PatchList ) ) ;
TextWriter writer = new StreamWriter ( GetSettingsPath ( ) ) ;
serializer . Serialize ( writer , patch_config ) ;
writer . Close ( ) ;
// Load music injection data
XmlWriter music_writer = XmlWriter . Create ( GetMusicSettingsPath ( ) ) ;
music_writer . WriteStartDocument ( ) ;
music_writer . WriteStartElement ( "music" ) ;
music_injector . SaveData ( music_writer ) ;
music_writer . WriteEndElement ( ) ; // music
music_writer . WriteEndDocument ( ) ;
music_writer . Close ( ) ;
2019-05-11 22:14:58 -04:00
}
2019-04-27 09:23:41 -04:00
[STAThread]
static void Main ( )
{
Application . EnableVisualStyles ( ) ;
Application . SetCompatibleTextRenderingDefault ( false ) ;
2020-03-10 04:39:11 -04:00
bool autorun = false ;
// Parse command line options
string [ ] arguments = Environment . GetCommandLineArgs ( ) ;
foreach ( string a in arguments )
{
string lower_arg = a . ToLower ( ) ;
switch ( a )
{
case "--help" :
case "-h" :
MessageBox . Show (
"LEGO Island Rebuilder\n\nSupported arguments:\n\n--help, -h\nDisplays this help page.\n\n--run, -r\nRun LEGO Island immediately with the last used configuration." ,
"Command Line Argument Help" ,
MessageBoxButtons . OK ,
MessageBoxIcon . Information
) ;
Environment . Exit ( 0 ) ;
break ;
case "--run" :
case "-r" :
autorun = true ;
break ;
}
}
Rebuilder instance = new Rebuilder ( ! autorun ) ;
if ( autorun )
{
instance . LaunchGame ( ) ;
}
else
{
Application . Run ( instance . form ) ;
}
2019-04-27 09:23:41 -04:00
}
}
}