Make build .psu file!

This commit is contained in:
Li 2023-03-04 04:59:36 +13:00
parent 94baeb74d8
commit 0fbf9e96c9
25 changed files with 829 additions and 145 deletions

View File

@ -10,11 +10,10 @@ namespace LibW4M.Data.Stats
{
public class TeamStatsCollective : SaveDataCollective
{
public List<XomString> TeamNames;
public List<XomString> TeamNames = new List<XomString>();
public TeamStatsCollective(W4SaveFile fileBelongs, XomContainer mainContainer) : base(fileBelongs, mainContainer)
{
TeamNames = new List<XomString>();
}
public override void Load()
@ -44,8 +43,6 @@ namespace LibW4M.Data.Stats
this.collectiveEntries.Add(stats);
}
}
public override void Save()
@ -84,7 +81,7 @@ namespace LibW4M.Data.Stats
// add team names too
newCollective[teamStatsStart] = teamNamesLen;
newCollective[teamNamesStart] = teamNamesLen;
for (int i = 0; i < teamNamesLen; i++)
newCollective[teamNamesStart + i + 1] = this.TeamNames[i].Id;

View File

@ -21,33 +21,10 @@ namespace LibW4M.Data.Teams
}
private int weaponContainerId;
private int clusterContainerId;
public WeaponData SecretWeapon
{
get
{
return fileBelongs.findWeaponWithContainerId(weaponContainerId);
}
set
{
weaponContainerId = value.containerId;
}
}
public WeaponData SecretWeaponCluster
{
get
{
return fileBelongs.findWeaponWithContainerId(clusterContainerId);
}
set
{
clusterContainerId = value.containerId;
}
}
public WeaponData SecretWeapon;
public WeaponData SecretWeaponCluster;
public XomString Name;
public XomString[] Worms;
public int TutorialsDone;
@ -92,8 +69,8 @@ namespace LibW4M.Data.Teams
this.AllMissionsDone = reader.ReadBool();
this.Player = fileBelongs.LookupStringFromId(reader.ReadCompressedInt());
this.weaponContainerId = reader.ReadCompressedInt();
this.clusterContainerId = reader.ReadCompressedInt();
this.SecretWeapon = fileBelongs.findWeaponWithContainerId(reader.ReadCompressedInt());
this.SecretWeaponCluster = fileBelongs.findWeaponWithContainerId(reader.ReadCompressedInt());
this.CustomWeapon = fileBelongs.LookupStringFromId(reader.ReadCompressedInt());

View File

@ -11,4 +11,28 @@
<ProjectReference Include="..\LibXom\LibXom.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="PS2\PS2SaveResources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>PS2SaveResources.resx</DependentUpon>
</Compile>
<Compile Update="PS2\SaveResources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>SaveResources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="PS2\PS2SaveResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>PS2SaveResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="PS2\SaveResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>SaveResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

72
LibW4M/PS2/Ps2Save.cs Normal file
View File

