diff --git a/LibW4M/W4SaveFile.cs b/LibW4M/W4SaveFile.cs index e1bd4ba..1b3832c 100644 --- a/LibW4M/W4SaveFile.cs +++ b/LibW4M/W4SaveFile.cs @@ -174,11 +174,11 @@ namespace LibW4M } } - public void SaveXBOX(Stream xboxSaveStream) + public void SaveXBOX(Stream xboxSaveStream, XboxRegion region) { using (xboxSaveStream) { - XboxSave.CreateSaveFile(this, xboxSaveStream); + XboxSave.CreateSaveFile(this, xboxSaveStream, region); } } @@ -190,9 +190,9 @@ namespace LibW4M } } - public void SaveXBOX(string newXom) + public void SaveXBOX(string newXom, XboxRegion region) { - SaveXBOX(File.OpenWrite(newXom)); + SaveXBOX(File.OpenWrite(newXom), region); } public void SavePS2(string newXom) { diff --git a/LibW4M/XBOX/XboxRegion.cs b/LibW4M/XBOX/XboxRegion.cs new file mode 100644 index 0000000..3a163af --- /dev/null +++ b/LibW4M/XBOX/XboxRegion.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LibW4M.XBOX +{ + public enum XboxRegion : int + { + USA = 0, + EU = 1, + UNKNOWN = 2 + } +} diff --git a/LibW4M/XBOX/XboxSave.cs b/LibW4M/XBOX/XboxSave.cs index 18acba3..239037e 100644 --- a/LibW4M/XBOX/XboxSave.cs +++ b/LibW4M/XBOX/XboxSave.cs @@ -16,9 +16,18 @@ namespace LibW4M.XBOX private const int SHA1_SIZE = 0x14; private const int XBOX_MAX_SZ = 0x64000; - public static byte[] XboxSigningKey = new byte[0x40] { 0x20, 0x23, 0x8B, 0x22, 0xED, 0x13, 0xB7, 0xC1, 0x71, 0x5F, 0xC5, 0xB3, 0x72, 0x07, 0xC0, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + public static byte[] GetXboxSigningKeyForRegion(XboxRegion region) { + switch (region) + { + case XboxRegion.EU: + return new byte[0x10] { 0x78, 0x9F, 0x93, 0x67, 0xFD, 0xC8, 0x70, 0xAC, 0xCF, 0x9D, 0x46, 0xB0, 0x5E, 0x51, 0x36, 0x43 }; + case XboxRegion.USA: + default: + return new byte[0x10] { 0x20, 0x23, 0x8B, 0x22, 0xED, 0x13, 0xB7, 0xC1, 0x71, 0x5F, 0xC5, 0xB3, 0x72, 0x07, 0xC0, 0x24 }; - public static void CreateSaveFile(W4SaveFile save, Stream output) + } + } + public static void CreateSaveFile(W4SaveFile save, Stream output, XboxRegion region) { using(MemoryStream ms = new MemoryStream()) { @@ -32,7 +41,7 @@ namespace LibW4M.XBOX XomWriter.WriteXom(save.OriginalXom, ms); ms.Seek(0, SeekOrigin.Begin); - XboxSave.CalcAndWriteSignature(ms); + XboxSave.CalcAndWriteSignature(ms, region); ms.Seek(0, SeekOrigin.Begin); ms.CopyTo(output); @@ -47,14 +56,17 @@ namespace LibW4M.XBOX return outdata; } - private static byte[] calcSignature(byte[] data) + private static byte[] calcSignature(byte[] data, XboxRegion region) { using (SHA1 sha = SHA1.Create()) { using (MemoryStream ms = new MemoryStream()) { + byte[] signHeader = new byte[0x40]; + Array.Copy(GetXboxSigningKeyForRegion(region), signHeader, 0x10); + // hash save data - ms.Write(xor(XboxSigningKey, 0x36), 0, XboxSigningKey.Length); + ms.Write(xor(signHeader, 0x36), 0, signHeader.Length); ms.Write(data); ms.Seek(0, SeekOrigin.Begin); @@ -65,7 +77,7 @@ namespace LibW4M.XBOX ms.Seek(0, SeekOrigin.Begin); ms.SetLength(0); - ms.Write(xor(XboxSigningKey, 0x5C), 0, XboxSigningKey.Length); + ms.Write(xor(signHeader, 0x5C), 0, signHeader.Length); ms.Write(hash, 0, hash.Length); sha.Initialize(); @@ -74,21 +86,49 @@ namespace LibW4M.XBOX } } } - - public static W4SaveFile ReadXboxSave(string filepath) + private static MemoryStream readSaveData(Stream stream) { - using (FileStream fs = File.OpenRead(filepath)) + MemoryStream ms = new MemoryStream(); + stream.Seek(SHA1_SIZE, SeekOrigin.Begin); + stream.CopyTo(ms); + ms.Seek(0, SeekOrigin.Begin); + + stream.Seek(0, SeekOrigin.Begin); + return ms; + } + + public static XboxRegion DetermineRegion(Stream stream) + { + byte[] expectedSignature = new byte[SHA1_SIZE]; + stream.Read(expectedSignature, 0, SHA1_SIZE); + + byte[] saveData = new byte[XBOX_MAX_SZ]; + stream.Read(saveData, 0, XBOX_MAX_SZ); + + stream.Seek(0, SeekOrigin.Begin); + + byte[] usaSig = calcSignature(saveData, XboxRegion.USA); + byte[] eurSig = calcSignature(saveData, XboxRegion.EU); + + if (expectedSignature.SequenceEqual(usaSig)) return XboxRegion.USA; + else if (expectedSignature.SequenceEqual(eurSig)) return XboxRegion.EU; + else return XboxRegion.UNKNOWN; + + } + + public static W4SaveFile ReadXboxSave(Stream stream) + { + using (MemoryStream ms = readSaveData(stream)) { - fs.Seek(SHA1_SIZE, SeekOrigin.Begin); - return new W4SaveFile(XomReader.ReadXomFile(fs)); + return new W4SaveFile(XomReader.ReadXomFile(ms)); } } - public static void CalcAndWriteSignature(Stream s) + public static void CalcAndWriteSignature(Stream s, XboxRegion region) { byte[] data = new byte[XBOX_MAX_SZ]; s.Read(data, 0, XBOX_MAX_SZ); - byte[] signature = calcSignature(data); + byte[] signature = calcSignature(data, region); using(MemoryStream ms = new MemoryStream()) { ms.Write(signature, 0, SHA1_SIZE); diff --git a/W4Gui/DataManager.cs b/W4Gui/DataManager.cs index bacfd2e..54486e7 100644 --- a/W4Gui/DataManager.cs +++ b/W4Gui/DataManager.cs @@ -1,4 +1,5 @@ using LibW4M; +using LibW4M.XBOX; using System; using System.Collections.Generic; using System.Linq; diff --git a/W4Gui/Dialogs/AboutW4Gui.resx b/W4Gui/Dialogs/AboutW4Gui.resx index 4770e53..30210af 100644 --- a/W4Gui/Dialogs/AboutW4Gui.resx +++ b/W4Gui/Dialogs/AboutW4Gui.resx @@ -61,7 +61,7 @@ iVBORw0KGgoAAAANSUhEUgAAAHgAAAEGCAIAAAAhWcaAAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - wAAADsABataJCQAAZ9BJREFUeF7tvXuUXdV15nvqnH1eVSUhQIAeVVLpLUCgB3ohCRCWAIEFyEY22GDz + vAAADrwBlbxySQAAZ9BJREFUeF7tvXuUXdV15nvqnH1eVSUhQIAeVVLpLUCgB3ohCRCWAIEFyEY22GDz DGDAGPzENrbEGxsTYjsYY4zBBr9ix0+cOMFxnOAOTuwOSZzupOPb7U4naXdfd3f6dm5GMu4/fX9zfmvN s2qfkiA36XvHHfEenxZzffObc8019z777HOqkBqNG8cabxiz8QbHjaONN4wn26YFf+O4T92LBqbUMIZ4 xmnYimVRm3raAe/G9XmJxLPQuBeZxZYcWxkcyXZZ1CaSURuMqRm5+FRGdmm0nXp+BSKO2oAqkW3151GC @@ -527,6 +527,7 @@ shadowknight1620 - Reverse Engineering: Li + - Worms 4 Mayhem: Team17 Codemasters diff --git a/W4Gui/Main.cs b/W4Gui/Main.cs index f792691..0d50875 100644 --- a/W4Gui/Main.cs +++ b/W4Gui/Main.cs @@ -35,15 +35,17 @@ namespace W4Gui private void openToolStripMenuItem_Click(object sender, EventArgs e) { +#if !DEBUG try { +#endif OpenFileDialog fd = new OpenFileDialog(); fd.Filter = "Worms 4: Mayhem PC Save File|*.xom|Worms 4: Mayhem PS2 Save File|*.psu|Worms 4: Mayhem XBOX Save File|data"; fd.Title = "Open Worms 4 Save File"; if (fd.ShowDialog() == DialogResult.OK) { DataManager.LoadedSavePath = fd.FileName; - DataManager.LoadedSaveType = (SaveType)(fd.FilterIndex - 1); + DataManager.LoadedSaveType = (SaveType)(fd.FilterIndex); switch (DataManager.LoadedSaveType) { @@ -54,8 +56,17 @@ namespace W4Gui case SaveType.PS2: DataManager.SaveFile = Ps2Save.ReadPS2Save(fd.FileName); break; - case SaveType.XBOX: - DataManager.SaveFile = XboxSave.ReadXboxSave(fd.FileName); + case SaveType.XBOX_US: + case SaveType.XBOX_EU: + using(FileStream fs = File.OpenRead(fd.FileName)) + { + XboxRegion region = XboxSave.DetermineRegion(fs); + + if (region == XboxRegion.USA) DataManager.LoadedSaveType = SaveType.XBOX_US; + if (region == XboxRegion.EU) DataManager.LoadedSaveType = SaveType.XBOX_EU; + + DataManager.SaveFile = XboxSave.ReadXboxSave(fs); + } break; } @@ -69,18 +80,21 @@ namespace W4Gui DataManager.LoadAll(); } +#if !DEBUG } catch (Exception ex) { - MessageBox.Show("Failed to load save: " + ex.Message + "\nIt may be invalid or corrupt.", "Load failed!", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show("Failed to load save due to an error\n" + ex.Message + "\nThe file may be invalid or corrupt.", "Load failed!", MessageBoxButtons.OK, MessageBoxIcon.Error); } - +#endif } private void saveToolStripMenuItem_Click(object sender, EventArgs e) { +#if !DEBUG try { +#endif mainTabControl.Enabled = false; DataManager.SaveAll(); switch (DataManager.LoadedSaveType) @@ -91,17 +105,22 @@ namespace W4Gui case SaveType.PS2: DataManager.SaveFile.SavePS2PSU(DataManager.LoadedSavePath); break; - case SaveType.XBOX: - DataManager.SaveFile.SaveXBOX(DataManager.LoadedSavePath); + case SaveType.XBOX_US: + DataManager.SaveFile.SaveXBOX(DataManager.LoadedSavePath, XboxRegion.USA); + break; + case SaveType.XBOX_EU: + DataManager.SaveFile.SaveXBOX(DataManager.LoadedSavePath, XboxRegion.EU); break; } mainTabControl.Enabled = true; MessageBox.Show("File saved to: " + DataManager.LoadedSavePath, "Save Complete!", MessageBoxButtons.OK, MessageBoxIcon.Information); +#if !DEBUG } catch (Exception ex) { MessageBox.Show("Failed to save: " + ex.Message, "Save failed!", MessageBoxButtons.OK, MessageBoxIcon.Error); } +#endif } private void mainTabControl_SelectedIndexChanged(object sender, EventArgs e) { @@ -140,10 +159,12 @@ namespace W4Gui private void saveAsToolStripMenuItem_Click(object sender, EventArgs e) { +#if !DEBUG try { +#endif SaveFileDialog fd = new SaveFileDialog(); - fd.Filter = "Worms 4: Mayhem PC Save File|*.xom|Worms 4: Mayhem PS2 Single Save File|*.psu|Worms 4: Mayhem XBOX Save File|data"; + fd.Filter = "Worms 4: Mayhem PC Save File|*.xom|Worms 4: Mayhem PS2 Single Save File|*.psu|Worms 4: Mayhem XBOX United States Save File|data|Worms 4: Mayhem XBOX Europe/Austrailia Save File|data"; fd.Title = "Save Worms 4 Save File"; fd.FileName = Path.GetFileName(DataManager.LoadedSavePath); if (fd.ShowDialog() == DialogResult.OK) @@ -151,7 +172,7 @@ namespace W4Gui this.mainTabControl.Enabled = false; DataManager.SaveAll(); - SaveType saveType = (SaveType)(fd.FilterIndex - 1); + SaveType saveType = (SaveType)(fd.FilterIndex); switch (saveType) { @@ -161,8 +182,11 @@ namespace W4Gui case SaveType.PS2: DataManager.SaveFile.SavePS2PSU(fd.FileName); break; - case SaveType.XBOX: - DataManager.SaveFile.SaveXBOX(fd.FileName); + case SaveType.XBOX_US: + DataManager.SaveFile.SaveXBOX(fd.FileName, XboxRegion.USA); + break; + case SaveType.XBOX_EU: + DataManager.SaveFile.SaveXBOX(fd.FileName, XboxRegion.EU); break; } @@ -172,11 +196,13 @@ namespace W4Gui MessageBox.Show("File saved to: " + fd.FileName, "Save Complete!", MessageBoxButtons.OK, MessageBoxIcon.Information); this.mainTabControl.Enabled = true; } +#if !DEBUG } catch (Exception ex) { MessageBox.Show("Failed to save: " + ex.Message, "Save failed!", MessageBoxButtons.OK, MessageBoxIcon.Error); } +#endif } private void aboutProgramToolStripMenuItem_Click(object sender, EventArgs e) diff --git a/W4Gui/Properties/PublishProfiles/FolderProfile.pubxml.user b/W4Gui/Properties/PublishProfiles/FolderProfile.pubxml.user index 6ca7310..6801b15 100644 --- a/W4Gui/Properties/PublishProfiles/FolderProfile.pubxml.user +++ b/W4Gui/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -4,7 +4,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. --> - True|2023-03-04T16:20:37.5344385Z;False|2023-03-05T05:11:04.5753179+13:00;True|2023-03-04T13:33:03.9184567+13:00;True|2023-03-04T05:17:31.1862857+13:00;True|2023-03-04T04:40:06.7859106+13:00;True|2023-03-01T11:45:37.3479871+13:00;True|2023-03-01T09:19:31.4651141+13:00;True|2023-02-25T21:53:35.8769435+13:00;True|2023-02-20T23:18:32.2496478+13:00;True|2023-02-19T06:40:54.2502643+13:00;False|2023-02-16T17:57:46.9563146+13:00;True|2023-01-19T20:41:26.7371208+13:00;True|2023-01-15T17:06:35.5919106+13:00;True|2023-01-14T13:57:56.0824690+13:00;True|2023-01-11T22:22:28.8737310+13:00;True|2023-01-11T22:16:55.3469226+13:00; + True|2023-03-04T17:13:14.7341877Z;True|2023-03-05T05:34:42.0662279+13:00;True|2023-03-05T05:20:37.5344385+13:00;False|2023-03-05T05:11:04.5753179+13:00;True|2023-03-04T13:33:03.9184567+13:00;True|2023-03-04T05:17:31.1862857+13:00;True|2023-03-04T04:40:06.7859106+13:00;True|2023-03-01T11:45:37.3479871+13:00;True|2023-03-01T09:19:31.4651141+13:00;True|2023-02-25T21:53:35.8769435+13:00;True|2023-02-20T23:18:32.2496478+13:00;True|2023-02-19T06:40:54.2502643+13:00;False|2023-02-16T17:57:46.9563146+13:00;True|2023-01-19T20:41:26.7371208+13:00;True|2023-01-15T17:06:35.5919106+13:00;True|2023-01-14T13:57:56.0824690+13:00;True|2023-01-11T22:22:28.8737310+13:00;True|2023-01-11T22:16:55.3469226+13:00; \ No newline at end of file diff --git a/W4Gui/SaveType.cs b/W4Gui/SaveType.cs index 7b6e0ef..4239e1a 100644 --- a/W4Gui/SaveType.cs +++ b/W4Gui/SaveType.cs @@ -6,10 +6,11 @@ using System.Threading.Tasks; namespace W4Gui { - public enum SaveType + public enum SaveType : int { - PC, - PS2, - XBOX + PC = 1, + PS2 = 2, + XBOX_US = 3, + XBOX_EU = 4 } }