146 lines
4.7 KiB
C#
146 lines
4.7 KiB
C#
using LibW4M.Data.Stats;
|
|
using LibW4M.PS2;
|
|
using LibXom;
|
|
using LibXom.Streams;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace LibW4M.XBOX
|
|
{
|
|
public static class XboxSave
|
|
{
|
|
private const int SHA1_SIZE = 0x14;
|
|
private const int XBOX_MAX_SZ = 0x64000;
|
|
|
|
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, XboxRegion region)
|
|
{
|
|
using(MemoryStream ms = new MemoryStream())
|
|
{
|
|
// Remove all stats ...
|
|
foreach (StatsContainerData stat in save.StatsCollective.ToArray())
|
|
save.StatsCollective.Delete(stat);
|
|
|
|
// Save changes
|
|
save.saveData();
|
|
|
|
XomWriter.WriteXom(save.OriginalXom, ms);
|
|
ms.Seek(0, SeekOrigin.Begin);
|
|
|
|
XboxSave.CalcAndWriteSignature(ms, region);
|
|
|
|
ms.Seek(0, SeekOrigin.Begin);
|
|
ms.CopyTo(output);
|
|
}
|
|
}
|
|
|
|
private static byte[] xor(byte[] data, byte key)
|
|
{
|
|
byte[] outdata = new byte[data.Length];
|
|
for (int i = 0; i < data.Length; i++)
|
|
outdata[i] = Convert.ToByte(data[i] ^ key);
|
|
return outdata;
|
|
}
|
|
|
|
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(signHeader, 0x36), 0, signHeader.Length);
|
|
ms.Write(data);
|
|
|
|
ms.Seek(0, SeekOrigin.Begin);
|
|
|
|
byte[] hash = sha.ComputeHash(ms.ToArray());
|
|
|
|
// hash that hash
|
|
ms.Seek(0, SeekOrigin.Begin);
|
|
ms.SetLength(0);
|
|
|
|
ms.Write(xor(signHeader, 0x5C), 0, signHeader.Length);
|
|
ms.Write(hash, 0, hash.Length);
|
|
|
|
sha.Initialize();
|
|
|
|
return sha.ComputeHash(ms.ToArray());
|
|
}
|
|
}
|
|
}
|
|
private static MemoryStream readSaveData(Stream stream)
|
|
{
|
|
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))
|
|
{
|
|
return new W4SaveFile(XomReader.ReadXomFile(ms));
|
|
}
|
|
}
|
|
|
|
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, region);
|
|
using(MemoryStream ms = new MemoryStream())
|
|
{
|
|
ms.Write(signature, 0, SHA1_SIZE);
|
|
ms.Write(data, 0, XBOX_MAX_SZ);
|
|
|
|
ms.Seek(0, SeekOrigin.Begin);
|
|
s.Seek(0, SeekOrigin.Begin);
|
|
s.SetLength(ms.Length);
|
|
ms.CopyTo(s);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|