@ -0,0 +1,72 @@
using LibXom.Data;
using LibXom.Streams;
using LibXom;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LibW4M.Data.Stats;
namespace LibW4M.PS2
{
public static class Ps2Save
{
private const int PS2_MAX_SZ = 0x20000;
public static void CreateSaveFile(W4SaveFile save, Stream output)
{
// Remove all stats ...
foreach (StatsContainerData stat in save.StatsCollective.ToArray())
save.StatsCollective.Delete(stat);
// Save changes
save.saveData();
XomWriter.WriteXom(save.OriginalXom,output);
Ps2Save.CalcAndWriteChecksum(output);
}
public static void CalcAndWriteChecksum(Stream s)
{
XomStreamReader reader = new XomStreamReader(s);
XomStreamWriter writer = new XomStreamWriter(s);
// Set size to ps2 save size.
s.SetLength(PS2_MAX_SZ);
s.Seek(0, SeekOrigin.Begin);
// Calculate checksum
Int64 chk = 0;
for (int i = 0; i < (PS2_MAX_SZ - 4); i += 4)
{
int c = reader.ReadInt32();
chk = (chk + c) & UInt32.MaxValue;
}
writer.WriteUInt32(Convert.ToUInt32(chk));
s.Seek(0, SeekOrigin.Begin);
}
public static void CreatePSU(W4SaveFile save, Stream output)
{
PsuFile psuFile = new PsuFile();
psuFile.InitalizePSU("BESLES-53096W4MA");
psuFile.CreateFile("Icon1.ico", SaveResources.IconFile);
psuFile.CreateFile("icon.sys", SaveResources.IconSys);
using(MemoryStream ms = new MemoryStream())
{
CreateSaveFile(save, ms);
ms.Seek(0, SeekOrigin.Begin);
psuFile.CreateFile("BESLES-53096W4MA", ms.ToArray());
}
psuFile.WritePSU(output);
}
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
namespace LibW4M.PS2
{
public enum PsuEntryType : ushort
{
DIRECTORY_ENTRY = 0x8427,
FILE_ENTRY = 0x8497,
SYSTEM_ENTRY = 0xA027
}
}

176
LibW4M/PS2/PsuFile.cs Normal file
View File

@ -0,0 +1,176 @@
using LibXom.Streams;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Threading.Tasks;
namespace LibW4M.PS2
{
public class PsuFile
{
public PsuFile()
{
entries = new List<PsuFileEntry>();
}
private List<PsuFileEntry> entries;
public PsuFileEntry[] Entires
{
get
{
return entries.ToArray();
}
}
public PsuFileEntry[] Files
{
get
{
List<PsuFileEntry> tmpList = new List<PsuFileEntry>();
foreach (PsuFileEntry entry in entries)
{
if (entry.EntryType == PsuEntryType.FILE_ENTRY || entry.EntryType == PsuEntryType.SYSTEM_ENTRY)
{
tmpList.Add(entry);
}
}
return tmpList.ToArray();
}
}
public PsuFileEntry[] Directorys
{
get
{
List<PsuFileEntry> tmpList = new List<PsuFileEntry>();
foreach (PsuFileEntry entry in entries)
{
if (entry.EntryType == PsuEntryType.DIRECTORY_ENTRY)
{
tmpList.Add(entry);
}
}
return tmpList.ToArray();
}
}
public void CreateDirectory(string directoryName)
{
this.entries.Add(new PsuFileEntry(PsuEntryType.DIRECTORY_ENTRY, 0x00, DateTime.Now, 0, DateTime.Now, directoryName, null));
this.entries[0].directoryEntries = this.entries.Count - 1;
}
public void CreateFile(string fileName, byte[] data)
{
this.entries.Add(new PsuFileEntry(PsuEntryType.FILE_ENTRY, data.Length, DateTime.Now, 0, DateTime.Now, fileName, data));
this.entries[0].directoryEntries = this.entries.Count - 1;
}
public void CreateSystemFile(string fileName, byte[] data)
{
this.entries.Add(new PsuFileEntry(PsuEntryType.SYSTEM_ENTRY, data.Length, DateTime.Now, 0, DateTime.Now, fileName, data));
this.entries[0].directoryEntries = this.entries.Count - 1;
}
public void InitalizePSU(string titleId)
{
this.entries.Clear();
CreateDirectory(titleId);
CreateDirectory(".");
CreateDirectory("..");
}
public PsuFileEntry GetFileByName(string filename)
{
foreach(PsuFileEntry entry in this.Files)
{
if(entry.Filename.Equals(filename, StringComparison.InvariantCulture))
{
return entry;
}
}
throw new FileNotFoundException("Cannot find the file");
}
public static PsuFile ReadPSU(string psuFilePath)
{
PsuFile psuFile = new PsuFile();
psuFile.ReadPSU(File.OpenRead(psuFilePath));
return psuFile;
}
public void WritePSU(Stream psuStream)
{
using (XomStreamWriter writer = new XomStreamWriter(psuStream))
{
foreach (PsuFileEntry fileEntry in this.Entires)
{
writer.WriteUInt16((ushort)fileEntry.EntryType);
writer.Skip(0x02);
writer.WriteInt32(fileEntry.Filesize);
writer.Skip(0x01);
writer.WriteDateTime(fileEntry.CreationDate);
writer.WriteUInt16(fileEntry.Sector);
writer.Skip(0x07);
writer.WriteDateTime(fileEntry.ModifiedDate);
writer.Skip(0x20);
writer.WriteStrLen(fileEntry.Filename, 0x1C0);
if(fileEntry.FileData != Stream.Null)
{
fileEntry.FileData.Seek(0, SeekOrigin.Begin);
fileEntry.FileData.CopyTo(psuStream);
writer.WritePadding(0xFF, 0x400 - (fileEntry.Filesize % 0x400));
}
}
}
}
public void ReadPSU(Stream psuStream)
{
using (XomStreamReader reader = new XomStreamReader(psuStream))
{
do
{
// read header
PsuEntryType type = (PsuEntryType)reader.ReadUInt16();
reader.Skip(0x02);
int size = reader.ReadInt32();
reader.Skip(0x01);
DateTime creationDate = reader.ReadDateTime();
ushort sector = reader.ReadUInt16();
reader.Skip(0x07);
DateTime modifiedDate = reader.ReadDateTime();
reader.Skip(0x20);
string filename = reader.ReadStrLen(0x1C0);
if (type == PsuEntryType.FILE_ENTRY || type == PsuEntryType.SYSTEM_ENTRY)
{
byte[] fileData = reader.ReadBytes(size);
reader.Skip(0x400 - (size % 0x400));
this.entries.Add(new PsuFileEntry(type, size, creationDate, sector, modifiedDate, filename, fileData));
}
else
{
this.entries.Add(new PsuFileEntry(type, size, creationDate, sector, modifiedDate, filename, null));
}
}
while (psuStream.Position < psuStream.Length);
}
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LibW4M.PS2
{
public class PsuFileEntry
{
internal int directoryEntries;
public Stream FileData;
public PsuEntryType EntryType;
public DateTime CreationDate;
public ushort Sector;
public DateTime ModifiedDate;
public string Filename;
public bool IsFile
{
get
{
return (EntryType == PsuEntryType.FILE_ENTRY || EntryType == PsuEntryType.SYSTEM_ENTRY);
}
}
public int Filesize
{
get
{
if (IsFile)
{
return Convert.ToInt32(FileData.Length);
}
else
{
return directoryEntries;
}
}
set
{
if (IsFile)
{
FileData.SetLength(value);
}
else
{
directoryEntries = value;
}
}
}
public PsuFileEntry(PsuEntryType entryType, int fileSize, DateTime creationDate, ushort sector, DateTime modifiedDate, string fileName, byte[]? fileData)
{
this.EntryType = entryType;
this.directoryEntries = fileSize;
this.CreationDate = creationDate;
this.Sector = sector;
this.ModifiedDate = modifiedDate;
this.Filename = fileName;
if(fileData is null)
{
this.FileData = Stream.Null;
}
else
{
this.FileData = new MemoryStream();
this.FileData.Write(fileData, 0, fileSize);
this.FileData.Seek(0, SeekOrigin.Begin);
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

83
LibW4M/PS2/SaveResources.Designer.cs generated Normal file
View File

@ -0,0 +1,83 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace LibW4M.PS2 {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class SaveResources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal SaveResources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LibW4M.PS2.SaveResources", typeof(SaveResources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] IconFile {
get {
object obj = ResourceManager.GetObject("IconFile", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
public static byte[] IconSys {
get {
object obj = ResourceManager.GetObject("IconSys", resourceCulture);
return ((byte[])(obj));
}
}
}
}

View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="IconFile" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\Icon1.ico;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="IconSys" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\icon.sys;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net7.0\publish\win-x86\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>true</PublishReadyToRun>
<PublishTrimmed>false</PublishTrimmed>
</PropertyGroup>
</Project>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<History>False|2023-02-18T17:31:12.6646298Z;</History>
<LastFailureDetails />
</PropertyGroup>
</Project>

View File

@ -9,11 +9,13 @@ using LibW4M.Data.WeaponFactory;
using LibW4M.Data.WXFE;
using LibW4M.Data.WXFE.UnlockableItem;
using LibW4M.Data.X;
using LibW4M.PS2;
using LibXom;
using LibXom.Blocks;
using LibXom.Data;
using LibXom.Exceptions;
using LibXom.Streams;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml.XPath;
@ -21,24 +23,31 @@ namespace LibW4M
{
public class W4SaveFile
{
public const int PS2_MAX_SZ = 0x20000;
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 StatsCollective TeamStatsCollective;
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
@ -84,7 +93,7 @@ namespace LibW4M
break;
case "PersistStats":
StatsCollective = new StatsCollective(this, containerResourceDetail.Value);
TeamStatsCollective = new StatsCollective(this, containerResourceDetail.Value);
TeamStatsCollective = new TeamStatsCollective(this, containerResourceDetail.Value);
break;
case "Awards":
TeamAwardsContainer = new TeamAwardsData(this, containerResourceDetail.Value);
@ -114,11 +123,15 @@ namespace LibW4M
}
}
}
private void saveData()
internal void saveData()
{
XDataBank.Save();
#if !DEBUG
xomFile.ClearAllStrings();
#endif
XDataBank.Save();
WeaponFactoryCollective.Save();
// Save Teams, Highscores
@ -137,60 +150,44 @@ namespace LibW4M
foreach (UnlockableItemData unlockableItem in UnlockableItems)
unlockableItem.Save();
}
public void SavePC(Stream pcSaveStream)
{
saveData();
using (MemoryStream ms = new MemoryStream())
{
XomWriter.WriteXom(xomFile, pcSaveStream);
ms.Seek(0, SeekOrigin.Begin);
ms.CopyTo(pcSaveStream);
}
}
public void SavePS2PSU(Stream ps2PsuSaveStream)
{
using (ps2PsuSaveStream)
{
Ps2Save.CreatePSU(this, ps2PsuSaveStream);
}
}
public void SavePS2(Stream ps2SaveStream)
{
using (ps2SaveStream)
{
Ps2Save.CreateSaveFile(this, ps2SaveStream);
}
}
public void SavePS2(string newXom)
{
// Remove all stats ...
foreach(StatsContainerData stat in StatsCollective.ToArray())
StatsCollective.Delete(stat);
// Save changes
saveData();
using(FileStream fs = File.OpenWrite(newXom))
{
using (MemoryStream ms = new MemoryStream())
{
using (XomStreamReader reader = new XomStreamReader(ms))
{
using (XomStreamWriter writer = new XomStreamWriter(ms))
{
XomWriter.WriteXom(xomFile, ms);
// Set size to ps2 save size.
ms.SetLength(PS2_MAX_SZ);
ms.Seek(0, SeekOrigin.Begin);
// Calculate checksum
Int64 chk = 0;
for (int i = 0; i < (PS2_MAX_SZ - 4); i += 4)
{
int c = reader.ReadInt32();
chk = (chk + c) & UInt32.MaxValue;
}
writer.WriteUInt32(Convert.ToUInt32(chk));
ms.Seek(0, SeekOrigin.Begin);
ms.CopyTo(fs);
}
}
}
}
SavePS2(File.OpenWrite(newXom));
}
public void Save(string newXom)
public void SavePS2PSU(string newXom)
{
saveData();
XomWriter.WriteXom(xomFile, newXom);
SavePS2PSU(File.OpenWrite(newXom));
}
public void SavePC(string newXom)
{
SavePC(File.OpenWrite(newXom));
}
public void ExtractAllContainers(string path)

View File

@ -12,6 +12,7 @@ namespace LibXom.Data
{
private int version;
private int unk0;
private List<XomString> xomStrings = new List<XomString>();
private List<XomType> xomTypes = new List<XomType>();
@ -47,14 +48,25 @@ namespace LibXom.Data
xomStrings[stringId] = newStr;
}
internal int addString(XomString xStr)
{
xomStrings.Add(xStr);
return xomStrings.Count - 1;
}
public void ClearAllStrings()
{
xomStrings.Clear();
}
public XomString AddOrGetString(string value)
{
XomString[] strings = XomStrings;
for (int i = 0; i < strings.Length; i++)
if (strings[i].Value == value) return strings[i];
if (strings[i].Equals(value)) return strings[i];
XomString str = new XomString(this, value);
xomStrings.Add(str);
addString(str);
return str;
}
@ -78,6 +90,15 @@ namespace LibXom.Data
throw new XomTypeNotFoundException("Type \"" + typeName + "\" was not found in XOM.");
}
internal int calculateIdForXomStrings(XomString searchString, XomString[] stringsList)
{
for (int i = 0; i < stringsList.Length; i++)
if (stringsList[i].Equals(searchString)) return i;
return addString(searchString);
}
internal int calculateIdForXomFileComponent(string searchUuid, XomFileComponent[] components)
{
for (int i = 0; i < components.Length; i++)
@ -90,11 +111,13 @@ namespace LibXom.Data
{
XomContainer[] containers = this.XomContainers;
XomType[] types = this.XomTypes;
XomString[] strings = XomStrings;
XomString[] strings = this.XomStrings;
List<XomBlock> blocks = new List<XomBlock>();
// add MOIK block
blocks.Add(new MoikBlock(this.version, containers.Length, types.Length));
// add type blocks
foreach (XomType type in types) blocks.Add(new TypeBlock(type.Containers.Length, type.Md5, type.Name));

View File

@ -15,7 +15,7 @@ namespace LibXom.Data
{
get
{
return this.fileBelongs.calculateIdForXomFileComponent(this.uuid, fileBelongs.XomStrings) - 1;
return this.fileBelongs.calculateIdForXomStrings(this, fileBelongs.XomStrings);
}
}
public string Value
@ -34,13 +34,23 @@ namespace LibXom.Data
}
}
/*
public void OverwriteXomString(string newValue)
public override bool Equals(object? obj)
{
this.value = newValue;
fileBelongs.updateStringById(this.Id, this);
XomString? xStr = obj as XomString;
if (xStr is XomString)
return this.Value.Equals(xStr.Value, StringComparison.InvariantCulture);
else
return false;
}
*/
/*
public void OverwriteXomString(string newValue)
{
this.value = newValue;
fileBelongs.updateStringById(this.Id, this);
}
*/
public override string ToString()
{
return this.Value;

View File

@ -50,7 +50,10 @@ namespace LibXom.Data
return i;
}
}
throw new XomContainerNotFoundException("Could not find Xom Container with uuid: " + container.uuid + " in type: " + this.Name);
// maybe removed this container by mistakes; so lets just add it back
container.Type.xomContainers.Add(container);
return getContainerIndex(container);
}
public void ReplaceContainerData(XomContainer container, byte[] newData)
{

View File

@ -53,6 +53,19 @@ namespace LibXom.Streams
xStream.Read(buffer, 0, amt);
return buffer;
}
public DateTime ReadDateTime()
{
byte secs = ReadByte();
byte minutes = ReadByte();
byte hours = ReadByte();
byte days = ReadByte();
byte months = ReadByte();
UInt16 years = ReadUInt16();
return new DateTime(years, months, days, hours, minutes, secs);
}
public bool ReadBool()
{
return (ReadByte() == 0x01);
@ -92,6 +105,16 @@ namespace LibXom.Streams
{
return BitConverter.ToSingle(ReadBytes(0x4));
}
public ushort ReadUInt16()
{
return BitConverter.ToUInt16(ReadBytes(0x2));
}
public short ReadInt16()
{
return BitConverter.ToInt16(ReadBytes(0x2));
}
public uint ReadUInt32()
{
return BitConverter.ToUInt32(ReadBytes(0x4));

View File

@ -95,6 +95,26 @@ namespace LibXom.Streams
WriteBytes(buffer);
}
public void WriteDateTime(DateTime date)
{
WriteByte(Convert.ToByte(date.Second));
WriteByte(Convert.ToByte(date.Minute));
WriteByte(Convert.ToByte(date.Hour));
WriteByte(Convert.ToByte(date.Day));
WriteByte(Convert.ToByte(date.Month));
WriteUInt16(Convert.ToUInt16(date.Year));
}
public void WriteUInt16(ushort value)
{
byte[] buffer = BitConverter.GetBytes((ushort)value);
WriteBytes(buffer);
}
public void WriteInt16(short value)
{
byte[] buffer = BitConverter.GetBytes((short)value);
WriteBytes(buffer);
}
public void WriteUInt32(uint value)
{
byte[] buffer = BitConverter.GetBytes((uint)value);

View File

@ -68,7 +68,7 @@ namespace W4Gui.Components
{
int sel = lst.SelectedIndex;
if (sel >= 0)
lst.Items[sel] = newEntry.Text;
lst.Items[sel] = newEntry.Text.ToString();
}
private void newEntry_KeyDown(object sender, KeyEventArgs e)
@ -93,7 +93,9 @@ namespace W4Gui.Components
private void lst_SelectedIndexChanged(object sender, EventArgs e)
{
if (prevSelected >= 0)
if (prevSelected == lst.SelectedIndex) return;
if (prevSelected >= 0 && prevSelected < lst.Items.Count)
lst.Items[prevSelected] = newEntry.Text;
if (lst.SelectedIndex >= 0)

View File

@ -10,6 +10,9 @@ namespace W4Gui
{
public static class DataManager
{
public static SaveType LoadedSaveType;
public static string LoadedSavePath;
public static bool SaveLoaded = false;
public static W4SaveFile SaveFile;
public static void SaveAll()

17
W4Gui/Main.Designer.cs generated
View File

@ -58,6 +58,7 @@ namespace W4Gui
this.awardPanel = new W4Gui.Tabs.AwardTab();
this.variablesTab = new System.Windows.Forms.TabPage();
this.variablesPage = new W4Gui.Tabs.VariablesTab.VariablesTabs();
this.saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.mainMenuStrip.SuspendLayout();
this.mainTabControl.SuspendLayout();
this.weaponTab.SuspendLayout();
@ -87,7 +88,8 @@ namespace W4Gui
//
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.openToolStripMenuItem,
this.saveToolStripMenuItem});
this.saveToolStripMenuItem,
this.saveAsToolStripMenuItem});
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
this.fileToolStripMenuItem.Text = "File";
@ -95,7 +97,7 @@ namespace W4Gui
// openToolStripMenuItem
//
this.openToolStripMenuItem.Name = "openToolStripMenuItem";
this.openToolStripMenuItem.Size = new System.Drawing.Size(103, 22);
this.openToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.openToolStripMenuItem.Text = "Open";
this.openToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click);
//
@ -103,7 +105,7 @@ namespace W4Gui
//
this.saveToolStripMenuItem.Enabled = false;
this.saveToolStripMenuItem.Name = "saveToolStripMenuItem";
this.saveToolStripMenuItem.Size = new System.Drawing.Size(103, 22);
this.saveToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.saveToolStripMenuItem.Text = "Save";
this.saveToolStripMenuItem.Click += new System.EventHandler(this.saveToolStripMenuItem_Click);
//
@ -331,6 +333,14 @@ namespace W4Gui
this.variablesPage.Size = new System.Drawing.Size(182, 62);
this.variablesPage.TabIndex = 0;
//
// saveAsToolStripMenuItem
//
this.saveAsToolStripMenuItem.Enabled = false;
this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem";
this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.saveAsToolStripMenuItem.Text = "Save As";
this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.saveAsToolStripMenuItem_Click);
//
// Main
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
@ -389,5 +399,6 @@ namespace W4Gui
private TabPage awardsTab;
private AwardTab awardPanel;
private InputEventMappingTab inputEventMappingsPanel;
private ToolStripMenuItem saveAsToolStripMenuItem;
}
}

View File

@ -1,5 +1,6 @@
using LibW4M;
using LibW4M.Data.WeaponFactory;
using LibW4M.PS2;
using LibXom;
using LibXom.Data;
using System;
@ -29,20 +30,47 @@ namespace W4Gui
}
private SaveType getTypeFromName(string filename)
{
switch (Path.GetExtension(filename))
{
case ".psu":
return SaveType.PS2;
case ".xom":
default:
return SaveType.PC;
}
}
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog fd = new OpenFileDialog();
fd.Filter = "Worms 4: Mayhem Save File|*.xom";
fd.Filter = "Worms 4: Mayhem Save Files|*.xom;*.psu|Worms 4: Mayhem PC Save File|*.xom|Worms 4: Mayhem PS2 Save File|*.psu";
fd.Title = "Open Worms 4 Save File";
if(fd.ShowDialog() == DialogResult.OK)
{
DataManager.SaveFile = new W4SaveFile(XomReader.ReadXomFile(fd.FileName));
DataManager.LoadedSavePath = fd.FileName;
DataManager.LoadedSaveType = getTypeFromName(DataManager.LoadedSavePath);
switch (DataManager.LoadedSaveType)
{
case SaveType.PS2:
DataManager.SaveFile = new W4SaveFile(XomReader.ReadXomFile(PsuFile.ReadPSU(fd.FileName).GetFileByName("BESLES-53096W4MA").FileData));
break;
case SaveType.PC:
default:
DataManager.SaveFile = new W4SaveFile(XomReader.ReadXomFile(fd.FileName));
break;
}
this.mainTabControl.Enabled = true;
this.saveToolStripMenuItem.Enabled = true;
this.saveAsToolStripMenuItem.Enabled = true;
this.convertToolStripMenuItem.Enabled = true;
this.Text = defaultTitle + " [" + fd.FileName + "]";
this.Text = defaultTitle + " (" + DataManager.LoadedSaveType.ToString() + ") " + "[" + fd.FileName + "]";
DataManager.LoadAll();
}
@ -50,19 +78,22 @@ namespace W4Gui
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog fd = new SaveFileDialog();
fd.Filter = "Worms 4: Mayhem Save File|*.xom";
fd.Title = "Save Worms 4 Save File";
fd.FileName = "SaveGame.xom";
if (fd.ShowDialog() == DialogResult.OK)
mainTabControl.Enabled = false;
DataManager.SaveAll();
switch (DataManager.LoadedSaveType)
{
this.mainTabControl.Enabled = false;
DataManager.SaveAll();
DataManager.SaveFile.Save(fd.FileName);
this.mainTabControl.Enabled = true;
case SaveType.PC:
DataManager.SaveFile.SavePC(DataManager.LoadedSavePath);
break;
case SaveType.PS2:
DataManager.SaveFile.SavePS2PSU(DataManager.LoadedSavePath);
break;
case SaveType.XBOX:
break;
}
mainTabControl.Enabled = true;
MessageBox.Show("File saved to: " + DataManager.LoadedSavePath, "Save Complete!", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void extractAllXomContainersToolStripMenuItem_Click(object sender, EventArgs e)
@ -131,5 +162,33 @@ namespace W4Gui
this.mainTabControl.Enabled = true;
}
}
private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog fd = new SaveFileDialog();
fd.Filter = "Worms 4: Mayhem Save Files|*.xom;*.psu|Worms 4: Mayhem PC Save File|*.xom|Worms 4: Mayhem PS2 Single Save File|*.psu";
fd.Title = "Save Worms 4 Save File";
fd.FileName = Path.GetFileName(DataManager.LoadedSavePath);
if (fd.ShowDialog() == DialogResult.OK)
{
this.mainTabControl.Enabled = false;
DataManager.SaveAll();
SaveType saveType = getTypeFromName(fd.FileName);
switch (saveType)
{
case SaveType.PS2:
DataManager.SaveFile.SavePS2PSU(fd.FileName);
break;
case SaveType.PC:
DataManager.SaveFile.SavePC(fd.FileName);
break;
}
MessageBox.Show("File saved to: " + fd.FileName, "Save Complete!", MessageBoxButtons.OK, MessageBoxIcon.Information);
this.mainTabControl.Enabled = true;
}
}
}
}

View File

@ -4,7 +4,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<History>True|2023-02-28T20:19:31.4651141Z;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;</History>
<History>True|2023-03-03T15:40:06.7859106Z;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;</History>
<LastFailureDetails />
</PropertyGroup>
</Project>

15
W4Gui/SaveType.cs Normal file
View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace W4Gui
{
public enum SaveType
{
PC,
PS2,
XBOX
}
}