diff --git a/README.md b/README.md index 698708c..0ae11b1 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,21 @@ This project is still in its early stages, much more will hopefully come soon :) - Play with Max - Choose traps and gear - Choose your skin and your dye +- Choose Guardians - New extra difficulties for every map - New Mods: - - Remove Trap Cap - - Edit the starting coin - - God Mode - Automatic file backups -- Reset configuration button -- More coming soon +- Reset configuration button and config saves + +### Coming Next +- Trap Tiers +- Play with other starter heroes like Gabriella and Smolder +- Save multiple loadouts + +### Known Problems +- Hero HP doesn't scale as expected +- UI of the launcher is lacking + diff --git a/SingleplayerLauncher/Hero.cs b/SingleplayerLauncher/Hero.cs index 59d599e..52d8eb4 100644 --- a/SingleplayerLauncher/Hero.cs +++ b/SingleplayerLauncher/Hero.cs @@ -1,4 +1,6 @@ using System; +using System.Linq; +using System.Windows.Forms; namespace SingleplayerLauncher { @@ -20,35 +22,60 @@ public static Hero Instance } } - public string name { get; set; } - private int objectOffset { get; set; } public UPKFile UPKFile { get; set; } - private byte[] skinPattern; - public string skin { get; set; } - public string[] loadout { get; set; } // Could be it's own class if we get Guardians and Traits to work. - private byte[] loadoutHeader; + public string Skin { get; set; } + public string[] Loadout { get; set; } // Could be it's own class if we get Guardians and Traits to work. + public string[] Guardians { get; set; } private const int LoadoutSlotByteSize = 4; - private const int LoadoutSlotsNumber = 9; + private const int LoadoutSlotsNumber = 9; + // Where the actual array of the loadout starts is + 12 bytes from the loadout header. + // Array Size in bytes + Array (start?) index + Array number of elements (4 + 4 + 4 ) + private const int LoadoutOffsetFromHeader = 12; - // TODO Add resource file with the rest of heroes and remove this (and use above ones) + private const int GuardianSlotsNumber = 2; + // Where the actual array of the guardians starts is + 12 bytes from the loadout header. + // Array Size in bytes + Array (start?) index + Array number of elements (4 + 4 + 4 ) + private const int GuardiansOffsetFromHeader = 12; + + // TODO Add resource file with the rest of heroes and remove this (and use above ones only, the rest to resource files) private const string NameMaximilian = "Maximilian"; - private int HeroObjectOffsetMaximilian = 0x28AB495; // Within the file offset where it starts - private const string SpitfireGameUPKMaximilian = "..//SpitfireGame//CookedPCConsole//SpitfireGame.upk"; + private const int HeroObjectOffsetMaximillian = 0x28AB495; // Within the file offset where it starts (Maximillian currently default) + private const int HeroObjectSizeMaximillian = 1542; + // private const string SpitfireGameUPKMaximilian = "..//SpitfireGame//CookedPCConsole//SpitfireGame.upk"; private static readonly byte[] LoadoutHeaderMaximilian = new byte[] { 0xC6, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC5, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - private static readonly byte[] StartHeaderAfterLoadoutMaximilian = new byte[] { 0xF3, 0x2C, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0xC5, 0x07, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 - }; - private static readonly byte[] WavesHeaderMaximillian = new byte[] { 0xF3, 0x2c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 + private static readonly byte[] GuardiansHeaderMaximillian = new byte[] { 0xBA, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xC5, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + private static readonly int DefaultWaveClassesSectionLength = 64; + private static readonly byte[] DefaultWaveClassesHeaderMaximillian = new byte[] { + 0xF3, 0x2c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + private static readonly int HeroDamageTypeSectionLength = 40; + private static readonly byte[] HeroDamageTypeHeaderMaximillian = new byte[] { + 0x0E, 0x46, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; + private static readonly int StrategicRoleSectionLength = 40; + private static readonly byte[] StrategicRoleHeaderMaximillian = new byte[] { + 0x74, 0xA3, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + private static readonly int DefaultRoleClassSectionLength = 28; + private static readonly byte[] DefaultRoleClassHeaderMaximillian = new byte[] { + 0xE4, 0x2C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + private static readonly byte[] SkinPatternMaximilian = new byte[] { 0x52, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x66, 0x00, 0x00, @@ -64,56 +91,106 @@ public static Hero Instance 0x05, 0x00, 0x00, 0x00, 0x41, 0xCC, 0x02, 0x00 }; - private static readonly byte[] StartHeaderAfterGuardiansMaximillian = new byte[] { }; - - public void ApplySkin() - { - int skinIndex = UPKFile.FindBytesKMP(SkinPatternMaximilian, HeroObjectOffsetMaximilian) + SkinPatternMaximilian.Length; - UPKFile.OverrideBytes(Resources.skins[NameMaximilian][skin], skinIndex); - } - + + private static readonly byte[] WeaverTreeDefaultHeaderMaximillian = new byte[] { 0x00, 0xB4, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + private static readonly byte[] StartHeaderAfterGuardiansMaximillian = WeaverTreeDefaultHeaderMaximillian; + public void ApplyLoadoutChanges() { + RemoveByteSection(DefaultWaveClassesHeaderMaximillian, DefaultWaveClassesSectionLength); + RemoveByteSection(HeroDamageTypeHeaderMaximillian, HeroDamageTypeSectionLength); + RemoveByteSection(StrategicRoleHeaderMaximillian, StrategicRoleSectionLength); + RemoveByteSection(DefaultRoleClassHeaderMaximillian, DefaultRoleClassSectionLength); + ApplyTrapsGear(); - //ApplyGuardians(); //ApplyTraits(); - //ApplySkin(); + ApplySkin(); + + // FillRemovedBytes should be run after all the removing + if (UPKFile.nBytesRemoved > 0) + { + int positionToFillRemovedBytesWithZeros = UPKFile.FindBytesKMP(StartHeaderAfterGuardiansMaximillian, HeroObjectOffsetMaximillian, HeroObjectSizeMaximillian); + FillRemovedBytes(positionToFillRemovedBytesWithZeros); + } + + ApplyGuardians(); // Should go after everything else since it's where we are inserting the extra bytes and needs to know the size } private void ApplyTrapsGear() { - if (loadout == null || loadout.Length != 9) + if (Loadout == null || Loadout.Length != 9) throw new Exception("9 traps/gear must be used"); - int startIndex = UPKFile.FindBytesKMP(LoadoutHeaderMaximilian, HeroObjectOffsetMaximilian) + LoadoutHeaderMaximilian.Length; - int endIndex = UPKFile.FindBytesKMP(StartHeaderAfterLoadoutMaximilian, HeroObjectOffsetMaximilian); - - // Where the actual array of the loadout starts is + 12 bytes from the loadout header. - // Array Size in bytes // Array (start?) index // Array number of elements - // 4 + 4 + 4 - int arrayOffset = 12; + int startIndex = UPKFile.FindBytesKMP(LoadoutHeaderMaximilian, HeroObjectOffsetMaximillian, HeroObjectSizeMaximillian) + LoadoutHeaderMaximilian.Length; + int arrayElementCountIndex = startIndex + 8; + int arraySizeIndex = startIndex; - // Less than 9 slots are setup so we set them up to 9 and insert+remove bytes - if (endIndex - startIndex < 12 * 4) + // There aren't 9 slots set up so we create them and insert necessary bytes + if (UPKFile.getByte(arrayElementCountIndex) != 9) { - UPKFile.OverrideSingleByte((byte)(LoadoutSlotsNumber + 1) * LoadoutSlotByteSize, startIndex); // Array Size - UPKFile.OverrideSingleByte((byte)LoadoutSlotsNumber, startIndex + 8); // Array Element Count ( the 4 bytes inbetween are "index 0") + UPKFile.OverrideSingleByte((byte)(LoadoutSlotsNumber + 1) * LoadoutSlotByteSize, arraySizeIndex); // Array Size + UPKFile.OverrideSingleByte((byte)LoadoutSlotsNumber, arrayElementCountIndex); // Array Element Count ( the 4 bytes inbetween are "index 0") // Add new slots (2 slots) - UPKFile.InsertZeroedBytes(startIndex + arrayOffset + 8, 2 * LoadoutSlotByteSize); + UPKFile.InsertZeroedBytes(startIndex + LoadoutOffsetFromHeader + 8, 2 * LoadoutSlotByteSize); + } + + // Convert and apply Loadout + byte[] loadoutBytes = ConvertLoadoutToBytes(Loadout); + UPKFile.OverrideBytes(loadoutBytes, startIndex + LoadoutOffsetFromHeader); + } + + private void ApplyGuardians() + { + if (Guardians == null || Guardians.Length != 2) + throw new Exception("2 guardians must be used"); + + int startIndex = UPKFile.FindBytesKMP(GuardiansHeaderMaximillian, HeroObjectOffsetMaximillian, HeroObjectSizeMaximillian) + GuardiansHeaderMaximillian.Length; + int endIndex = UPKFile.FindBytesKMP(StartHeaderAfterGuardiansMaximillian, startIndex + GuardiansOffsetFromHeader, HeroObjectSizeMaximillian); + int totalSize = endIndex - startIndex; // Everything after header + + byte[] firstGuardian = Resources.guardians[Guardians[0]].First; // Extra space after each guardian is already included + byte[] secondGuardian = Resources.guardians[Guardians[1]].First; + + byte[] sizeFirstGuardian = new byte[] {Resources.guardians[Guardians[0]].Second, 0x00, 0x00, 0x00 }; // Add the 0x00 to complete the 4 bytes field + + int secondGuardianOffset = firstGuardian.Length + sizeFirstGuardian.Length + GuardiansOffsetFromHeader + 4; // 4 from second guardian size itself + byte[] sizeSecondGuardian = new byte[] {(byte) (totalSize - secondGuardianOffset), 0x00, 0x00, 0x00 }; // Counting extra space + + int emptySpaceOffset = secondGuardianOffset + Resources.guardians[Guardians[1]].Second; + byte[] emptySpace = Enumerable.Repeat((byte)0x00, totalSize - emptySpaceOffset).ToArray(); - // Remove 8 bytes to make space for 2 slots - int removeIndex = UPKFile.FindBytesKMP(IconToRemoveFromFileBytes, HeroObjectOffsetMaximilian); - int nBytesRemove = 2 * LoadoutSlotByteSize; - UPKFile.RemoveBytes(removeIndex, nBytesRemove); + // Combining arrays to SizeGuardian1 + Guardian1 + SizeGuardian2 + Guardian2 + emptySpace + byte[] guardiansBytes = sizeFirstGuardian.Concat(firstGuardian).Concat(sizeSecondGuardian).Concat(secondGuardian).Concat(emptySpace).ToArray(); - // set to 0 the icon field (20 bytes remaining, we removed 8) - UPKFile.OverrideBytes(UPKFile.CreateZeroedByteArray(IconToRemoveFromFileBytes.Length - nBytesRemove), removeIndex); + UPKFile.OverrideBytes(guardiansBytes, startIndex + GuardiansOffsetFromHeader); + + UPKFile.OverrideSingleByte((byte) (totalSize - 8), startIndex); // Size (counts array element count and both guardians and their sizes but not itself or index so -8) + //TODO check single byte and avoid override? + UPKFile.OverrideSingleByte(GuardianSlotsNumber, startIndex + 8); // Array Element Count + } + + private void ApplySkin() + { + int skinIndex = UPKFile.FindBytesKMP(SkinPatternMaximilian, HeroObjectOffsetMaximillian, HeroObjectSizeMaximillian) + SkinPatternMaximilian.Length; + UPKFile.OverrideBytes(Resources.skins[NameMaximilian][Skin], skinIndex); + } + + private void RemoveByteSection(byte[] sectionHeaderByteArray, int length) + { + int removeIndex = UPKFile.FindBytesKMP(sectionHeaderByteArray, HeroObjectOffsetMaximillian, HeroObjectSizeMaximillian); + + if (removeIndex != -1) + { + UPKFile.RemoveBytes(removeIndex, length); } + } - // Convert and apply Loadout - byte[] loadoutBytes = ConvertLoadoutToBytes(loadout); - UPKFile.OverrideBytes(loadoutBytes, startIndex + arrayOffset); + private void FillRemovedBytes(int insertIndex) + { + UPKFile.InsertZeroedBytes(insertIndex, 0); } private byte[] ConvertLoadoutToBytes(string[] loadout) @@ -122,7 +199,7 @@ private byte[] ConvertLoadoutToBytes(string[] loadout) for (int i = 0; i < loadout.Length; i++) { - byte[] slotBytes = new byte[LoadoutSlotByteSize]; + byte[] slotBytes; if (Resources.traps.ContainsKey(loadout[i])) { @@ -141,5 +218,6 @@ private byte[] ConvertLoadoutToBytes(string[] loadout) return loadoutBytes; } + } } diff --git a/SingleplayerLauncher/LauncherMainForm.Designer.cs b/SingleplayerLauncher/LauncherMainForm.Designer.cs index aee3160..036b59e 100644 --- a/SingleplayerLauncher/LauncherMainForm.Designer.cs +++ b/SingleplayerLauncher/LauncherMainForm.Designer.cs @@ -173,6 +173,7 @@ private void InitializeComponent() this.chkLog.TabIndex = 6; this.chkLog.Text = "Log"; this.chkLog.UseVisualStyleBackColor = true; + this.chkLog.CheckedChanged += new System.EventHandler(this.ChkLog_CheckedChanged); // // btnLoadoutEditor // diff --git a/SingleplayerLauncher/LauncherMainForm.cs b/SingleplayerLauncher/LauncherMainForm.cs index a7f82ec..8137ba7 100644 --- a/SingleplayerLauncher/LauncherMainForm.cs +++ b/SingleplayerLauncher/LauncherMainForm.cs @@ -1,4 +1,5 @@ -using SingleplayerLauncher.Mods; +using Newtonsoft.Json.Linq; +using SingleplayerLauncher.Mods; using System; using System.Collections.Generic; using System.Diagnostics; @@ -181,7 +182,30 @@ private void Form1_Load(object sender, EventArgs e) chkCustomIni.Checked = defaultCustomIniSetting; - hero.loadout = Resources.defaultLoadout; + hero.Loadout = Resources.defaultLoadout; + if (Settings.Instance.ContainsKey("hero")) + comBoxHero.SelectedItem = Settings.Instance["hero"]; + if (Settings.Instance.ContainsKey("skin")) + comBoxSkin.SelectedItem = Settings.Instance["skin"]; + if (Settings.Instance.ContainsKey("dye")) + comBoxDye.SelectedItem = Settings.Instance["dye"]; + if (Settings.Instance.ContainsKey("map")) + comBoxMap.SelectedItem = Settings.Instance["map"]; + if (Settings.Instance.ContainsKey("gameMode")) + comBoxGameMode.SelectedItem = Settings.Instance["gameMode"]; + if (Settings.Instance.ContainsKey("difficulty")) + comBoxDifficulty.SelectedItem = Settings.Instance["difficulty"]; + if (Settings.Instance.ContainsKey("extraDifficulty")) + comBoxExtraDifficulty.SelectedItem = Settings.Instance["extraDifficulty"]; + if (Settings.Instance.ContainsKey("customIni")) + chkCustomIni.Checked = (bool)Settings.Instance["customIni"]; + if (Settings.Instance.ContainsKey("log")) + chkLog.Checked = (bool)Settings.Instance["log"]; + if (Settings.Instance.ContainsKey("loadout")) + { + var savedLoadOut = ((JArray)Settings.Instance["loadout"]).ToObject(); + hero.Loadout = savedLoadOut; + } } private void btnLaunch_Click(object sender, EventArgs e) @@ -194,23 +218,35 @@ private void btnLaunch_Click(object sender, EventArgs e) UpdateCharacterDataIni(); UpdateDefaultGameIni(); } - if (comBoxSkin.SelectedItem != null || LoadoutEditorForm.bytes.Count > 0) + if (comBoxSkin.SelectedItem != null) { - hero.skin = comBoxSkin.SelectedItem.ToString(); - hero.ApplySkin(); + hero.Skin = comBoxSkin.SelectedItem.ToString(); } - + + MessageBox.Show("Saving your changes. Please wait."); hero.ApplyLoadoutChanges(); ApplyMods(spitfireGameUPKFile); - MessageBox.Show("Saving your changes. Please wait."); spitfireGameUPKFile.Save(); MessageBox.Show("Finished"); - + SaveSettings(); StartGame(); } + public void SaveSettings() + { + Settings.Instance["hero"] = comBoxHero.SelectedItem; + Settings.Instance["skin"] = comBoxSkin.SelectedItem; + Settings.Instance["dye"] = comBoxDye.SelectedItem; + Settings.Instance["map"] = comBoxMap.SelectedItem; + Settings.Instance["gameMode"] = comBoxGameMode.SelectedItem; + Settings.Instance["difficulty"] = comBoxDifficulty.SelectedItem; + Settings.Instance["extraDifficulty"] = comBoxExtraDifficulty.SelectedItem; + Settings.Instance["customIni"] = chkCustomIni.Checked; + Settings.Instance["log"] = chkLog.Checked; + Settings.Save(); + } private static void ApplyMods(UPKFile spitfireGameUPKFile) { NoTrapCap ntp = new NoTrapCap(spitfireGameUPKFile); @@ -484,6 +520,10 @@ private void btnMods_Click(object sender, EventArgs e) ModLoaderForm mlf = new ModLoaderForm(); mlf.Show(); } + + private void ChkLog_CheckedChanged(object sender, EventArgs e) + { + } } } diff --git a/SingleplayerLauncher/LoadoutEditorForm.Designer.cs b/SingleplayerLauncher/LoadoutEditorForm.Designer.cs index 1bbb56f..fc20d8f 100644 --- a/SingleplayerLauncher/LoadoutEditorForm.Designer.cs +++ b/SingleplayerLauncher/LoadoutEditorForm.Designer.cs @@ -39,14 +39,31 @@ private void InitializeComponent() this.comBoxLoadoutSlot8 = new System.Windows.Forms.ComboBox(); this.comBoxLoadoutSlot9 = new System.Windows.Forms.ComboBox(); this.btnSave = new System.Windows.Forms.Button(); - this.cmbHero = new System.Windows.Forms.ComboBox(); + this.comBoxHero = new System.Windows.Forms.ComboBox(); + this.groupBoxLoadout = new System.Windows.Forms.GroupBox(); + this.labelLoadoutSlot9 = new System.Windows.Forms.Label(); + this.labelLoadoutSlot8 = new System.Windows.Forms.Label(); + this.labelLoadoutSlot7 = new System.Windows.Forms.Label(); + this.labelLoadoutSlot6 = new System.Windows.Forms.Label(); + this.labelLoadoutSlot5 = new System.Windows.Forms.Label(); + this.labelLoadoutSlot4 = new System.Windows.Forms.Label(); + this.labelLoadoutSlot3 = new System.Windows.Forms.Label(); + this.labelLoadoutSlot2 = new System.Windows.Forms.Label(); + this.labelLoadoutSlot1 = new System.Windows.Forms.Label(); + this.groupBoxGuardians = new System.Windows.Forms.GroupBox(); + this.labelGuardianSlot1 = new System.Windows.Forms.Label(); + this.labelGuardianSlot2 = new System.Windows.Forms.Label(); + this.comBoxGuardianSlot1 = new System.Windows.Forms.ComboBox(); + this.comBoxGuardianSlot2 = new System.Windows.Forms.ComboBox(); + this.groupBoxLoadout.SuspendLayout(); + this.groupBoxGuardians.SuspendLayout(); this.SuspendLayout(); // // comBoxLoadoutSlot1 // this.comBoxLoadoutSlot1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comBoxLoadoutSlot1.FormattingEnabled = true; - this.comBoxLoadoutSlot1.Location = new System.Drawing.Point(12, 37); + this.comBoxLoadoutSlot1.Location = new System.Drawing.Point(45, 19); this.comBoxLoadoutSlot1.Name = "comBoxLoadoutSlot1"; this.comBoxLoadoutSlot1.Size = new System.Drawing.Size(216, 21); this.comBoxLoadoutSlot1.TabIndex = 0; @@ -55,7 +72,7 @@ private void InitializeComponent() // this.comBoxLoadoutSlot2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comBoxLoadoutSlot2.FormattingEnabled = true; - this.comBoxLoadoutSlot2.Location = new System.Drawing.Point(12, 64); + this.comBoxLoadoutSlot2.Location = new System.Drawing.Point(45, 46); this.comBoxLoadoutSlot2.Name = "comBoxLoadoutSlot2"; this.comBoxLoadoutSlot2.Size = new System.Drawing.Size(216, 21); this.comBoxLoadoutSlot2.TabIndex = 1; @@ -64,7 +81,7 @@ private void InitializeComponent() // this.comBoxLoadoutSlot3.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comBoxLoadoutSlot3.FormattingEnabled = true; - this.comBoxLoadoutSlot3.Location = new System.Drawing.Point(12, 91); + this.comBoxLoadoutSlot3.Location = new System.Drawing.Point(45, 73); this.comBoxLoadoutSlot3.Name = "comBoxLoadoutSlot3"; this.comBoxLoadoutSlot3.Size = new System.Drawing.Size(216, 21); this.comBoxLoadoutSlot3.TabIndex = 2; @@ -73,7 +90,7 @@ private void InitializeComponent() // this.comBoxLoadoutSlot4.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comBoxLoadoutSlot4.FormattingEnabled = true; - this.comBoxLoadoutSlot4.Location = new System.Drawing.Point(12, 118); + this.comBoxLoadoutSlot4.Location = new System.Drawing.Point(45, 100); this.comBoxLoadoutSlot4.Name = "comBoxLoadoutSlot4"; this.comBoxLoadoutSlot4.Size = new System.Drawing.Size(216, 21); this.comBoxLoadoutSlot4.TabIndex = 3; @@ -82,7 +99,7 @@ private void InitializeComponent() // this.comBoxLoadoutSlot5.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comBoxLoadoutSlot5.FormattingEnabled = true; - this.comBoxLoadoutSlot5.Location = new System.Drawing.Point(12, 145); + this.comBoxLoadoutSlot5.Location = new System.Drawing.Point(45, 127); this.comBoxLoadoutSlot5.Name = "comBoxLoadoutSlot5"; this.comBoxLoadoutSlot5.Size = new System.Drawing.Size(216, 21); this.comBoxLoadoutSlot5.TabIndex = 4; @@ -91,7 +108,7 @@ private void InitializeComponent() // this.comBoxLoadoutSlot6.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comBoxLoadoutSlot6.FormattingEnabled = true; - this.comBoxLoadoutSlot6.Location = new System.Drawing.Point(12, 172); + this.comBoxLoadoutSlot6.Location = new System.Drawing.Point(45, 154); this.comBoxLoadoutSlot6.Name = "comBoxLoadoutSlot6"; this.comBoxLoadoutSlot6.Size = new System.Drawing.Size(216, 21); this.comBoxLoadoutSlot6.TabIndex = 5; @@ -100,7 +117,7 @@ private void InitializeComponent() // this.comBoxLoadoutSlot7.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comBoxLoadoutSlot7.FormattingEnabled = true; - this.comBoxLoadoutSlot7.Location = new System.Drawing.Point(12, 199); + this.comBoxLoadoutSlot7.Location = new System.Drawing.Point(45, 181); this.comBoxLoadoutSlot7.Name = "comBoxLoadoutSlot7"; this.comBoxLoadoutSlot7.Size = new System.Drawing.Size(216, 21); this.comBoxLoadoutSlot7.TabIndex = 6; @@ -109,7 +126,7 @@ private void InitializeComponent() // this.comBoxLoadoutSlot8.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comBoxLoadoutSlot8.FormattingEnabled = true; - this.comBoxLoadoutSlot8.Location = new System.Drawing.Point(12, 226); + this.comBoxLoadoutSlot8.Location = new System.Drawing.Point(45, 208); this.comBoxLoadoutSlot8.Name = "comBoxLoadoutSlot8"; this.comBoxLoadoutSlot8.Size = new System.Drawing.Size(216, 21); this.comBoxLoadoutSlot8.TabIndex = 7; @@ -118,14 +135,14 @@ private void InitializeComponent() // this.comBoxLoadoutSlot9.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comBoxLoadoutSlot9.FormattingEnabled = true; - this.comBoxLoadoutSlot9.Location = new System.Drawing.Point(12, 253); + this.comBoxLoadoutSlot9.Location = new System.Drawing.Point(45, 235); this.comBoxLoadoutSlot9.Name = "comBoxLoadoutSlot9"; this.comBoxLoadoutSlot9.Size = new System.Drawing.Size(216, 21); this.comBoxLoadoutSlot9.TabIndex = 8; // // btnSave // - this.btnSave.Location = new System.Drawing.Point(12, 280); + this.btnSave.Location = new System.Drawing.Point(159, 321); this.btnSave.Name = "btnSave"; this.btnSave.Size = new System.Drawing.Size(216, 23); this.btnSave.TabIndex = 9; @@ -133,35 +150,189 @@ private void InitializeComponent() this.btnSave.UseVisualStyleBackColor = true; this.btnSave.Click += new System.EventHandler(this.btnSave_Click); // - // cmbHero + // comBoxHero // - this.cmbHero.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.cmbHero.Enabled = false; - this.cmbHero.FormattingEnabled = true; - this.cmbHero.Location = new System.Drawing.Point(12, 12); - this.cmbHero.Name = "cmbHero"; - this.cmbHero.Size = new System.Drawing.Size(216, 21); - this.cmbHero.TabIndex = 10; + this.comBoxHero.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comBoxHero.Enabled = false; + this.comBoxHero.FormattingEnabled = true; + this.comBoxHero.Location = new System.Drawing.Point(35, 12); + this.comBoxHero.Name = "comBoxHero"; + this.comBoxHero.Size = new System.Drawing.Size(216, 21); + this.comBoxHero.TabIndex = 10; + // + // groupBoxLoadout + // + this.groupBoxLoadout.Controls.Add(this.labelLoadoutSlot9); + this.groupBoxLoadout.Controls.Add(this.labelLoadoutSlot8); + this.groupBoxLoadout.Controls.Add(this.labelLoadoutSlot7); + this.groupBoxLoadout.Controls.Add(this.labelLoadoutSlot6); + this.groupBoxLoadout.Controls.Add(this.labelLoadoutSlot5); + this.groupBoxLoadout.Controls.Add(this.labelLoadoutSlot4); + this.groupBoxLoadout.Controls.Add(this.labelLoadoutSlot3); + this.groupBoxLoadout.Controls.Add(this.labelLoadoutSlot2); + this.groupBoxLoadout.Controls.Add(this.labelLoadoutSlot1); + this.groupBoxLoadout.Controls.Add(this.comBoxLoadoutSlot1); + this.groupBoxLoadout.Controls.Add(this.comBoxLoadoutSlot2); + this.groupBoxLoadout.Controls.Add(this.comBoxLoadoutSlot3); + this.groupBoxLoadout.Controls.Add(this.comBoxLoadoutSlot9); + this.groupBoxLoadout.Controls.Add(this.comBoxLoadoutSlot4); + this.groupBoxLoadout.Controls.Add(this.comBoxLoadoutSlot8); + this.groupBoxLoadout.Controls.Add(this.comBoxLoadoutSlot5); + this.groupBoxLoadout.Controls.Add(this.comBoxLoadoutSlot7); + this.groupBoxLoadout.Controls.Add(this.comBoxLoadoutSlot6); + this.groupBoxLoadout.Location = new System.Drawing.Point(12, 39); + this.groupBoxLoadout.Name = "groupBox1"; + this.groupBoxLoadout.Size = new System.Drawing.Size(273, 266); + this.groupBoxLoadout.TabIndex = 11; + this.groupBoxLoadout.TabStop = false; + this.groupBoxLoadout.Text = "Loadout"; + // + // labelLoadoutSlot9 + // + this.labelLoadoutSlot9.AutoSize = true; + this.labelLoadoutSlot9.Location = new System.Drawing.Point(5, 238); + this.labelLoadoutSlot9.Name = "label9"; + this.labelLoadoutSlot9.Size = new System.Drawing.Size(34, 13); + this.labelLoadoutSlot9.TabIndex = 17; + this.labelLoadoutSlot9.Text = "Slot 9"; + // + // labelLoadoutSlot8 + // + this.labelLoadoutSlot8.AutoSize = true; + this.labelLoadoutSlot8.Location = new System.Drawing.Point(5, 211); + this.labelLoadoutSlot8.Name = "label8"; + this.labelLoadoutSlot8.Size = new System.Drawing.Size(34, 13); + this.labelLoadoutSlot8.TabIndex = 16; + this.labelLoadoutSlot8.Text = "Slot 8"; + // + // labelLoadoutSlot7 + // + this.labelLoadoutSlot7.AutoSize = true; + this.labelLoadoutSlot7.Location = new System.Drawing.Point(6, 184); + this.labelLoadoutSlot7.Name = "label7"; + this.labelLoadoutSlot7.Size = new System.Drawing.Size(34, 13); + this.labelLoadoutSlot7.TabIndex = 15; + this.labelLoadoutSlot7.Text = "Slot 7"; + // + // labelLoadoutSlot6 + // + this.labelLoadoutSlot6.AutoSize = true; + this.labelLoadoutSlot6.Location = new System.Drawing.Point(5, 157); + this.labelLoadoutSlot6.Name = "label6"; + this.labelLoadoutSlot6.Size = new System.Drawing.Size(34, 13); + this.labelLoadoutSlot6.TabIndex = 14; + this.labelLoadoutSlot6.Text = "Slot 6"; + // + // labelLoadoutSlot5 + // + this.labelLoadoutSlot5.AutoSize = true; + this.labelLoadoutSlot5.Location = new System.Drawing.Point(5, 130); + this.labelLoadoutSlot5.Name = "label5"; + this.labelLoadoutSlot5.Size = new System.Drawing.Size(34, 13); + this.labelLoadoutSlot5.TabIndex = 13; + this.labelLoadoutSlot5.Text = "Slot 5"; + // + // labelLoadoutSlot4 + // + this.labelLoadoutSlot4.AutoSize = true; + this.labelLoadoutSlot4.Location = new System.Drawing.Point(5, 103); + this.labelLoadoutSlot4.Name = "label4"; + this.labelLoadoutSlot4.Size = new System.Drawing.Size(34, 13); + this.labelLoadoutSlot4.TabIndex = 12; + this.labelLoadoutSlot4.Text = "Slot 4"; + // + // labelLoadoutSlot3 + // + this.labelLoadoutSlot3.AutoSize = true; + this.labelLoadoutSlot3.Location = new System.Drawing.Point(5, 76); + this.labelLoadoutSlot3.Name = "label3"; + this.labelLoadoutSlot3.Size = new System.Drawing.Size(34, 13); + this.labelLoadoutSlot3.TabIndex = 11; + this.labelLoadoutSlot3.Text = "Slot 3"; + // + // labelLoadoutSlot2 + // + this.labelLoadoutSlot2.AutoSize = true; + this.labelLoadoutSlot2.Location = new System.Drawing.Point(5, 49); + this.labelLoadoutSlot2.Name = "label2"; + this.labelLoadoutSlot2.Size = new System.Drawing.Size(34, 13); + this.labelLoadoutSlot2.TabIndex = 10; + this.labelLoadoutSlot2.Text = "Slot 2"; + // + // labelLoadoutSlot1 + // + this.labelLoadoutSlot1.AutoSize = true; + this.labelLoadoutSlot1.Location = new System.Drawing.Point(5, 22); + this.labelLoadoutSlot1.Name = "label1"; + this.labelLoadoutSlot1.Size = new System.Drawing.Size(34, 13); + this.labelLoadoutSlot1.TabIndex = 9; + this.labelLoadoutSlot1.Text = "Slot 1"; + // + // groupBoxGuardians + // + this.groupBoxGuardians.Controls.Add(this.labelGuardianSlot1); + this.groupBoxGuardians.Controls.Add(this.comBoxGuardianSlot1); + this.groupBoxGuardians.Controls.Add(this.labelGuardianSlot2); + this.groupBoxGuardians.Controls.Add(this.comBoxGuardianSlot2); + this.groupBoxGuardians.Location = new System.Drawing.Point(291, 39); + this.groupBoxGuardians.Name = "groupBox2"; + this.groupBoxGuardians.Size = new System.Drawing.Size(273, 76); + this.groupBoxGuardians.TabIndex = 12; + this.groupBoxGuardians.TabStop = false; + this.groupBoxGuardians.Text = "Guardians"; + // + // labelGuardianSlot1 + // + this.labelGuardianSlot1.AutoSize = true; + this.labelGuardianSlot1.Location = new System.Drawing.Point(5, 49); + this.labelGuardianSlot1.Name = "label10"; + this.labelGuardianSlot1.Size = new System.Drawing.Size(34, 13); + this.labelGuardianSlot1.TabIndex = 21; + this.labelGuardianSlot1.Text = "Slot 2"; + // + // labelGuardianSlot2 + // + this.labelGuardianSlot2.AutoSize = true; + this.labelGuardianSlot2.Location = new System.Drawing.Point(5, 22); + this.labelGuardianSlot2.Name = "label11"; + this.labelGuardianSlot2.Size = new System.Drawing.Size(34, 13); + this.labelGuardianSlot2.TabIndex = 20; + this.labelGuardianSlot2.Text = "Slot 1"; + // + // comBoxGuardianSlot1 + // + this.comBoxGuardianSlot1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comBoxGuardianSlot1.FormattingEnabled = true; + this.comBoxGuardianSlot1.Location = new System.Drawing.Point(45, 19); + this.comBoxGuardianSlot1.Name = "comboBox1"; + this.comBoxGuardianSlot1.Size = new System.Drawing.Size(216, 21); + this.comBoxGuardianSlot1.TabIndex = 18; + // + // comBoxGuardianSlot2 + // + this.comBoxGuardianSlot2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comBoxGuardianSlot2.FormattingEnabled = true; + this.comBoxGuardianSlot2.Location = new System.Drawing.Point(45, 46); + this.comBoxGuardianSlot2.Name = "comboBox2"; + this.comBoxGuardianSlot2.Size = new System.Drawing.Size(216, 21); + this.comBoxGuardianSlot2.TabIndex = 19; // // LoadoutEditorForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(240, 315); - this.Controls.Add(this.cmbHero); + this.ClientSize = new System.Drawing.Size(576, 356); + this.Controls.Add(this.groupBoxGuardians); + this.Controls.Add(this.groupBoxLoadout); + this.Controls.Add(this.comBoxHero); this.Controls.Add(this.btnSave); - this.Controls.Add(this.comBoxLoadoutSlot9); - this.Controls.Add(this.comBoxLoadoutSlot8); - this.Controls.Add(this.comBoxLoadoutSlot7); - this.Controls.Add(this.comBoxLoadoutSlot6); - this.Controls.Add(this.comBoxLoadoutSlot5); - this.Controls.Add(this.comBoxLoadoutSlot4); - this.Controls.Add(this.comBoxLoadoutSlot3); - this.Controls.Add(this.comBoxLoadoutSlot2); - this.Controls.Add(this.comBoxLoadoutSlot1); this.Name = "LoadoutEditorForm"; this.Text = "Loadout Editor"; this.Load += new System.EventHandler(this.LoadoutEditor_Load); + this.groupBoxLoadout.ResumeLayout(false); + this.groupBoxLoadout.PerformLayout(); + this.groupBoxGuardians.ResumeLayout(false); + this.groupBoxGuardians.PerformLayout(); this.ResumeLayout(false); } @@ -178,6 +349,21 @@ private void InitializeComponent() private System.Windows.Forms.ComboBox comBoxLoadoutSlot8; private System.Windows.Forms.ComboBox comBoxLoadoutSlot9; private System.Windows.Forms.Button btnSave; - private System.Windows.Forms.ComboBox cmbHero; + private System.Windows.Forms.ComboBox comBoxHero; + private System.Windows.Forms.GroupBox groupBoxLoadout; + private System.Windows.Forms.Label labelLoadoutSlot9; + private System.Windows.Forms.Label labelLoadoutSlot8; + private System.Windows.Forms.Label labelLoadoutSlot7; + private System.Windows.Forms.Label labelLoadoutSlot6; + private System.Windows.Forms.Label labelLoadoutSlot5; + private System.Windows.Forms.Label labelLoadoutSlot4; + private System.Windows.Forms.Label labelLoadoutSlot3; + private System.Windows.Forms.Label labelLoadoutSlot2; + private System.Windows.Forms.Label labelLoadoutSlot1; + private System.Windows.Forms.GroupBox groupBoxGuardians; + private System.Windows.Forms.Label labelGuardianSlot1; + private System.Windows.Forms.ComboBox comBoxGuardianSlot1; + private System.Windows.Forms.Label labelGuardianSlot2; + private System.Windows.Forms.ComboBox comBoxGuardianSlot2; } } \ No newline at end of file diff --git a/SingleplayerLauncher/LoadoutEditorForm.cs b/SingleplayerLauncher/LoadoutEditorForm.cs index 5cf2677..a10f45b 100644 --- a/SingleplayerLauncher/LoadoutEditorForm.cs +++ b/SingleplayerLauncher/LoadoutEditorForm.cs @@ -1,78 +1,123 @@ -using System; +using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; +using System.Linq; using System.Windows.Forms; namespace SingleplayerLauncher { public partial class LoadoutEditorForm : Form { - readonly List comBoxLoadoutSlots; + readonly List comBoxLoadoutSlots; + readonly List comBoxGuardianSlots; public static List bytes = new List(); private readonly Hero hero = Hero.Instance; - private const int loadoutSize = 9; + private const int nLoadoutSlots = 9; + private const int nGuardianSlots = 2; + + //TODO move defaults to a resource file + readonly string[] defaultLoadout = + { + "Mending Root", "Mage's Clover", "Barricade", + "Viscous Tar", "Flip Trap", "Wall Blades", + "Arrow Wall", "Concussive Pounder", "Ceiling Ballista" + }; + + readonly string[] defaultGuardians = + { + "Dragon Guardian", "Serpent Guardian" + }; public LoadoutEditorForm() { InitializeComponent(); - comBoxLoadoutSlots = new List() - { comBoxLoadoutSlot1, comBoxLoadoutSlot2, comBoxLoadoutSlot3, + comBoxLoadoutSlots = new List() + { + comBoxLoadoutSlot1, comBoxLoadoutSlot2, comBoxLoadoutSlot3, comBoxLoadoutSlot4, comBoxLoadoutSlot5, comBoxLoadoutSlot6, comBoxLoadoutSlot7, comBoxLoadoutSlot8, comBoxLoadoutSlot9 }; + + comBoxGuardianSlots = new List() + { + comBoxGuardianSlot1, comBoxGuardianSlot2 + }; } private void LoadoutEditor_Load(object sender, EventArgs e) { - PopulateTrapsGearSlots(); + PopulateSlots(this.comBoxLoadoutSlots, Resources.traps.Keys.ToList()); + PopulateSlots(this.comBoxLoadoutSlots, Resources.gear.Keys.ToList()); + PopulateSlots(this.comBoxGuardianSlots, Resources.guardians.Keys.ToList()); // TODO implement a way of loading previous loadout used // Placeholder -> Default loadout - SetDefaultLoadoutInForm(); + SetDefaultSlots(this.comBoxLoadoutSlots, this.defaultLoadout); + SetDefaultSlots(this.comBoxGuardianSlots, this.defaultGuardians); } private void btnSave_Click(object sender, EventArgs e) { - hero.loadout = CreateLoadout(); - + SetDefaultLoadoutInForm(); + Settings.Instance["loadout"] = hero.Loadout; + Settings.Save(); this.Close(); } private void SetDefaultLoadoutInForm() - { + { + hero.Loadout = SaveLoadout(); + hero.Guardians = SaveGuardians(); - for (int i = 0; i < 9; i++) - { - comBoxLoadoutSlots[i].SelectedItem = Resources.defaultLoadout[i]; - } + this.Close(); } - private void PopulateTrapsGearSlots() + private void PopulateSlots(List comBoxSlotList, List entryList) { - foreach (var comBoxLoadoutSlot in comBoxLoadoutSlots) + foreach (ComboBox comBoxSlot in comBoxSlotList) { - foreach (var trap in Resources.traps) + foreach (string entry in entryList) { - comBoxLoadoutSlot.Items.Add(trap.Key); - } - foreach (var gear in Resources.gear) - { - comBoxLoadoutSlot.Items.Add(gear.Key); + comBoxSlot.Items.Add(entry); } } } - private string[] CreateLoadout() + private void SetDefaultSlots(List comBoxSlotList, string[] defaultEntryList) + { + int i = 0; + foreach (ComboBox comBoxSlot in comBoxSlotList) + { + comBoxSlot.SelectedItem = defaultEntryList[i]; + i++; + } + } + + private string[] SaveLoadout() { - string[] loadout = new string[loadoutSize]; + string[] loadout = new string[nLoadoutSlots]; - for (int i = 0; i < 9; i++) + for (int i = 0; i < nLoadoutSlots; i++) { loadout[i] = comBoxLoadoutSlots[i].Text; } return loadout; } + + private string[] SaveGuardians() + { + string[] guardians = new string[nGuardianSlots]; + + for (int i = 0; i < nGuardianSlots; i++) + { + guardians[i] = comBoxGuardianSlots[i].Text; + } + + return guardians; + } } + } diff --git a/SingleplayerLauncher/Resources.cs b/SingleplayerLauncher/Resources.cs index abeebd4..69b896c 100644 --- a/SingleplayerLauncher/Resources.cs +++ b/SingleplayerLauncher/Resources.cs @@ -2,6 +2,25 @@ namespace SingleplayerLauncher { + public class Tuple + { + public T1 First { get; private set; } + public T2 Second { get; private set; } + internal Tuple(T1 first, T2 second) + { + First = first; + Second = second; + } + } + + public static class Tuple + { + public static Tuple New(T1 first, T2 second) + { + var tuple = new Tuple(first, second); + return tuple; + } + } internal static class Resources { @@ -87,6 +106,105 @@ internal static class Resources { "AntiTrap Vambrace", new byte[] { 0x02, 0xC4, 0x00, 0x00 } } }; + // Guardians (Name (Hex String code, Length in Hex)) + public static Dictionary> guardians = new Dictionary> + { + { "Bartender Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x62, 0x61, 0x72, 0x6d, 0x61, 0x69, 0x64, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x42, 0x61, 0x72, 0x6d, 0x61, 0x69, 0x64, 0x00 }, 47 + ) + }, + { "Blacksmith Guardian",new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x42, 0x65, 0x61, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x42, 0x65, 0x61, 0x72, 0x00 }, 33 + ) + }, + { "Cook Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x43, 0x6f, 0x6f, 0x6b, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x43, 0x6f, 0x6f, 0x6b, 0x00 }, 33 + ) + }, + { "Deckhand Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x64, 0x65, 0x63, 0x6b, 0x68, 0x61, 0x6e, 0x64, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x44, 0x65, 0x63, 0x6b, 0x68, 0x61, 0x6e, 0x64, 0x00 }, 49 + ) + }, + { "Dragon Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x44, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x44, 0x72, 0x61, 0x67, 0x6f, 0x6e, 0x00 }, 37 + ) + }, + { "Friar Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x6f, 0x72, 0x63, 0x70, 0x72, 0x69, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4f, 0x72, 0x63, 0x50, 0x72, 0x69, 0x65, 0x73, 0x74, 0x00 }, 51 + ) + }, + { "Headhunter Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x54, 0x72, 0x6f, 0x6c, 0x6c, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x65, 0x72, 0x00 }, 61 + ) + }, + { "Jade Empire", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x6a, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x43, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x65, 0x4a, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x00 }, 59 + ) + }, + { "Jailer Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4a, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4a, 0x61, 0x69, 0x6c, 0x65, 0x72, 0x00 }, 45 + ) + }, + { "Lion Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4c, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x4c, 0x69, 0x6f, 0x6e, 0x00 }, 33 + ) + }, + { "Moon Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x4d, 0x6f, 0x6f, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x4d, 0x6f, 0x6f, 0x6e, 0x00 }, 33 + ) + }, + { "Priest Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x50, 0x72, 0x69, 0x65, 0x73, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x50, 0x72, 0x69, 0x65, 0x73, 0x74, 0x00 }, 45 + ) + }, + { "Quartermaster Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x63, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x43, 0x61, 0x70, 0x74, 0x61, 0x69, 0x6e, 0x00 }, 47 + ) + }, + { "Ranch Hand Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x68, 0x61, 0x6e, 0x64, 0x6d, 0x69, 0x6e, 0x6f, 0x74, 0x61, 0x75, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x68, 0x61, 0x6e, 0x64, 0x4d, 0x69, 0x6e, 0x6f, 0x74, 0x61, 0x75, 0x72, 0x00 }, 69 + ) + }, + { "Rumrudder Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x63, 0x75, 0x72, 0x76, 0x79, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x63, 0x75, 0x72, 0x76, 0x79, 0x00 }, 45 + ) + }, + { "Serpent Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x52, 0x61, 0x7a, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x52, 0x61, 0x7a, 0x65, 0x72, 0x00 }, 35 + ) + }, + { "Stablehand Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x68, 0x61, 0x6e, 0x64, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x68, 0x61, 0x6e, 0x64, 0x00 }, 53 + ) + }, + { "Sun Guardian", new Tuple + ( + new byte [] { 0x50, 0x61, 0x77, 0x6e, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x53, 0x75, 0x6e, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x53, 0x75, 0x6e, 0x00 }, 31 + ) + }, + { "WeaponWright Guardian", new Tuple + ( + new byte [] { 0x70, 0x61, 0x77, 0x6e, 0x5f, 0x67, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x77, 0x65, 0x61, 0x70, 0x6f, 0x6e, 0x77, 0x72, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x50, 0x6c, 0x61, 0x63, 0x65, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x47, 0x75, 0x61, 0x72, 0x64, 0x69, 0x61, 0x6e, 0x57, 0x65, 0x61, 0x70, 0x6f, 0x6e, 0x77, 0x72, 0x69, 0x67, 0x68, 0x74, 0x00 }, 57 + ) + } + }; // Initialize usable variables within CharactersData.ini diff --git a/SingleplayerLauncher/Settings.cs b/SingleplayerLauncher/Settings.cs index 016e125..c008d97 100644 --- a/SingleplayerLauncher/Settings.cs +++ b/SingleplayerLauncher/Settings.cs @@ -34,6 +34,7 @@ public static void Load() public static void Save() { File.WriteAllText(SettingsFile, JsonConvert.SerializeObject(Instance)); + Load(); } } } diff --git a/SingleplayerLauncher/UPKFile.cs b/SingleplayerLauncher/UPKFile.cs index fde7e3f..747f60f 100644 --- a/SingleplayerLauncher/UPKFile.cs +++ b/SingleplayerLauncher/UPKFile.cs @@ -11,12 +11,19 @@ public class UPKFile private byte[] bytes; private string filePath; private int fileLength = 0; + public int nBytesRemoved = 0; + /// + /// + /// Initializes a new instance of the class. + /// + /// + /// The file path. public UPKFile(string path) { filePath = path; bytes = ReadAllBytes(path); - } + } protected byte[] Bytes { @@ -32,7 +39,22 @@ protected byte[] Bytes } } + private byte[] ReadAllBytes(string fileName) + { + byte[] buffer = null; + using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) + { + buffer = new byte[fs.Length]; + fs.Read(buffer, 0, (int)fs.Length); + } + fileLength = buffer.Length; + return buffer; + } + + /// Creates a zeroed byte array. (0x00 value) + /// The length of the array to create. + /// Created Array with bytes of value 0x00. public byte[] CreateZeroedByteArray(int length) { byte[] arr = new byte[length]; @@ -43,20 +65,44 @@ public byte[] CreateZeroedByteArray(int length) return arr; } + /// + /// Saves the file to disk. + /// public void Save() { File.WriteAllBytes(filePath, Bytes); + } + + public byte getByte(int index) + { + return Bytes[index]; } - public void RemoveBytes(int startPosition, int length) + /// + /// Overrides the bytes with the ones given starting in the given position (included). + /// + public void OverrideBytes(byte[] bytesToWrite, int startPosition) { - var tmpBytes = new List(Bytes); - tmpBytes.RemoveRange(startPosition, length); - Bytes = tmpBytes.ToArray(); + int length = bytesToWrite.Length; + + for(int i = 0; i < length; i++) + { + Bytes[startPosition + i] = bytesToWrite[i]; + } + } + + /// + /// Overrides the byte with the one given at the given index. + /// + /// Byte to set. + /// Index of the byte to override. + public void OverrideSingleByte(byte b, int index) + { + Bytes[index] = b; } /// - /// Overrides the bytes with the ones given starting in the first of the bytes to find. + /// Overrides the bytes with the ones given starting in the first index of the bytes to find. /// public void FindAndOverrideBytes(byte[] bytesToWrite, byte[] bytesToFind, int startSearchPosition) { @@ -66,24 +112,35 @@ public void FindAndOverrideBytes(byte[] bytesToWrite, byte[] bytesToFind, int st } /// - /// Overrides the bytes with the ones given starting in given position (included). + /// Removes the bytes in the given index. /// - public void OverrideBytes(byte[] bytesToWrite, int startPosition) + /// Index to where start removing. + /// Number of bytes to remove. + public void RemoveBytes(int index, int numberBytes) { - int length = bytesToWrite.Length; + var tmpBytes = new List(Bytes); + tmpBytes.RemoveRange(index, numberBytes); + Bytes = tmpBytes.ToArray(); - for(int i = 0; i < length; i++) - { - Bytes[startPosition + i] = bytesToWrite[i]; - } + nBytesRemoved += numberBytes; } /// - /// Overrides the byte with the one given at the given position. + /// Inserts a number of 0x00 bytes at the given index. /// - public void OverrideSingleByte(byte b, int position) + /// Index to where start inserting. + /// Number of 00x0 bytes to insert. + /// (Optional) Default value is the number of bytes removed, the difference in size from the original version + public void InsertZeroedBytes(int index, int numberBytes) { - Bytes[position] = b; + if (numberBytes <= 0) + { + numberBytes = nBytesRemoved; + } + + byte[] zeroedBytes = CreateZeroedByteArray(numberBytes); + + InsertBytes(zeroedBytes, index); } // TODO use a better way to "insert" bytes (like shifting positions) @@ -95,23 +152,18 @@ public void InsertBytes(byte[] bytesToInsert, int position) var tmpBytes = new List(Bytes); tmpBytes.InsertRange(position, bytesToInsert); Bytes = tmpBytes.ToArray(); - } - - /// - /// Inserts a number of 0x00 bytes at the given position. - /// - public void InsertZeroedBytes(int position, int numberBytes) - { - byte[] zeroedBytes = CreateZeroedByteArray(numberBytes); - InsertBytes(zeroedBytes, position); + nBytesRemoved -= bytesToInsert.Length; } /// - /// Finds the position of the given array in the UPK file. - /// + /// Finds the position of the given array in the UPK file. Uses Naive method. + /// /// Returns the index of the first position of the array occurrence. + /// /// + /// Array of bytes to find. + /// Offset index of where to start the search. public int FindBytesNaive(byte[] needle, int start = 0) { byte[] haystack = bytes; @@ -147,22 +199,22 @@ protected bool Match(byte[] needle, int start) } } - private byte[] ReadAllBytes(string fileName) + /// + /// Finds the position of the given array in the UPK file. Uses Knuth-Morris Patt (KMP) method. + /// Returns the index of the first position of the array occurrence or -1 if not found. + /// + /// Array of bytes to find. + /// Offset index of where to start the search. + /// Number of bytes to search from the start index. + public int FindBytesKMP(byte[] pattern, int start = 0, int length = 0) { - byte[] buffer = null; - using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) + int M = pattern.Length; + int N = bytes.Length; + + if (length > 0) { - buffer = new byte[fs.Length]; - fs.Read(buffer, 0, (int)fs.Length); + N = length + start; } - fileLength = buffer.Length; - return buffer; - } - - public int FindBytesKMP(byte[] pat, int start = 0) - { - int M = pat.Length; - int N = bytes.Length; // create lps[] that will hold the longest // prefix suffix values for pattern @@ -171,12 +223,12 @@ public int FindBytesKMP(byte[] pat, int start = 0) // Preprocess the pattern (calculate lps[] // array) - ComputeLPSArray(pat, M, lps); + ComputeLPSArray(pattern, M, lps); int i = start; // index for txt[] while (i < N) { - if (pat[j] == bytes[i]) + if (pattern[j] == bytes[i]) { j++; i++; @@ -186,8 +238,8 @@ public int FindBytesKMP(byte[] pat, int start = 0) return i - j; } - // mismatch after j matches - else if (i < N && pat[j] != bytes[i]) + // missmatch after j matches + else if (i < N && pattern[j] != bytes[i]) { // Do not match lps[0..lps[j-1]] characters, // they will match anyway