Worms4Editor/LibW4M/W4SaveFile.cs

381 lines
15 KiB
C#

using LibW4M.Data;
using LibW4M.Data.Awards;
using LibW4M.Data.Highscores;
using LibW4M.Data.InputMapping;
using LibW4M.Data.Schemes;
using LibW4M.Data.Stats;
using LibW4M.Data.Teams;
using LibW4M.Data.WeaponFactory;
using LibW4M.Data.WXFE;
using LibW4M.Data.WXFE.UnlockableItem;
using LibW4M.Data.X;
using LibW4M.PS2;
using LibW4M.XBOX;
using LibXom;
using LibXom.Blocks;
using LibXom.Data;
using LibXom.Exceptions;
using LibXom.Streams;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml.XPath;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace LibW4M
{
public class W4SaveFile
{
private List<UnlockableItemData> unlockableItems;
private XomFile xomFile;
public DataBank XDataBank;
public WeaponsCollective WeaponFactoryCollective;
public TeamsCollective TeamDataColective;
public SchemesCollective SchemesCollective;
public HighscoreCollective HighscoreCollective;
public StatsCollective StatsCollective;
public TeamStatsCollective TeamStatsCollective;
public InputMappingCollective InputMappingCollective;
public TeamAwardsData TeamAwardsContainer;
public XomString Message;
public XomString FEResourceID;
public XomFile OriginalXom
{
get
{
return this.xomFile;
}
}
public UnlockableItemData[] UnlockableItems
{
get
{
return unlockableItems.ToArray();
}
}
public void Load()
{
unlockableItems = new List<UnlockableItemData>();
XDataBank = new DataBank(this, this.xomFile.GetTypeByName("XDataBank").Containers.First());
foreach(ContainerResourceDetail containerResourceDetail in XDataBank.ContainerResourceDetails)
{
switch (containerResourceDetail.Name.Value)
{
case "DATA.Weapons":
WeaponFactoryCollective = new WeaponsCollective(this, containerResourceDetail.Value);
break;
case "DATA.TeamBarracks":
TeamDataColective = new TeamsCollective(this, containerResourceDetail.Value);
HighscoreCollective = new HighscoreCollective(this, containerResourceDetail.Value);
break;
case "DATA.Schemes":
SchemesCollective = new SchemesCollective(this, containerResourceDetail.Value);
break;
case "PersistStats":
StatsCollective = new StatsCollective(this, containerResourceDetail.Value);
TeamStatsCollective = new TeamStatsCollective(this, containerResourceDetail.Value);
break;
case "Awards":
TeamAwardsContainer = new TeamAwardsData(this, containerResourceDetail.Value);
break;
case "InputMapping":
InputMappingCollective = new InputMappingCollective(this, containerResourceDetail.Value);
break;
default:
// stupid hack; the only other option is manually hardcoding every unlockable item in the game
// but this way atleast allows for it to be more generic incase for some reason not all (or more) are present.
// Team17, why isnt there a collective for this?
if (containerResourceDetail.Value.Type.Name.Equals("WXFE_UnlockableItem", StringComparison.InvariantCultureIgnoreCase))
{
UnlockableItemData itemData = new UnlockableItemData(this, containerResourceDetail.Value);
itemData.nameOverride = containerResourceDetail.Name.Value;
unlockableItems.Add(itemData);
}
else
{
#if !DEBUG
throw new XomException("Unknown container resource: " + containerResourceDetail.Name);
#else
Console.Error.WriteLine("Unknown container resource: " + containerResourceDetail.Name);
#endif
}
break;
}
}
}
internal void saveData()
{
#if !DEBUG
xomFile.ClearAllStrings();
#endif
XDataBank.Save();
WeaponFactoryCollective.Save();
// Save Teams, Highscores
TeamDataColective.Save();
HighscoreCollective.Save();
SchemesCollective.Save();
// Save Stats, TeamStats
StatsCollective.Save();
TeamStatsCollective.Save();
TeamAwardsContainer.Save();
InputMappingCollective.Save();
foreach (UnlockableItemData unlockableItem in UnlockableItems)
unlockableItem.Save();
}
public void Save(Stream pcSaveStream)
{
saveData();
using (pcSaveStream)
{
using (MemoryStream ms = new MemoryStream())
{
XomWriter.WriteXom(xomFile, pcSaveStream);
ms.Seek(0, SeekOrigin.Begin);
ms.CopyTo(pcSaveStream);
}
}
}
public void ExtractAllContainers(string path)
{
foreach(XomType type in xomFile.XomTypes)
{
string outfolder = Path.Combine(path, type.Name);
if (Directory.Exists(outfolder))
Directory.Delete(outfolder, true);
Directory.CreateDirectory(outfolder);
foreach(XomContainer container in type.Containers)
{
while (true)
{
try
{
File.WriteAllBytes(Path.ChangeExtension(Path.Combine(outfolder, container.Id.ToString("X2")), ".bin"), container.GetData());
break;
}
catch
{
continue;
}
}
}
}
}
public XomString[] StringArrayToXomStringArray(string[] strings)
{
XomString[] xstrings = new XomString[strings.Length];
for (int i = 0; i < xstrings.Length; i++) xstrings[i] = LookupString(strings[i]);
return xstrings;
}
public string[] XomStringArrayToStringArray(XomString[] xstrings)
{
string[] strings = new string[xstrings.Length];
for (int i = 0; i < strings.Length; i++) strings[i] = xstrings[i].Value;
return strings;
}
public int[] XomStringArrayToIntArray(XomString[] strs)
{
int[] ids = new int[strs.Length];
for (int i = 0; i < ids.Length; i++) ids[i] = strs[i].Id;
return ids;
}
public XomString[] IntArrayToXomStringArray(int[] stringIds)
{
XomString[] strings = new XomString[stringIds.Length];
for (int i = 0; i < strings.Length; i++) strings[i] = this.LookupStringFromId(stringIds[i]);
return strings;
}
private static byte[] getXomTypeXBytes(string name)
{
switch (name)
{
case "WeaponFactoryContainer":
return new byte[0x10] { 0xA5, 0x36, 0x42, 0x20, 0xF4, 0x4D, 0x60, 0x4F, 0x90, 0x97, 0x4F, 0xA8, 0x87, 0xD5, 0xC7, 0x4D };
case "StoreWeaponFactory":
return new byte[0x10] { 0xB5, 0x2C, 0x2F, 0xE3, 0x3A, 0x13, 0x1C, 0x4F, 0xB6, 0x4A, 0x81, 0xB8, 0x97, 0x30, 0x2E, 0x82 };
case "WeaponFactoryCollective":
return new byte[0x10] { 0xB2, 0x4A, 0x97, 0x29, 0xF8, 0x2E, 0x3D, 0x4E, 0xB7, 0x98, 0xF5, 0x8D, 0x3F, 0x36, 0x07, 0xFF };
case "StoredTeamData":
return new byte[0x10] { 0x55, 0xB4, 0x6A, 0x78, 0x13, 0x63, 0xC6, 0x4D, 0xA1, 0xA3, 0xA5, 0x4B, 0x67, 0xD5, 0x2C, 0xDE };
case "HighScoreData":
return new byte[0x10] { 0xED, 0xB1, 0x58, 0x90, 0x67, 0x06, 0x31, 0x44, 0xBC, 0x6B, 0x7E, 0x56, 0x6C, 0x8C, 0xBC, 0xE4 };
case "TeamDataColective":
return new byte[0x10] { 0x39, 0x11, 0x7C, 0x55, 0xF9, 0xEA, 0xBF, 0x49, 0x99, 0x8D, 0x05, 0xD8, 0x52, 0x59, 0x86, 0x55 };
case "WeaponSettingsData":
return new byte[0x10] { 0x16, 0x2E, 0xAC, 0x14, 0x90, 0x16, 0x97, 0x43, 0xBA, 0x75, 0x20, 0x4D, 0xAC, 0x1C, 0x56, 0x3F };
case "SchemeData":
return new byte[0x10] { 0x6B, 0xDD, 0x69, 0x39, 0x1A, 0xAF, 0xBF, 0x40, 0xBF, 0x2C, 0x21, 0xE3, 0x7C, 0x4B, 0xFC, 0xFE };
case "SchemeColective":
return new byte[0x10] { 0x1E, 0x40, 0x13, 0xDF, 0xC6, 0x8F, 0x50, 0x44, 0x8C, 0x7F, 0x55, 0xC6, 0x72, 0xA7, 0x27, 0x25 };
case "WXFE_UnlockableItem":
return new byte[0x10] { 0x72, 0x12, 0xCB, 0x5B, 0x48, 0xBF, 0x9F, 0x4A, 0xA2, 0xD3, 0xD1, 0x21, 0xA9, 0x82, 0xD7, 0x3A };
case "InputDetailsContainer":
return new byte[0x10] { 0x66, 0xE0, 0x60, 0xA5, 0xAB, 0x92, 0xC2, 0x43, 0x8E, 0x71, 0x46, 0xE8, 0x34, 0xF5, 0xDF, 0x80 };
case "InputEventMappingContainer":
return new byte[0x10] { 0x30, 0x7B, 0x4E, 0x74, 0x00, 0xED, 0x46, 0x4C, 0xA4, 0x5D, 0x87, 0x0C, 0xD3, 0x52, 0x81, 0xD8 };
case "InputMappingDetails":
return new byte[0x10] { 0x7B, 0x26, 0x21, 0x39, 0xC6, 0x3E, 0x69, 0x4C, 0xB7, 0xDD, 0x19, 0x9A, 0xED, 0xEB, 0x71, 0x5C };
case "StatsContainer":
return new byte[0x10] { 0x13, 0x54, 0xBC, 0xDF, 0x36, 0x80, 0x71, 0x4F, 0x83, 0x71, 0x6F, 0x3B, 0x5D, 0x87, 0x63, 0xED };
case "StoredStatsCollective":
return new byte[0x10] { 0x25, 0x1D, 0x56, 0xF7, 0x90, 0xD3, 0xF9, 0x4B, 0xA7, 0xE3, 0xE6, 0x73, 0xD6, 0x75, 0xCE, 0x03 };
case "TeamAwardsContainer":
return new byte[0x10] { 0xBB, 0x77, 0x14, 0x26, 0x44, 0xC0, 0xAC, 0x4C, 0xA4, 0x18, 0x90, 0x8F, 0xE9, 0x07, 0x5C, 0xF8 };
case "XResourceDetails":
return new byte[0x10] { 0xF2, 0x56, 0x75, 0xE7, 0x95, 0xA4, 0x1A, 0x49, 0x85, 0x63, 0x00, 0x6C, 0xE2, 0x57, 0x72, 0x44 };
case "XIntResourceDetails":
return new byte[0x10] { 0x8D, 0x9B, 0x88, 0x7D, 0x14, 0x2C, 0x5E, 0x44, 0x96, 0x1F, 0x5E, 0x0E, 0x55, 0x20, 0x41, 0x6A };
case "XUintResourceDetails":
return new byte[0x10] { 0x7C, 0x11, 0xE5, 0x93, 0xDA, 0x8A, 0xFB, 0x4A, 0xB1, 0x7A, 0x66, 0x8D, 0x0B, 0xDD, 0x15, 0xE6 };
case "XStringResourceDetails":
return new byte[0x10] { 0x72, 0x58, 0xDC, 0x7C, 0xA3, 0x2C, 0xCC, 0x4F, 0xAE, 0x66, 0x43, 0x10, 0x71, 0xE5, 0xCE, 0x67 };
case "XFloatResourceDetails":
return new byte[0x10] { 0x5F, 0x9D, 0x88, 0x08, 0x39, 0x80, 0xEA, 0x4C, 0xBA, 0x5B, 0x9C, 0x05, 0x9A, 0xC0, 0x57, 0x0E };
case "XVectorResourceDetails":
return new byte[0x10] { 0x63, 0x45, 0x1F, 0x07, 0xB3, 0x8C, 0x96, 0x49, 0xB7, 0x9F, 0xB0, 0xF5, 0xCA, 0xEC, 0x9D, 0x99 };
case "XContainerResourceDetails":
return new byte[0x10] { 0x20, 0xBF, 0xB6, 0xE0, 0x0B, 0xF8, 0x29, 0x43, 0xB6, 0xEB, 0x19, 0x98, 0xED, 0x8A, 0xEC, 0x1F };
case "XDataBank":
return new byte[0x10] { 0xAC, 0x61, 0xE6, 0x93, 0x41, 0xD9, 0x2A, 0x44, 0xA1, 0x8E, 0xE9, 0x9B, 0x79, 0x58, 0x79, 0xDC };
case "XContainer":
return new byte[0x10] { 0x46, 0xD4, 0x1C, 0x5E, 0xA3, 0x48, 0xFE, 0x44, 0xA5, 0x5A, 0xE2, 0x47, 0xB8, 0xF5, 0xE7, 0x13 };
default:
return new byte[0x10];
}
}
private static int findInStringArray(string str, string[] arr)
{
for(int i = 0; i < arr.Length; i++)
{
if (str.Equals(arr[i], StringComparison.InvariantCulture)) return i;
}
return -1;
}
private int getXomTypeInsertPos(string typeName)
{
XomType[] types = xomFile.XomTypes;
string[] typesOrder = new string[] {
"WeaponFactoryContainer",
"StoreWeaponFactory",
"WeaponFactoryCollective",
"StoredTeamData",
"HighScoreData",
"TeamDataColective",
"WeaponSettingsData",
"SchemeData",
"SchemeColective",
"WXFE_UnlockableItem",
"InputDetailsContainer",
"InputEventMappingContainer",
"InputMappingDetails",
"StatsContainer",
"StoredStatsCollective",
"TeamAwardsContainer",
"XResourceDetails",
"XIntResourceDetails",
"XUintResourceDetails",
"XStringResourceDetails",
"XFloatResourceDetails",
"XVectorResourceDetails",
"XContainerResourceDetails",
"XDataBank",
"XContainer"
};
int loc = findInStringArray(typeName, typesOrder);
string[] strs = new string[typesOrder.Length];
for (int i = 0; i < types.Length; i++)
{
int f = findInStringArray(types[i].Name, typesOrder);
strs[f] = types[i].Name;
}
strs[loc] = typeName;
int ins = 0;
foreach(string str in strs)
{
if (str == null)
continue;
if (str == typeName)
break;
ins++;
}
return ins - 1;
}
public void CreateXomType(string typeName)
{
byte[] xbytes = getXomTypeXBytes(typeName);
int insertInto = getXomTypeInsertPos(typeName);
xomFile.CreateXomType(xbytes, typeName, insertInto);
}
public XomContainer CreateContainer(string type)
{
try
{
return xomFile.CreateContainer(type);
}
catch(XomTypeNotFoundException) { this.CreateXomType(type); return this.CreateContainer(type); }
}
public XomContainer LookupContainerById(int id)
{
return xomFile.GetContainerById(id);
}
public XomContainer LookupContainerByUuid(string uuid)
{
return xomFile.GetContainerByUuid(uuid);
}
public XomString LookupStringFromId(int id)
{
return xomFile.GetStringById(id);
}
public XomString LookupString(string value)
{
return xomFile.AddOrGetString(value);
}
public W4SaveFile(XomFile w4Save)
{
this.xomFile = w4Save;
Load();
}
}
}