mirror of
https://gitlab.com/Syroot/Worms.git
synced 2025-03-04 01:15:21 +03:00
Overhaul implementations and documentation.
This commit is contained in:
parent
8b92e6a8be
commit
5321dfb49a
7
.gitattributes
vendored
7
.gitattributes
vendored
@ -1 +1,8 @@
|
|||||||
|
*.dat filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.img filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pxl filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pxs filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.wgt filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.wsc filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.wwp filter=lfs diff=lfs merge=lfs -text
|
||||||
src/test/Syroot.Worms.Armageddon.Test/Files/Schemes/WA/Wsdb/dump.csv filter=lfs diff=lfs merge=lfs -text
|
src/test/Syroot.Worms.Armageddon.Test/Files/Schemes/WA/Wsdb/dump.csv filter=lfs diff=lfs merge=lfs -text
|
||||||
|
8
src/010editor/Common.bt
Normal file
8
src/010editor/Common.bt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
void FAlign(byte alignment)
|
||||||
|
{
|
||||||
|
local int bytesToSkip <hidden=true> = (-FTell() % alignment + alignment) % alignment;
|
||||||
|
while (bytesToSkip--)
|
||||||
|
{
|
||||||
|
byte padding <fgcolor=0x808080, hidden=true>;
|
||||||
|
}
|
||||||
|
}
|
18
src/010editor/IMG.bt
Normal file
18
src/010editor/IMG.bt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
//------------------------------------------------
|
||||||
|
//--- 010 Editor v10.0.2 Binary Template
|
||||||
|
//
|
||||||
|
// File: IMG.bt
|
||||||
|
// Authors: Syroot
|
||||||
|
// Version: 0.1.0
|
||||||
|
// Purpose: Parse Worms Armageddon and Worms World Party image files.
|
||||||
|
// Category: Worms
|
||||||
|
// File Mask: *.img
|
||||||
|
// ID Bytes:
|
||||||
|
// History:
|
||||||
|
// 0.1.0 2020-06-29 Initial version.
|
||||||
|
//------------------------------------------------
|
||||||
|
|
||||||
|
#include "Image.bt"
|
||||||
|
|
||||||
|
LittleEndian();
|
||||||
|
Image image <open=true>;
|
74
src/010editor/Image.bt
Normal file
74
src/010editor/Image.bt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#include "Common.bt"
|
||||||
|
|
||||||
|
typedef struct(char description, char align)
|
||||||
|
{
|
||||||
|
uint signature <format=hex>;
|
||||||
|
if (signature != 0x1A474D49) // "IMG\x1A"
|
||||||
|
{
|
||||||
|
Warning("Invalid IMG signature.");
|
||||||
|
Exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint fileSize;
|
||||||
|
|
||||||
|
if (description)
|
||||||
|
string description;
|
||||||
|
|
||||||
|
ubyte bpp;
|
||||||
|
enum <ubyte> Flags
|
||||||
|
{
|
||||||
|
IMG_COMPRESSED = 1 << 6,
|
||||||
|
IMG_PALETTIZED = 1 << 7
|
||||||
|
} flags;
|
||||||
|
|
||||||
|
if (flags & IMG_PALETTIZED)
|
||||||
|
{
|
||||||
|
ushort colorCount;
|
||||||
|
struct Color
|
||||||
|
{
|
||||||
|
ubyte r;
|
||||||
|
ubyte g;
|
||||||
|
ubyte b;
|
||||||
|
} colors[colorCount];
|
||||||
|
}
|
||||||
|
|
||||||
|
ushort width;
|
||||||
|
ushort height;
|
||||||
|
|
||||||
|
if (align)
|
||||||
|
FAlign(4);
|
||||||
|
|
||||||
|
if (flags & IMG_COMPRESSED)
|
||||||
|
char data[GetCompressedImgSize()];
|
||||||
|
else
|
||||||
|
char data[bpp / 8.0 * width * height];
|
||||||
|
} Image;
|
||||||
|
|
||||||
|
int GetCompressedImgSize()
|
||||||
|
{
|
||||||
|
local int pos = FTell();
|
||||||
|
local int start = pos;
|
||||||
|
local int cmd;
|
||||||
|
while ((cmd = ReadByte(pos++)) != -1)
|
||||||
|
{
|
||||||
|
// Read a byte.
|
||||||
|
if ((cmd & 0x80) != 0)
|
||||||
|
{
|
||||||
|
local int arg1 = cmd >> 3 & 0b1111; // bits 2-5
|
||||||
|
local int arg2 = ReadByte(pos++);
|
||||||
|
if (arg2 == -1)
|
||||||
|
break;
|
||||||
|
// Arg2 = bits 6-16
|
||||||
|
arg2 = ((cmd << 8) | arg2) & 0x7FF;
|
||||||
|
if (arg1 == 0)
|
||||||
|
{
|
||||||
|
// Command: 0x80 0x00
|
||||||
|
if (arg2 == 0)
|
||||||
|
break;
|
||||||
|
if (ReadByte(pos++) == -1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pos - start;
|
||||||
|
}
|
20
src/010editor/OW_PAL.bt
Normal file
20
src/010editor/OW_PAL.bt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//------------------------------------------------
|
||||||
|
//--- 010 Editor v10.0.2 Binary Template
|
||||||
|
//
|
||||||
|
// File: OW_PAL.bt
|
||||||
|
// Authors: Syroot
|
||||||
|
// Version: 0.1.0
|
||||||
|
// Purpose: Parse Online Worms PAL color palettes.
|
||||||
|
// Category: Worms
|
||||||
|
// File Mask: *.pal
|
||||||
|
// ID Bytes:
|
||||||
|
// History:
|
||||||
|
// 0.1.0 2020-06-30 Initial version.
|
||||||
|
//------------------------------------------------
|
||||||
|
|
||||||
|
LittleEndian();
|
||||||
|
|
||||||
|
struct Color
|
||||||
|
{
|
||||||
|
ubyte r, g, b;
|
||||||
|
} colors[256];
|
44
src/010editor/W2_LandDAT.bt
Normal file
44
src/010editor/W2_LandDAT.bt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//------------------------------------------------
|
||||||
|
//--- 010 Editor v10.0.2 Binary Template
|
||||||
|
//
|
||||||
|
// File: W2_LandDAT.bt
|
||||||
|
// Authors: Syroot
|
||||||
|
// Version: 0.1.0
|
||||||
|
// Purpose: Parse Worms 2 land generator data files.
|
||||||
|
// Category: Worms
|
||||||
|
// File Mask: land.dat
|
||||||
|
// ID Bytes:
|
||||||
|
// History:
|
||||||
|
// 0.1.0 2020-06-30 Initial version.
|
||||||
|
//------------------------------------------------
|
||||||
|
|
||||||
|
#include "Image.bt"
|
||||||
|
|
||||||
|
LittleEndian();
|
||||||
|
|
||||||
|
uint signature <format=hex>;
|
||||||
|
if (signature != 0x1A444E4C) // "LND\x1A"
|
||||||
|
{
|
||||||
|
Warning("Invalid LND signature.");
|
||||||
|
Exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fileSize;
|
||||||
|
int mapWidth;
|
||||||
|
int mapHeight;
|
||||||
|
int hasCavernBorder;
|
||||||
|
int numObjectLocations;
|
||||||
|
struct ObjectLocation
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
} objectLocations[numObjectLocations];
|
||||||
|
int unknown;
|
||||||
|
Image foregroundImage(true, false);
|
||||||
|
Image collisionImage(true, false);
|
||||||
|
Image backgroundImage(true, false);
|
||||||
|
Image unknownImage(true, false);
|
||||||
|
ubyte landTexPathLength;
|
||||||
|
char landTexPath[landTexPathLength];
|
||||||
|
ubyte waterDirPathLength;
|
||||||
|
char waterDirPath[waterDirPathLength];
|
43
src/010editor/WA3.0_LandDAT.bt
Normal file
43
src/010editor/WA3.0_LandDAT.bt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//------------------------------------------------
|
||||||
|
//--- 010 Editor v10.0.2 Binary Template
|
||||||
|
//
|
||||||
|
// File: WA3.0_LandDAT.bt
|
||||||
|
// Authors: Syroot
|
||||||
|
// Version: 0.1.0
|
||||||
|
// Purpose: Parse Worms Armageddon (older than 3.6.28.0) land generator data files.
|
||||||
|
// Category: Worms
|
||||||
|
// File Mask: land.dat
|
||||||
|
// ID Bytes:
|
||||||
|
// History:
|
||||||
|
// 0.1.0 2020-06-30 Initial version.
|
||||||
|
//------------------------------------------------
|
||||||
|
|
||||||
|
#include "Image.bt"
|
||||||
|
|
||||||
|
LittleEndian();
|
||||||
|
|
||||||
|
uint signature <format=hex>;
|
||||||
|
if (signature != 0x1A444E4C) // "LND\x1A"
|
||||||
|
{
|
||||||
|
Warning("Invalid LND signature.");
|
||||||
|
Exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fileSize;
|
||||||
|
int mapWidth;
|
||||||
|
int mapHeight;
|
||||||
|
int hasCavernBorder;
|
||||||
|
int waterHeight;
|
||||||
|
int numObjectLocations;
|
||||||
|
struct ObjectLocation
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
} objectLocations[numObjectLocations];
|
||||||
|
Image foregroundImage(false, false);
|
||||||
|
Image collisionImage(false, false);
|
||||||
|
Image backgroundImage(false, false);
|
||||||
|
ubyte landTexPathLength;
|
||||||
|
char landTexPath[landTexPathLength];
|
||||||
|
ubyte waterDirPathLength;
|
||||||
|
char waterDirPath[waterDirPathLength];
|
44
src/010editor/WA3.6_LandDAT.bt
Normal file
44
src/010editor/WA3.6_LandDAT.bt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//------------------------------------------------
|
||||||
|
//--- 010 Editor v10.0.2 Binary Template
|
||||||
|
//
|
||||||
|
// File: WA3.6_LandDAT.bt
|
||||||
|
// Authors: Syroot
|
||||||
|
// Version: 0.1.0
|
||||||
|
// Purpose: Parse Worms Armageddon (3.6.28.0 and newer) land generator data files.
|
||||||
|
// Category: Worms
|
||||||
|
// File Mask: land.dat
|
||||||
|
// ID Bytes:
|
||||||
|
// History:
|
||||||
|
// 0.1.0 2020-06-30 Initial version.
|
||||||
|
//------------------------------------------------
|
||||||
|
|
||||||
|
#include "Image.bt"
|
||||||
|
|
||||||
|
LittleEndian();
|
||||||
|
|
||||||
|
uint signature <format=hex>;
|
||||||
|
if (signature != 0x1A444E4C) // "LND\x1A"
|
||||||
|
{
|
||||||
|
Warning("Invalid LND signature.");
|
||||||
|
Exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fileSize;
|
||||||
|
int mapWidth;
|
||||||
|
int mapHeight;
|
||||||
|
int hasCavernBorder;
|
||||||
|
int waterHeight;
|
||||||
|
int holeCount;
|
||||||
|
int numObjectLocations;
|
||||||
|
struct ObjectLocation
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
} objectLocations[numObjectLocations];
|
||||||
|
Image foregroundImage(false, false);
|
||||||
|
Image collisionImage(false, false);
|
||||||
|
Image backgroundImage(false, false);
|
||||||
|
ubyte landTexPathLength;
|
||||||
|
char landTexPath[landTexPathLength];
|
||||||
|
ubyte waterDirPathLength;
|
||||||
|
char waterDirPath[waterDirPathLength];
|
44
src/010editor/WWPA_LandDAT.bt
Normal file
44
src/010editor/WWPA_LandDAT.bt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//------------------------------------------------
|
||||||
|
//--- 010 Editor v10.0.2 Binary Template
|
||||||
|
//
|
||||||
|
// File: WWPA_LandDAT.bt
|
||||||
|
// Authors: Syroot
|
||||||
|
// Version: 0.1.0
|
||||||
|
// Purpose: Parse Worms World Party Aqua land generator data files.
|
||||||
|
// Category: Worms
|
||||||
|
// File Mask: land.dat
|
||||||
|
// ID Bytes:
|
||||||
|
// History:
|
||||||
|
// 0.1.0 2020-06-30 Initial version.
|
||||||
|
//------------------------------------------------
|
||||||
|
|
||||||
|
#include "Image.bt"
|
||||||
|
|
||||||
|
LittleEndian();
|
||||||
|
|
||||||
|
uint signature <format=hex>;
|
||||||
|
if (signature != 0x1B444E4C) // "LND\x1B"
|
||||||
|
{
|
||||||
|
Warning("Invalid LND signature.");
|
||||||
|
Exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fileSize;
|
||||||
|
int mapWidth;
|
||||||
|
int mapHeight;
|
||||||
|
int hasCavernBorder;
|
||||||
|
int waterHeight;
|
||||||
|
int holeCount;
|
||||||
|
int numObjectLocations;
|
||||||
|
struct ObjectLocation
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
} objectLocations[numObjectLocations];
|
||||||
|
Image foregroundImage(false, true);
|
||||||
|
Image collisionImage(false, true);
|
||||||
|
Image backgroundImage(false, true);
|
||||||
|
ubyte landTexPathLength;
|
||||||
|
char landTexPath[landTexPathLength];
|
||||||
|
ubyte waterDirPathLength;
|
||||||
|
char waterDirPath[waterDirPathLength];
|
43
src/010editor/WWP_LandDAT.bt
Normal file
43
src/010editor/WWP_LandDAT.bt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
//------------------------------------------------
|
||||||
|
//--- 010 Editor v10.0.2 Binary Template
|
||||||
|
//
|
||||||
|
// File: WWP_LandDAT.bt
|
||||||
|
// Authors: Syroot
|
||||||
|
// Version: 0.1.0
|
||||||
|
// Purpose: Parse Worms World Party land generator data files.
|
||||||
|
// Category: Worms
|
||||||
|
// File Mask: land.dat
|
||||||
|
// ID Bytes:
|
||||||
|
// History:
|
||||||
|
// 0.1.0 2020-06-30 Initial version.
|
||||||
|
//------------------------------------------------
|
||||||
|
|
||||||
|
#include "Image.bt"
|
||||||
|
|
||||||
|
LittleEndian();
|
||||||
|
|
||||||
|
uint signature <format=hex>;
|
||||||
|
if (signature != 0x1A444E4C) // "LND\x1A"
|
||||||
|
{
|
||||||
|
Warning("Invalid LND signature.");
|
||||||
|
Exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fileSize;
|
||||||
|
int mapWidth;
|
||||||
|
int mapHeight;
|
||||||
|
int hasCavernBorder;
|
||||||
|
int waterHeight;
|
||||||
|
int numObjectLocations;
|
||||||
|
struct ObjectLocation
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
} objectLocations[numObjectLocations];
|
||||||
|
Image foregroundImage(false, true);
|
||||||
|
Image collisionImage(false, true);
|
||||||
|
Image backgroundImage(false, true);
|
||||||
|
ubyte landTexPathLength;
|
||||||
|
char landTexPath[landTexPathLength];
|
||||||
|
ubyte waterDirPathLength;
|
||||||
|
char waterDirPath[waterDirPathLength];
|
@ -44,8 +44,11 @@ EndProject
|
|||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = " Solution Items", " Solution Items", "{8A49F314-1011-4819-A8F6-0EDDA94A9C3D}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = " Solution Items", " Solution Items", "{8A49F314-1011-4819-A8F6-0EDDA94A9C3D}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
build.xml = build.xml
|
build.xml = build.xml
|
||||||
|
test.xml = test.xml
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Syroot.Worms.SchemeEditor", "tool\Syroot.Worms.SchemeEditor\Syroot.Worms.SchemeEditor.csproj", "{4ACE4589-4478-4B4D-8179-87A491B79DAE}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -116,6 +119,10 @@ Global
|
|||||||
{1FAB6B9F-2585-46DC-81C0-579DC808C389}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{1FAB6B9F-2585-46DC-81C0-579DC808C389}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{1FAB6B9F-2585-46DC-81C0-579DC808C389}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{1FAB6B9F-2585-46DC-81C0-579DC808C389}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{1FAB6B9F-2585-46DC-81C0-579DC808C389}.Release|Any CPU.Build.0 = Release|Any CPU
|
{1FAB6B9F-2585-46DC-81C0-579DC808C389}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{4ACE4589-4478-4B4D-8179-87A491B79DAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{4ACE4589-4478-4B4D-8179-87A491B79DAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{4ACE4589-4478-4B4D-8179-87A491B79DAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{4ACE4589-4478-4B4D-8179-87A491B79DAE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -137,6 +144,7 @@ Global
|
|||||||
{392E4CA2-61D9-4BE1-B065-8801A9F102B8} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
|
{392E4CA2-61D9-4BE1-B065-8801A9F102B8} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
|
||||||
{212F8090-9775-4098-BD44-9ABC01FBE553} = {99E56312-A064-4AD3-8443-0B56A5F76E6B}
|
{212F8090-9775-4098-BD44-9ABC01FBE553} = {99E56312-A064-4AD3-8443-0B56A5F76E6B}
|
||||||
{1FAB6B9F-2585-46DC-81C0-579DC808C389} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
|
{1FAB6B9F-2585-46DC-81C0-579DC808C389} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
|
||||||
|
{4ACE4589-4478-4B4D-8179-87A491B79DAE} = {0B9B0B74-3EB1-46A4-BCCC-F2D6AE59A9EE}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {1CD4EDE2-A5FB-4A58-A850-3506AB7E7B69}
|
SolutionGuid = {1CD4EDE2-A5FB-4A58-A850-3506AB7E7B69}
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
<!-- Compilation -->
|
<!-- Compilation -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
{
|
{
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
public object Read(Stream stream, object instance, BinaryMemberAttribute memberAttribute,
|
public object? Read(Stream stream, object instance, BinaryMemberAttribute memberAttribute,
|
||||||
ByteConverter byteConverter)
|
ByteConverter byteConverter)
|
||||||
{
|
{
|
||||||
ExplosionAction explosionAction = instance switch
|
ExplosionAction explosionAction = instance switch
|
||||||
@ -18,12 +18,13 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
return explosionAction switch
|
return explosionAction switch
|
||||||
{
|
{
|
||||||
|
ExplosionAction.None => null,
|
||||||
ExplosionAction.Bounce => stream.ReadObject<BounceAction>(),
|
ExplosionAction.Bounce => stream.ReadObject<BounceAction>(),
|
||||||
ExplosionAction.Dig => stream.ReadObject<DigAction>(),
|
ExplosionAction.Dig => stream.ReadObject<DigAction>(),
|
||||||
ExplosionAction.Home => stream.ReadObject<HomeAction>(),
|
ExplosionAction.Home => stream.ReadObject<HomeAction>(),
|
||||||
ExplosionAction.Roam => stream.ReadObject<RoamAction>(),
|
ExplosionAction.Roam => stream.ReadObject<RoamAction>(),
|
||||||
_ => null,
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,250 +1,250 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Armageddon.ProjectX
|
namespace Syroot.Worms.Armageddon.ProjectX
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a library stored in a PXL file which contains reusable weapons, files and scripts.
|
/// Represents a library stored in a PXL file which contains reusable weapons, files and scripts.
|
||||||
/// Used by WA PX. S. https://worms2d.info/Project_X/Library_file.
|
/// Used by WA PX. S. https://worms2d.info/Project_X/Library_file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Library : List<LibraryItem>, ILoadableFile, ISaveableFile
|
public class Library : List<LibraryItem>, ILoadableFile, ISaveableFile
|
||||||
{
|
{
|
||||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private const int _signature = 0x1BCD0102;
|
private const int _signature = 0x1BCD0102;
|
||||||
private const byte _version = 0x00;
|
private const byte _version = 0x00;
|
||||||
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Scheme"/> class.
|
/// Initializes a new instance of the <see cref="Scheme"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Library() { }
|
public Library() { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Scheme"/> class, loading the data from the given
|
/// Initializes a new instance of the <see cref="Scheme"/> class, loading the data from the given
|
||||||
/// <see cref="Stream"/>.
|
/// <see cref="Stream"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
public Library(Stream stream) => Load(stream);
|
public Library(Stream stream) => Load(stream);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Scheme"/> class, loading the data from the given file.
|
/// Initializes a new instance of the <see cref="Scheme"/> class, loading the data from the given file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
public Library(string fileName) => Load(fileName);
|
public Library(string fileName) => Load(fileName);
|
||||||
|
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
public byte Version { get; set; }
|
public byte Version { get; set; }
|
||||||
|
|
||||||
// ---- OPERATORS ----------------------------------------------------------------------------------------------
|
// ---- OPERATORS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the library entries matching the given key.
|
/// Gets the library entries matching the given key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The key of the entries to match.</param>
|
/// <param name="key">The key of the entries to match.</param>
|
||||||
/// <returns>All matching entries.</returns>
|
/// <returns>All matching entries.</returns>
|
||||||
public IEnumerable<LibraryItem> this[string key] => this.Where(x => x.Key == key);
|
public IEnumerable<LibraryItem> this[string key] => this.Where(x => x.Key == key);
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads the data from the given <see cref="Stream"/>.
|
/// Loads the data from the given <see cref="Stream"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
public void Load(Stream stream)
|
public void Load(Stream stream)
|
||||||
{
|
{
|
||||||
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
|
|
||||||
// Read the header.
|
// Read the header.
|
||||||
if (reader.ReadInt32() != _signature)
|
if (reader.ReadInt32() != _signature)
|
||||||
throw new InvalidDataException("Invalid PXL file signature.");
|
throw new InvalidDataException("Invalid PXL file signature.");
|
||||||
Version = reader.Read1Byte();
|
Version = reader.Read1Byte();
|
||||||
if (Version != _version)
|
if (Version != _version)
|
||||||
throw new InvalidDataException("Invalid PXL file version.");
|
throw new InvalidDataException("Invalid PXL file version.");
|
||||||
|
|
||||||
// Read the items.
|
// Read the items.
|
||||||
Clear();
|
Clear();
|
||||||
int itemCount = reader.ReadInt32();
|
int itemCount = reader.ReadInt32();
|
||||||
for (int i = 0; i < itemCount; i++)
|
for (int i = 0; i < itemCount; i++)
|
||||||
{
|
{
|
||||||
LibraryItemType type = reader.ReadEnum<LibraryItemType>(true);
|
LibraryItemType type = reader.ReadEnum<LibraryItemType>(true);
|
||||||
string name = reader.ReadString(StringCoding.Int32CharCount);
|
string name = reader.ReadString(StringCoding.Int32CharCount);
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case LibraryItemType.File:
|
case LibraryItemType.File:
|
||||||
Add(new LibraryItem(name, reader.ReadBytes(reader.ReadInt32())));
|
Add(new LibraryItem(name, reader.ReadBytes(reader.ReadInt32())));
|
||||||
break;
|
break;
|
||||||
case LibraryItemType.Script:
|
case LibraryItemType.Script:
|
||||||
Add(new LibraryItem(name, reader.ReadString(StringCoding.Int32CharCount)));
|
Add(new LibraryItem(name, reader.ReadString(StringCoding.Int32CharCount)));
|
||||||
break;
|
break;
|
||||||
case LibraryItemType.Weapon:
|
case LibraryItemType.Weapon:
|
||||||
Add(new LibraryItem(name, reader.Load<Weapon>()));
|
Add(new LibraryItem(name, reader.Load<Weapon>()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads the data from the given file.
|
/// Loads the data from the given file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
public void Load(string fileName)
|
public void Load(string fileName)
|
||||||
{
|
{
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
Load(stream);
|
Load(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the data into the given <paramref name="stream"/>.
|
/// Saves the data into the given <paramref name="stream"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
|
/// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
|
||||||
public void Save(Stream stream)
|
public void Save(Stream stream)
|
||||||
{
|
{
|
||||||
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
|
|
||||||
// Write the header.
|
// Write the header.
|
||||||
writer.Write(_signature);
|
writer.Write(_signature);
|
||||||
writer.Write(Version);
|
writer.Write(Version);
|
||||||
|
|
||||||
// Write the items.
|
// Write the items.
|
||||||
writer.Write(Count);
|
writer.Write(Count);
|
||||||
foreach (LibraryItem item in this)
|
foreach (LibraryItem item in this)
|
||||||
{
|
{
|
||||||
writer.WriteEnum(item.Type, true);
|
writer.WriteEnum(item.Type, true);
|
||||||
writer.Write(item.Key, StringCoding.Int32CharCount);
|
writer.Write(item.Key, StringCoding.Int32CharCount);
|
||||||
switch (item.Type)
|
switch (item.Type)
|
||||||
{
|
{
|
||||||
case LibraryItemType.File:
|
case LibraryItemType.File:
|
||||||
byte[] value = (byte[])item.Value;
|
byte[] value = (byte[])item.Value;
|
||||||
writer.Write(value.Length);
|
writer.Write(value.Length);
|
||||||
writer.Write(value);
|
writer.Write(value);
|
||||||
break;
|
break;
|
||||||
case LibraryItemType.Script:
|
case LibraryItemType.Script:
|
||||||
writer.Write((string)item.Value, StringCoding.Int32CharCount);
|
writer.Write((string)item.Value, StringCoding.Int32CharCount);
|
||||||
break;
|
break;
|
||||||
case LibraryItemType.Weapon:
|
case LibraryItemType.Weapon:
|
||||||
writer.Save((Weapon)item.Value);
|
writer.Save((Weapon)item.Value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the data in the given file.
|
/// Saves the data in the given file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fileName">The name of the file to save the data in.</param>
|
/// <param name="fileName">The name of the file to save the data in.</param>
|
||||||
public void Save(string fileName)
|
public void Save(string fileName)
|
||||||
{
|
{
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
Save(stream);
|
Save(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all attached files.
|
/// Gets all attached files.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The enumeration of attached files.</returns>
|
/// <returns>The enumeration of attached files.</returns>
|
||||||
public IEnumerable<byte[]> GetFiles()
|
public IEnumerable<byte[]> GetFiles()
|
||||||
=> this.Where(x => x.Type == LibraryItemType.File).Select(x => (byte[])x.Value);
|
=> this.Where(x => x.Type == LibraryItemType.File).Select(x => (byte[])x.Value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all attached scripts.
|
/// Gets all attached scripts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The enumeration of attached scripts.</returns>
|
/// <returns>The enumeration of attached scripts.</returns>
|
||||||
public IEnumerable<string> GetScripts()
|
public IEnumerable<string> GetScripts()
|
||||||
=> this.Where(x => x.Type == LibraryItemType.Script).Select(x => (string)x.Value);
|
=> this.Where(x => x.Type == LibraryItemType.Script).Select(x => (string)x.Value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets all attached weapons.
|
/// Gets all attached weapons.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The enumeration of attached weapons.</returns>
|
/// <returns>The enumeration of attached weapons.</returns>
|
||||||
public IEnumerable<Weapon> GetWeapons()
|
public IEnumerable<Weapon> GetWeapons()
|
||||||
=> this.Where(x => x.Type == LibraryItemType.Weapon).Select(x => (Weapon)x.Value);
|
=> this.Where(x => x.Type == LibraryItemType.Weapon).Select(x => (Weapon)x.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an entry in a Project X library.
|
/// Represents an entry in a Project X library.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DebuggerDisplay("LibraryItem Key={Key} Type={Type}")]
|
[DebuggerDisplay("LibraryItem Key={Key} Type={Type}")]
|
||||||
public class LibraryItem
|
public class LibraryItem
|
||||||
{
|
{
|
||||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private object _value;
|
private object _value = null!;
|
||||||
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="LibraryItem"/> class with the given <paramref name="key"/> and
|
/// Initializes a new instance of the <see cref="LibraryItem"/> class with the given <paramref name="key"/> and
|
||||||
/// <paramref name="value"/>.
|
/// <paramref name="value"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The key under which the item will be stored.</param>
|
/// <param name="key">The key under which the item will be stored.</param>
|
||||||
/// <param name="value">The value which will be stored under the key. The type is inferred from this.</param>
|
/// <param name="value">The value which will be stored under the key. The type is inferred from this.</param>
|
||||||
public LibraryItem(string key, object value)
|
public LibraryItem(string key, object value)
|
||||||
{
|
{
|
||||||
Key = key;
|
Key = key;
|
||||||
Value = value;
|
Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name under which this item is stored.
|
/// Gets or sets the name under which this item is stored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Key { get; set; }
|
public string Key { get; set; } = String.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the type of the item.
|
/// Gets the type of the item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public LibraryItemType Type { get; private set; }
|
public LibraryItemType Type { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the data of the item.
|
/// Gets or sets the data of the item.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object Value
|
public object Value
|
||||||
{
|
{
|
||||||
get => _value;
|
get => _value;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
// Validate the type.
|
// Validate the type.
|
||||||
if (value.GetType() == typeof(byte[]))
|
if (value.GetType() == typeof(byte[]))
|
||||||
Type = LibraryItemType.File;
|
Type = LibraryItemType.File;
|
||||||
else if (value.GetType() == typeof(string))
|
else if (value.GetType() == typeof(string))
|
||||||
Type = LibraryItemType.Script;
|
Type = LibraryItemType.Script;
|
||||||
else if (value.GetType() == typeof(Weapon))
|
else if (value.GetType() == typeof(Weapon))
|
||||||
Type = LibraryItemType.Weapon;
|
Type = LibraryItemType.Weapon;
|
||||||
else
|
else
|
||||||
throw new ArgumentException("Invalid LibraryItemType.", nameof(value));
|
throw new ArgumentException("Invalid LibraryItemType.", nameof(value));
|
||||||
_value = value;
|
_value = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the possible type of a library entry.
|
/// Represents the possible type of a library entry.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum LibraryItemType : byte
|
public enum LibraryItemType : byte
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The entry is a raw file in form of a byte array.
|
/// The entry is a raw file in form of a byte array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
File = 2,
|
File = 2,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The entry is a script in form of a string.
|
/// The entry is a script in form of a string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Script = 4,
|
Script = 4,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The entry is a weapon in form of a <see cref="Weapon"/> instance.
|
/// The entry is a weapon in form of a <see cref="Weapon"/> instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Weapon = 8
|
Weapon = 8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Armageddon.ProjectX
|
namespace Syroot.Worms.Armageddon.ProjectX
|
||||||
{
|
{
|
||||||
@ -44,17 +45,17 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
|
|
||||||
public SchemeFlags Flags { get; set; }
|
public SchemeFlags Flags { get; set; }
|
||||||
|
|
||||||
public List<Weapon[]> WeaponTables { get; set; }
|
public IList<Weapon[]> WeaponTables { get; set; } = new List<Weapon[]>();
|
||||||
|
|
||||||
public Dictionary<string, byte[]> Files { get; set; }
|
public IDictionary<string, byte[]> Files { get; set; } = new Dictionary<string, byte[]>();
|
||||||
|
|
||||||
public Dictionary<string, string> Scripts { get; set; }
|
public IDictionary<string, string> Scripts { get; set; } = new Dictionary<string, string>();
|
||||||
|
|
||||||
public List<string> Libraries { get; set; }
|
public IList<string> Libraries { get; set; } = new List<string>();
|
||||||
|
|
||||||
public string GameSchemeName { get; set; }
|
public string GameSchemeName { get; set; } = String.Empty;
|
||||||
|
|
||||||
public Armageddon.Scheme GameScheme { get; set; }
|
public Armageddon.Scheme? GameScheme { get; set; }
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -74,8 +75,13 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
// Read the weapon tables.
|
// Read the weapon tables.
|
||||||
int weaponTableCount = reader.ReadInt32();
|
int weaponTableCount = reader.ReadInt32();
|
||||||
WeaponTables = new List<Weapon[]>(weaponTableCount);
|
WeaponTables = new List<Weapon[]>(weaponTableCount);
|
||||||
for (int i = 0; i < weaponTableCount; i++)
|
for (int i = 0; i < weaponTableCount; i++)
|
||||||
WeaponTables.Add(reader.Load<Weapon>(_weaponsPerTable));
|
{
|
||||||
|
Weapon[] weaponTable = new Weapon[_weaponsPerTable];
|
||||||
|
for (int j = 0; j < _weaponsPerTable; j++)
|
||||||
|
weaponTable[j] = reader.Load<Weapon>();
|
||||||
|
WeaponTables.Add(weaponTable);
|
||||||
|
}
|
||||||
|
|
||||||
// Read a placeholder array.
|
// Read a placeholder array.
|
||||||
reader.Seek(sizeof(int));
|
reader.Seek(sizeof(int));
|
||||||
@ -131,8 +137,9 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
|
|
||||||
// Write the weapon tables.
|
// Write the weapon tables.
|
||||||
writer.Write(WeaponTables.Count);
|
writer.Write(WeaponTables.Count);
|
||||||
foreach (Weapon[] weaponTable in WeaponTables)
|
foreach (Weapon[] weaponTable in WeaponTables)
|
||||||
writer.Save(weaponTable);
|
for (int i = 0; i < _weaponsPerTable; i++)
|
||||||
|
writer.Save(weaponTable[i]);
|
||||||
|
|
||||||
// Write a placeholder array.
|
// Write a placeholder array.
|
||||||
writer.Write(0);
|
writer.Write(0);
|
||||||
|
@ -19,7 +19,7 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
public WeaponAirstrikeSubstyle AirstrikeSubstyle;
|
public WeaponAirstrikeSubstyle AirstrikeSubstyle;
|
||||||
|
|
||||||
[BinaryMember(Converter = typeof(AirstrikeSubstyleConverter))]
|
[BinaryMember(Converter = typeof(AirstrikeSubstyleConverter))]
|
||||||
public IStyle Style;
|
public IStyle? Style;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum WeaponAirstrikeSubstyle : int
|
public enum WeaponAirstrikeSubstyle : int
|
||||||
|
@ -20,7 +20,7 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
{
|
{
|
||||||
WeaponAirstrikeSubstyle.Launcher => stream.ReadObject<LauncherStyle>(),
|
WeaponAirstrikeSubstyle.Launcher => stream.ReadObject<LauncherStyle>(),
|
||||||
WeaponAirstrikeSubstyle.Mines => stream.ReadObject<MineStyle>(),
|
WeaponAirstrikeSubstyle.Mines => stream.ReadObject<MineStyle>(),
|
||||||
_ => null,
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,13 +47,13 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
public ExplosionAction ExplosionAction { get; set; }
|
public ExplosionAction ExplosionAction { get; set; }
|
||||||
|
|
||||||
[BinaryMember(Converter = typeof(ActionConverter))]
|
[BinaryMember(Converter = typeof(ActionConverter))]
|
||||||
public IAction Action { get; set; }
|
public IAction? Action { get; set; }
|
||||||
|
|
||||||
[BinaryMember(OffsetOrigin = OffsetOrigin.Begin, Offset = 180)]
|
[BinaryMember(OffsetOrigin = OffsetOrigin.Begin, Offset = 180)]
|
||||||
public ExplosionTarget ExplosionTarget { get; set; }
|
public ExplosionTarget ExplosionTarget { get; set; }
|
||||||
|
|
||||||
[BinaryMember(Offset = sizeof(int), Converter = typeof(TargetConverter))]
|
[BinaryMember(Offset = sizeof(int), Converter = typeof(TargetConverter))]
|
||||||
public ITarget Target { get; set; }
|
public ITarget? Target { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ExplosionAction : int
|
public enum ExplosionAction : int
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>Syroot.Worms.Armageddon.ProjectX</AssemblyName>
|
<AssemblyName>Syroot.Worms.Armageddon.ProjectX</AssemblyName>
|
||||||
<Description>.NET library for loading and modifying files of Worms Armageddon ProjectX.</Description>
|
<Description>.NET library for loading and modifying files of Worms Armageddon ProjectX.</Description>
|
||||||
<PackageReleaseNotes>Fix issues when loading and saving some formats.</PackageReleaseNotes>
|
<PackageReleaseNotes>Overhaul implementation and documentation.</PackageReleaseNotes>
|
||||||
<PackageTags>$(PackageTags);project x;worms armageddon</PackageTags>
|
<PackageTags>$(PackageTags);project x;worms armageddon</PackageTags>
|
||||||
<Version>3.2.0</Version>
|
<Version>4.0.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Syroot.Worms.Armageddon\Syroot.Worms.Armageddon.csproj" />
|
<ProjectReference Include="..\Syroot.Worms.Armageddon\Syroot.Worms.Armageddon.csproj" />
|
||||||
|
@ -50,6 +50,6 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
public ExplosionAction ExplosionAction { get; set; }
|
public ExplosionAction ExplosionAction { get; set; }
|
||||||
|
|
||||||
[BinaryMember(Converter = typeof(ActionConverter))]
|
[BinaryMember(Converter = typeof(ActionConverter))]
|
||||||
public IAction Action { get; set; }
|
public IAction? Action { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
{
|
{
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
public object Read(Stream stream, object instance, BinaryMemberAttribute memberAttribute,
|
public object? Read(Stream stream, object instance, BinaryMemberAttribute memberAttribute,
|
||||||
ByteConverter byteConverter)
|
ByteConverter byteConverter)
|
||||||
{
|
{
|
||||||
ExplosionTarget explosionTarget = instance switch
|
ExplosionTarget explosionTarget = instance switch
|
||||||
@ -17,10 +17,11 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
_ => throw new NotImplementedException(),
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
return explosionTarget switch
|
return explosionTarget switch
|
||||||
{
|
{
|
||||||
|
ExplosionTarget.None => null,
|
||||||
ExplosionTarget.Clusters => stream.ReadObject<ClusterTarget>(),
|
ExplosionTarget.Clusters => stream.ReadObject<ClusterTarget>(),
|
||||||
ExplosionTarget.Fire => stream.ReadObject<FireTarget>(),
|
ExplosionTarget.Fire => stream.ReadObject<FireTarget>(),
|
||||||
_ => null,
|
_ => throw new NotImplementedException(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Armageddon.ProjectX
|
namespace Syroot.Worms.Armageddon.ProjectX
|
||||||
{
|
{
|
||||||
@ -49,7 +50,7 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
|
|
||||||
public WeaponThrowAction ThrowAction { get; set; }
|
public WeaponThrowAction ThrowAction { get; set; }
|
||||||
|
|
||||||
public IStyle Style { get; set; }
|
public IStyle? Style { get; set; }
|
||||||
|
|
||||||
public bool AmmunitionOverride { get; set; }
|
public bool AmmunitionOverride { get; set; }
|
||||||
|
|
||||||
@ -59,15 +60,15 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
|
|
||||||
public int WeaponSprite { get; set; }
|
public int WeaponSprite { get; set; }
|
||||||
|
|
||||||
public string NameLong { get; set; }
|
public string NameLong { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string GridImageFile { get; set; }
|
public string GridImageFile { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string GfxDirectoryFile { get; set; }
|
public string GfxDirectoryFile { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string[] SpriteNames { get; set; }
|
public string[] SpriteNames { get; } = new string[5];
|
||||||
|
|
||||||
public bool DelayOverride { get; set; }
|
public bool DelayOverride { get; set; }
|
||||||
|
|
||||||
@ -75,27 +76,27 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
|
|
||||||
public bool UseLibrary { get; set; }
|
public bool UseLibrary { get; set; }
|
||||||
|
|
||||||
public string LibraryName { get; set; }
|
public string LibraryName { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string LibraryWeaponName { get; set; }
|
public string LibraryWeaponName { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string AimSpriteEven { get; set; }
|
public string AimSpriteEven { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string AimSpriteUphill { get; set; }
|
public string AimSpriteUphill { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string AimSpriteDownhill { get; set; }
|
public string AimSpriteDownhill { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string PickSpriteEven { get; set; }
|
public string PickSpriteEven { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string PickSpriteUphill { get; set; }
|
public string PickSpriteUphill { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string PickSpriteDownhill { get; set; }
|
public string PickSpriteDownhill { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string FireSpriteEven { get; set; }
|
public string FireSpriteEven { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string FireSpriteUphill { get; set; }
|
public string FireSpriteUphill { get; set; } = String.Empty;
|
||||||
|
|
||||||
public string FireSpriteDownhill { get; set; }
|
public string FireSpriteDownhill { get; set; } = String.Empty;
|
||||||
|
|
||||||
public bool AimSpriteOverride { get; set; }
|
public bool AimSpriteOverride { get; set; }
|
||||||
|
|
||||||
@ -243,8 +244,9 @@ namespace Syroot.Worms.Armageddon.ProjectX
|
|||||||
NameLong = reader.ReadString(StringCoding.Int32CharCount);
|
NameLong = reader.ReadString(StringCoding.Int32CharCount);
|
||||||
Name = reader.ReadString(StringCoding.Int32CharCount);
|
Name = reader.ReadString(StringCoding.Int32CharCount);
|
||||||
GridImageFile = reader.ReadString(StringCoding.Int32CharCount);
|
GridImageFile = reader.ReadString(StringCoding.Int32CharCount);
|
||||||
GfxDirectoryFile = reader.ReadString(StringCoding.Int32CharCount);
|
GfxDirectoryFile = reader.ReadString(StringCoding.Int32CharCount);
|
||||||
SpriteNames = reader.ReadStrings(5, StringCoding.Int32CharCount);
|
for (int i = 0; i < SpriteNames.Length; i++)
|
||||||
|
SpriteNames[i] = reader.ReadString(StringCoding.Int32CharCount);
|
||||||
DelayOverride = reader.ReadBoolean(BooleanCoding.Dword);
|
DelayOverride = reader.ReadBoolean(BooleanCoding.Dword);
|
||||||
Delay = reader.ReadInt32();
|
Delay = reader.ReadInt32();
|
||||||
|
|
||||||
|
@ -1,71 +1,175 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Runtime.InteropServices;
|
||||||
using Syroot.BinaryData;
|
using System.Text;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.BinaryData;
|
||||||
|
using Syroot.Worms.IO;
|
||||||
namespace Syroot.Worms.Armageddon
|
|
||||||
{
|
namespace Syroot.Worms.Armageddon
|
||||||
/// <summary>
|
{
|
||||||
/// Represents <see cref="MapGeneratorSettings"/> stored in a LEV file.
|
/// <summary>
|
||||||
/// Used by WA and WWP. S. https://worms2d.info/Monochrome_map_(.bit,_.lev).
|
/// Represents <see cref="MapGenSettings"/> stored in a LEV file.
|
||||||
/// </summary>
|
/// Used by WA and WWP. S. https://worms2d.info/Monochrome_map_(.bit,_.lev).
|
||||||
public class GeneratedMap : ILoadableFile, ISaveableFile
|
/// </summary>
|
||||||
{
|
public class GeneratedMap : ILoadableFile, ISaveableFile
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
{
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="GeneratedMap"/> class.
|
/// <summary>
|
||||||
/// </summary>
|
/// Initializes a new instance of the <see cref="GeneratedMap"/> class.
|
||||||
public GeneratedMap() { }
|
/// </summary>
|
||||||
|
public GeneratedMap() { }
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="GeneratedMap"/> class, loading the data from the given
|
/// <summary>
|
||||||
/// <see cref="Stream"/>.
|
/// Initializes a new instance of the <see cref="GeneratedMap"/> class, loading the data from the given
|
||||||
/// </summary>
|
/// <see cref="Stream"/>.
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// </summary>
|
||||||
public GeneratedMap(Stream stream) => Load(stream);
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
|
public GeneratedMap(Stream stream) => Load(stream);
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="GeneratedMap"/> class, loading the data from the given file.
|
/// <summary>
|
||||||
/// </summary>
|
/// Initializes a new instance of the <see cref="GeneratedMap"/> class, loading the data from the given file.
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// </summary>
|
||||||
public GeneratedMap(string fileName) => Load(fileName);
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
|
public GeneratedMap(string fileName) => Load(fileName);
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the <see cref="MapGeneratorSettings"/>.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets the <see cref="MapGenSettings"/>.
|
||||||
public MapGeneratorSettings Settings { get; set; }
|
/// </summary>
|
||||||
|
public MapGenSettings Settings { get; set; }
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Load(Stream stream)
|
/// <inheritdoc/>
|
||||||
{
|
public void Load(Stream stream)
|
||||||
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
{
|
||||||
Settings = reader.ReadStruct<MapGeneratorSettings>();
|
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
}
|
Settings = reader.ReadStruct<MapGenSettings>();
|
||||||
|
}
|
||||||
/// <inheritdoc/>>
|
|
||||||
public void Load(string fileName)
|
/// <inheritdoc/>>
|
||||||
{
|
public void Load(string fileName)
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
{
|
||||||
Load(stream);
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
}
|
Load(stream);
|
||||||
|
}
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Save(Stream stream)
|
/// <inheritdoc/>
|
||||||
{
|
public void Save(Stream stream)
|
||||||
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
{
|
||||||
writer.WriteStruct(Settings);
|
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
}
|
writer.WriteStruct(Settings);
|
||||||
|
}
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Save(string fileName)
|
/// <inheritdoc/>
|
||||||
{
|
public void Save(string fileName)
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
{
|
||||||
Save(stream);
|
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
}
|
Save(stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the required configuration for the land generator to create a map. This structure appears in LEV
|
||||||
|
/// files (being their only content), in BIT files (being the header) and in PNG maps stored by WA (being the data
|
||||||
|
/// of the w2lv or waLV chunks).
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct MapGenSettings
|
||||||
|
{
|
||||||
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>Random seed to generate the land shape (no effect in BIT files).</summary>
|
||||||
|
public int LandSeed;
|
||||||
|
/// <summary>Random seed to generate object placements.</summary>
|
||||||
|
public int ObjectSeed;
|
||||||
|
/// <summary><see langword="true"/> treats the generated map as a cavern, otherwise as an island, depending on
|
||||||
|
/// <see cref="Style"/>.</summary>
|
||||||
|
public bool Cavern;
|
||||||
|
/// <summary>Style of the land generated, dependending on <see cref="Cavern"/>.</summary>
|
||||||
|
public MapGenStyle Style;
|
||||||
|
/// <summary><see langword="true"/> to disable an indestructible border being placed around the map.</summary>
|
||||||
|
public bool NoIndiBorders;
|
||||||
|
/// <summary>Probability percentage of map objects to appear on the land, from 0-100.</summary>
|
||||||
|
public int ObjectPercentage;
|
||||||
|
/// <summary>Probability percentage of bridges to appear on the land, from 0-100.</summary>
|
||||||
|
public int BridgePercentage;
|
||||||
|
/// <summary>Height of the water in percent, from 0-99.</summary>
|
||||||
|
public int WaterLevel;
|
||||||
|
/// <summary>Version of the structure, determining the available <see cref="SoilTextureIndex"/> values in Worms
|
||||||
|
/// Armageddon.</summary>
|
||||||
|
public MapGenVersion Version;
|
||||||
|
/// <summary>Texture index determining the style of the generated map.</summary>
|
||||||
|
public MapGenSoil SoilTextureIndex;
|
||||||
|
/// <summary>Water color used for the map (deprecated, used only in Worms Armageddon 1.0).</summary>
|
||||||
|
public int WaterColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the possible land styles.
|
||||||
|
/// </summary>
|
||||||
|
public enum MapGenStyle : int
|
||||||
|
{
|
||||||
|
/// <summary>A single island or a cavern.</summary>
|
||||||
|
OneIslandOrCavern,
|
||||||
|
/// <summary>Two islands or a double-layer cavern.</summary>
|
||||||
|
TwoIslandsOrCaverns,
|
||||||
|
/// <summary>One flat island or a cavern open at the bottom.</summary>
|
||||||
|
OneFlatIslandOrCavernOpenBottom,
|
||||||
|
/// <summary>Two flat islands or a cavern open at the left or right.</summary>
|
||||||
|
TwoFlatIslandsOrCavernOpenLeftRight
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the possible versions which can be stored in the <see cref="MapGenSettings.Version"/> field.
|
||||||
|
/// </summary>
|
||||||
|
public enum MapGenVersion : short
|
||||||
|
{
|
||||||
|
/// <summary>Game version 3.0. Last soil texture indices are 26 = Tribal, 27 = Tribal, 28 = Urban. Additionally,
|
||||||
|
/// floor and ceiling trimming is more severe.</summary>
|
||||||
|
Version_3_0_0_0 = -1,
|
||||||
|
/// <summary>Game version 3.5. Last soil texture indices are 26 = Tribal, 27 = Urban, 28 = undefined.</summary>
|
||||||
|
Version_3_5_0_0 = 0,
|
||||||
|
/// <summary>Game version 3.6.26.4. Last soil texture indices are 26 = Tools, 27 = Tribal, Urban = 28 (same as
|
||||||
|
/// in WWP).</summary>
|
||||||
|
Version_3_6_26_4 = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the possible soil styles. In case of Worms Armageddon, this list matches
|
||||||
|
/// <see cref="MapGenVersion.Version_3_6_26_4"/>.
|
||||||
|
/// </summary>
|
||||||
|
public enum MapGenSoil : short
|
||||||
|
{
|
||||||
|
ClassicBeach,
|
||||||
|
ClassicDesert,
|
||||||
|
ClassicFarm,
|
||||||
|
ClassicForest,
|
||||||
|
ClassicHell,
|
||||||
|
Art,
|
||||||
|
Cheese,
|
||||||
|
Construction,
|
||||||
|
Desert,
|
||||||
|
Dungeon,
|
||||||
|
Easter,
|
||||||
|
Forest,
|
||||||
|
Fruit,
|
||||||
|
Gulf,
|
||||||
|
Hell,
|
||||||
|
Hospital,
|
||||||
|
Jungle,
|
||||||
|
Manhattan,
|
||||||
|
Medieval,
|
||||||
|
Music,
|
||||||
|
Pirate,
|
||||||
|
Snow,
|
||||||
|
Space,
|
||||||
|
Sports,
|
||||||
|
Tentacle,
|
||||||
|
Time,
|
||||||
|
Tools,
|
||||||
|
Tribal,
|
||||||
|
Urban
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,167 +1,246 @@
|
|||||||
using System.Drawing;
|
using System;
|
||||||
using System.IO;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Drawing;
|
||||||
using Syroot.BinaryData;
|
using System.IO;
|
||||||
using Syroot.Worms.Core.IO;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
namespace Syroot.Worms.Armageddon
|
using Syroot.BinaryData;
|
||||||
{
|
using Syroot.Worms.IO;
|
||||||
/// <summary>
|
|
||||||
/// Represents map configuration stored by the land generator in LAND.DAT files.
|
namespace Syroot.Worms.Armageddon
|
||||||
/// Used by WA. S. https://worms2d.info/Land_Data_file.
|
{
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public class LandData : ILoadableFile, ISaveableFile
|
/// Represents map configuration stored by the land generator in LAND.DAT files.
|
||||||
{
|
/// Used by WA and WWP. S. https://worms2d.info/Land_Data_file.
|
||||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
/// </summary>
|
||||||
|
public class LandData : ILoadableFile, ISaveableFile
|
||||||
private const int _signature = 0x1A444E4C; // "LND", 0x1A
|
{
|
||||||
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
|
||||||
|
private bool _alignImgData;
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="LandData"/> class.
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
/// </summary>
|
|
||||||
public LandData() { }
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="LandData"/> class.
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given
|
public LandData() { }
|
||||||
/// <see cref="Stream"/>.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given
|
||||||
public LandData(Stream stream) => Load(stream);
|
/// <see cref="Stream"/>.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given file.
|
public LandData(Stream stream) => Load(stream);
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// <summary>
|
||||||
public LandData(string fileName) => Load(fileName);
|
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given file.
|
||||||
|
/// </summary>
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
|
public LandData(string fileName) => Load(fileName);
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the size of the landscape in pixels.
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
/// </summary>
|
|
||||||
public Size Size { get; set; }
|
/// <summary>
|
||||||
|
/// Gets or sets the format version of the file.
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// Gets or sets a value indicating whether an indestructible top border will be enabled.
|
public LandDataVersion Version { get; set; }
|
||||||
/// </summary>
|
|
||||||
public bool TopBorder { get; set; }
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether image data is aligned by 4-bytes. This is the case only for WWP.
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// Gets or sets the height of the water in pixels.
|
public bool AlignImgData
|
||||||
/// </summary>
|
{
|
||||||
public int WaterHeight { get; set; }
|
get => _alignImgData;
|
||||||
|
set => _alignImgData = value;
|
||||||
/// <summary>
|
}
|
||||||
/// Gets or sets an unknown value.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public int Unknown { get; set; }
|
/// Gets or sets the size of the landscape in pixels.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public Size Size { get; set; }
|
||||||
/// Gets or sets an array of coordinates at which objects can be placed.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public Point[] ObjectLocations { get; set; }
|
/// Gets or sets a value indicating whether an indestructible top border will be enabled.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public bool TopBorder { get; set; }
|
||||||
/// Gets or sets the visual foreground image.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public Img Foreground { get; set; }
|
/// Gets or sets the height of the water in pixels.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public int WaterHeight { get; set; }
|
||||||
/// Gets or sets the collision mask of the landscape.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public Img CollisionMask { get; set; }
|
/// Gets or sets an unknown value only available in newer W:A versions (apparently since 3.6.28.0).
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public int? Unknown { get; set; }
|
||||||
/// Gets or sets the visual background image.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public Img Background { get; set; }
|
/// Gets or sets an array of coordinates at which objects can be placed.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public IList<Point> ObjectLocations { get; set; } = new List<Point>();
|
||||||
/// Gets or sets the path to the land image file.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public string LandTexturePath { get; set; }
|
/// Gets or sets the visual foreground image.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public Img Foreground { get; set; } = new Img();
|
||||||
/// Gets or sets the path to the Water.dir file.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public string WaterDirPath { get; set; }
|
/// Gets or sets the collision mask of the landscape.
|
||||||
|
/// </summary>
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
public Img CollisionMask { get; set; } = new Img();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <summary>
|
||||||
public void Load(Stream stream)
|
/// Gets or sets the visual background image.
|
||||||
{
|
/// </summary>
|
||||||
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
public Img Background { get; set; } = new Img();
|
||||||
|
|
||||||
// Read the header.
|
/// <summary>
|
||||||
if (reader.ReadInt32() != _signature)
|
/// Gets or sets the path to the land image file.
|
||||||
throw new InvalidDataException("Invalid LND file signature.");
|
/// </summary>
|
||||||
int fileSize = reader.ReadInt32();
|
public string LandTexturePath { get; set; } = String.Empty;
|
||||||
|
|
||||||
// Read the data.
|
/// <summary>
|
||||||
Size = reader.ReadStruct<Size>();
|
/// Gets or sets the path to the Water.dir file.
|
||||||
TopBorder = reader.ReadBoolean(BooleanCoding.Dword);
|
/// </summary>
|
||||||
WaterHeight = reader.ReadInt32();
|
public string WaterDirPath { get; set; } = String.Empty;
|
||||||
Unknown = reader.ReadInt32();
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
// Read the possible object coordinate array.
|
|
||||||
ObjectLocations = reader.ReadStructs<Point>(reader.ReadInt32());
|
/// <inheritdoc/>
|
||||||
|
public void Load(Stream stream)
|
||||||
// Read the image data.
|
{
|
||||||
Foreground = reader.Load<Img>();
|
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
CollisionMask = reader.Load<Img>();
|
|
||||||
Background = reader.Load<Img>();
|
int readLocationCount()
|
||||||
|
{
|
||||||
// Read the file paths.
|
int oldLocationCount = reader.ReadInt32();
|
||||||
LandTexturePath = reader.ReadString(StringCoding.ByteCharCount);
|
int oldImgStart = oldLocationCount * Unsafe.SizeOf<Point>();
|
||||||
WaterDirPath = reader.ReadString(StringCoding.ByteCharCount);
|
|
||||||
}
|
// Check whether the old IMG start would be invalid.
|
||||||
|
bool isNew = false;
|
||||||
/// <inheritdoc/>
|
using (reader.TemporarySeek(oldImgStart))
|
||||||
public void Load(string fileName)
|
isNew = reader.EndOfStream || reader.ReadUInt32() != Img.Signature;
|
||||||
{
|
if (isNew)
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
{
|
||||||
Load(stream);
|
Unknown = oldLocationCount;
|
||||||
}
|
return reader.ReadInt32();
|
||||||
|
}
|
||||||
/// <inheritdoc/>
|
else
|
||||||
public void Save(Stream stream)
|
{
|
||||||
{
|
Unknown = null;
|
||||||
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
return oldLocationCount;
|
||||||
|
}
|
||||||
// Write the header.
|
}
|
||||||
writer.Write(_signature);
|
|
||||||
uint fileSizeOffset = writer.ReserveOffset();
|
Img readImage(ref bool aligned)
|
||||||
|
{
|
||||||
// Write the data.
|
long imgStart = reader.Position;
|
||||||
writer.WriteStruct(Size);
|
Img img = new Img(reader.BaseStream, aligned);
|
||||||
writer.Write(TopBorder, BooleanCoding.Dword);
|
|
||||||
writer.Write(WaterHeight);
|
// If reading the next image fails, the previous data was aligned and must be reread.
|
||||||
writer.Write(Unknown);
|
if (!aligned)
|
||||||
|
{
|
||||||
// Write the possible object coordinate array.
|
if (reader.ReadUInt32() == Img.Signature)
|
||||||
writer.Write(ObjectLocations.Length);
|
{
|
||||||
writer.WriteStructs(ObjectLocations);
|
reader.Position -= sizeof(uint);
|
||||||
|
}
|
||||||
// Write the image data.
|
else
|
||||||
Foreground.Save(writer.BaseStream);
|
{
|
||||||
CollisionMask.Save(writer.BaseStream);
|
reader.Position = imgStart;
|
||||||
Background.Save(writer.BaseStream);
|
img = new Img(reader.BaseStream, true);
|
||||||
|
aligned = true;
|
||||||
// Write the file paths.
|
}
|
||||||
writer.Write(LandTexturePath, StringCoding.ByteCharCount);
|
}
|
||||||
writer.Write(WaterDirPath, StringCoding.ByteCharCount);
|
|
||||||
|
return img;
|
||||||
writer.SatisfyOffset(fileSizeOffset, (int)writer.Position);
|
}
|
||||||
}
|
|
||||||
|
// Read the header.
|
||||||
/// <inheritdoc/>
|
Version = reader.ReadEnum<LandDataVersion>(true);
|
||||||
public void Save(string fileName)
|
int fileSize = reader.ReadInt32();
|
||||||
{
|
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
// Read the data.
|
||||||
Save(stream);
|
Size = reader.ReadStruct<Size>();
|
||||||
}
|
TopBorder = reader.ReadBoolean(BooleanCoding.Dword);
|
||||||
}
|
WaterHeight = reader.ReadInt32();
|
||||||
}
|
|
||||||
|
// Read the object locations.
|
||||||
|
int locationCount = readLocationCount();
|
||||||
|
ObjectLocations = new List<Point>(locationCount);
|
||||||
|
for (int i = 0; i < locationCount; i++)
|
||||||
|
ObjectLocations.Add(reader.ReadStruct<Point>());
|
||||||
|
|
||||||
|
// Read the image data, which alignment must be detected by trial-and-error.
|
||||||
|
_alignImgData = Version == LandDataVersion.WormsWorldPartyAqua;
|
||||||
|
long foreImgLocation = reader.Position;
|
||||||
|
Foreground = readImage(ref _alignImgData);
|
||||||
|
CollisionMask = readImage(ref _alignImgData);
|
||||||
|
Background = new Img(reader.BaseStream, _alignImgData);
|
||||||
|
|
||||||
|
// Read the file paths.
|
||||||
|
LandTexturePath = reader.ReadString(StringCoding.ByteCharCount);
|
||||||
|
WaterDirPath = reader.ReadString(StringCoding.ByteCharCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Load(string fileName)
|
||||||
|
{
|
||||||
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
Load(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Save(Stream stream)
|
||||||
|
{
|
||||||
|
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
|
|
||||||
|
// Write the header.
|
||||||
|
writer.WriteEnum(Version, true);
|
||||||
|
uint fileSizeOffset = writer.ReserveOffset();
|
||||||
|
|
||||||
|
// Write the data.
|
||||||
|
writer.WriteStruct(Size);
|
||||||
|
writer.Write(TopBorder, BooleanCoding.Dword);
|
||||||
|
writer.Write(WaterHeight);
|
||||||
|
if (Unknown.HasValue)
|
||||||
|
writer.Write(Unknown.Value);
|
||||||
|
|
||||||
|
// Write the possible object coordinate array.
|
||||||
|
writer.Write(ObjectLocations.Count);
|
||||||
|
for (int i = 0; i < ObjectLocations.Count; i++)
|
||||||
|
writer.WriteStruct(ObjectLocations[i]);
|
||||||
|
|
||||||
|
// Write the image data.
|
||||||
|
Foreground.Save(writer.BaseStream, _alignImgData);
|
||||||
|
CollisionMask.Save(writer.BaseStream, _alignImgData);
|
||||||
|
Background.Save(writer.BaseStream, _alignImgData);
|
||||||
|
|
||||||
|
// Write the file paths.
|
||||||
|
writer.Write(LandTexturePath, StringCoding.ByteCharCount);
|
||||||
|
writer.Write(WaterDirPath, StringCoding.ByteCharCount);
|
||||||
|
|
||||||
|
writer.SatisfyOffset(fileSizeOffset, (uint)writer.Position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Save(string fileName)
|
||||||
|
{
|
||||||
|
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
|
Save(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the version of <see cref="LandData"/> files, encoded as the signature.
|
||||||
|
/// </summary>
|
||||||
|
public enum LandDataVersion : uint
|
||||||
|
{
|
||||||
|
/// <summary>Found in W2, WA, WWP, and OW.</summary>
|
||||||
|
Team17 = 0x1A444E4C, // "LND\x1A"
|
||||||
|
/// <summary>Found only in WWPA.</summary>
|
||||||
|
WormsWorldPartyAqua = 0x1B444E4C // "LND\x1B"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,158 +0,0 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.Armageddon
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the required configuration for the land generator to create a map. This structure appears in LEV
|
|
||||||
/// files (being their only content), in BIT files (being the header) and in PNG maps stored by WA (being the data
|
|
||||||
/// of the w2lv or waLV chunks).
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct MapGeneratorSettings
|
|
||||||
{
|
|
||||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The random seed to generate the land shape (no effect in BIT files).
|
|
||||||
/// </summary>
|
|
||||||
public int LandSeed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The random seed to generate object placements.
|
|
||||||
/// </summary>
|
|
||||||
public int ObjectSeed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <c>true</c> treats the generated map as a cavern, otherwise as an island, depending on <see cref="Style"/>.
|
|
||||||
/// </summary>
|
|
||||||
[MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public bool Cavern;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The style of the land generated, dependending on <see cref="Cavern"/>
|
|
||||||
/// </summary>
|
|
||||||
public MapGeneratorStyle Style;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <c>true</c> to disable an indestructible border being placed around the map.
|
|
||||||
/// </summary>
|
|
||||||
[MarshalAs(UnmanagedType.Bool)]
|
|
||||||
public bool NoIndestructibleBorders;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The probability percentage of map objects to appear on the land, from 0-100.
|
|
||||||
/// </summary>
|
|
||||||
public int ObjectPercentage;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The probability percentage of bridges to appear on the land, from 0-100.
|
|
||||||
/// </summary>
|
|
||||||
public int BridgePercentage;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The height of the water in percent, from 0-99.
|
|
||||||
/// </summary>
|
|
||||||
public int WaterLevel;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The version of the structure, determining the available <see cref="SoilTextureIndex"/> values in Worms
|
|
||||||
/// Armageddon.
|
|
||||||
/// </summary>
|
|
||||||
public MapGeneratorVersion Version;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the texture index determining the style of the generated map.
|
|
||||||
/// </summary>
|
|
||||||
public MapGeneratorSoil SoilTextureIndex;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the water color used for the map (deprecated, used only in Worms Armageddon 1.0).
|
|
||||||
/// </summary>
|
|
||||||
public int WaterColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the possible land styles.
|
|
||||||
/// </summary>
|
|
||||||
public enum MapGeneratorStyle : int
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a single island or a cavern.
|
|
||||||
/// </summary>
|
|
||||||
OneIslandOrCavern,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents two islands or a double-layer cavern.
|
|
||||||
/// </summary>
|
|
||||||
TwoIslandsOrCaverns,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents one flat island or a cavern open at the bottom.
|
|
||||||
/// </summary>
|
|
||||||
OneFlatIslandOrCavernOpenBottom,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents two flat islands or a cavern open at the left or right.
|
|
||||||
/// </summary>
|
|
||||||
TwoFlatIslandsOrCavernOpenLeftRight
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the possible versions which can be stored in the <see cref="MapGeneratorSettings.Version"/> field.
|
|
||||||
/// </summary>
|
|
||||||
public enum MapGeneratorVersion : short
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Game version 3.0. Last soil texture indices are 26 = Tribal, 27 = Tribal, 28 = Urban. Additionally, floor
|
|
||||||
/// and ceiling trimming is more severe.
|
|
||||||
/// </summary>
|
|
||||||
Version_3_0_0_0 = -1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Game version 3.5. Last soil texture indices are 26 = Tribal, 27 = Urban, 28 = undefined.
|
|
||||||
/// </summary>
|
|
||||||
Version_3_5_0_0 = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Game version 3.6.26.4. Last soil texture indices are 26 = Tools, 27 = Tribal, Urban = 28 (same as in WWP).
|
|
||||||
/// </summary>
|
|
||||||
Version_3_6_26_4 = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the possible soil styles. In case of Worms Armageddon, this list matches
|
|
||||||
/// <see cref="MapGeneratorVersion.Version_3_6_26_4"/>.
|
|
||||||
/// </summary>
|
|
||||||
public enum MapGeneratorSoil : short
|
|
||||||
{
|
|
||||||
ClassicBeach,
|
|
||||||
ClassicDesert,
|
|
||||||
ClassicFarm,
|
|
||||||
ClassicForest,
|
|
||||||
ClassicHell,
|
|
||||||
Art,
|
|
||||||
Cheese,
|
|
||||||
Construction,
|
|
||||||
Desert,
|
|
||||||
Dungeon,
|
|
||||||
Easter,
|
|
||||||
Forest,
|
|
||||||
Fruit,
|
|
||||||
Gulf,
|
|
||||||
Hell,
|
|
||||||
Hospital,
|
|
||||||
Jungle,
|
|
||||||
Manhattan,
|
|
||||||
Medieval,
|
|
||||||
Music,
|
|
||||||
Pirate,
|
|
||||||
Snow,
|
|
||||||
Space,
|
|
||||||
Sports,
|
|
||||||
Tentacle,
|
|
||||||
Time,
|
|
||||||
Tools,
|
|
||||||
Tribal,
|
|
||||||
Urban
|
|
||||||
}
|
|
||||||
}
|
|
@ -145,7 +145,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the stockpiling mode of weapon armory between rounds.
|
/// Represents the stockpiling mode of weapon armory between rounds.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum StockpilingMode : byte
|
public enum Stockpiling : byte
|
||||||
{
|
{
|
||||||
/// <summary>Each round starts with the exact amount of weapons set in the scheme.</summary>
|
/// <summary>Each round starts with the exact amount of weapons set in the scheme.</summary>
|
||||||
Off,
|
Off,
|
1136
src/library/Syroot.Worms.Armageddon/Scheme.ExtendedOptions.cs
Normal file
1136
src/library/Syroot.Worms.Armageddon/Scheme.ExtendedOptions.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,5 @@
|
|||||||
using System.Collections;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
@ -14,30 +15,32 @@ namespace Syroot.Worms.Armageddon
|
|||||||
public class WeaponList : IReadOnlyCollection<SchemeWeapon>
|
public class WeaponList : IReadOnlyCollection<SchemeWeapon>
|
||||||
{
|
{
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
|
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
|
||||||
private readonly SchemeWeapon[] _list = new SchemeWeapon[64];
|
private readonly SchemeWeapon[] _array = new SchemeWeapon[64];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a reference to the weapon with the given <paramref name="index"/>.
|
/// Gets a reference to the weapon with the given <paramref name="index"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The index of the weapon to access.</param>
|
/// <param name="name">The index of the weapon to access.</param>
|
||||||
/// <returns>A reference to the <see cref="SchemeWeapon"/>.</returns>
|
/// <returns>A reference to the <see cref="SchemeWeapon"/>.</returns>
|
||||||
public ref SchemeWeapon this[int index] => ref _list[index];
|
public ref SchemeWeapon this[int index] => ref _array[index];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a reference to the weapon with the given <paramref name="name"/>.
|
/// Gets a reference to the weapon with the given <paramref name="name"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The <see cref="Weapon"/> of the weapon to access.</param>
|
/// <param name="name">The <see cref="Weapon"/> of the weapon to access.</param>
|
||||||
/// <returns>A reference to the <see cref="SchemeWeapon"/>.</returns>
|
/// <returns>A reference to the <see cref="SchemeWeapon"/>.</returns>
|
||||||
public ref SchemeWeapon this[Weapon name] => ref _list[(int)name];
|
public ref SchemeWeapon this[Weapon name] => ref _array[(int)name];
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public int Count => _list.Length;
|
public int Count => _array.Length;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IEnumerator<SchemeWeapon> GetEnumerator() => ((IEnumerable<SchemeWeapon>)_list).GetEnumerator();
|
public IEnumerator<SchemeWeapon> GetEnumerator() => ((IEnumerable<SchemeWeapon>)_array).GetEnumerator();
|
||||||
|
|
||||||
|
internal Span<SchemeWeapon> AsSpan(int length) => _array.AsSpan(0, length);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => _array.GetEnumerator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core;
|
using Syroot.Worms.Core;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Armageddon
|
namespace Syroot.Worms.Armageddon
|
||||||
{
|
{
|
||||||
@ -38,8 +38,8 @@ namespace Syroot.Worms.Armageddon
|
|||||||
private byte _roundTimeMinutes;
|
private byte _roundTimeMinutes;
|
||||||
private byte _roundTimeSeconds;
|
private byte _roundTimeSeconds;
|
||||||
|
|
||||||
private SchemeExtendedOptions _extended = SchemeExtendedOptions.Default;
|
private ExtendedOptions _extended = ExtendedOptions.Default;
|
||||||
private ushort _rwVersionOverride;
|
private ushort _rwVersion;
|
||||||
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the delay in seconds between each team's turn to allow relaxed switching of seats.
|
/// Gets or sets the delay in seconds between each team's turn to allow relaxed switching of seats.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte HotSeatDelay { get; set; }
|
public byte HotSeatTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the time in seconds available for a worm to retreat after using a weapon which ends the turn
|
/// Gets or sets the time in seconds available for a worm to retreat after using a weapon which ends the turn
|
||||||
@ -139,7 +139,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether significant turns will be replayed in offline games.
|
/// Gets or sets a value indicating whether significant turns will be replayed in offline games.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AutomaticReplays { get; set; }
|
public bool Replays { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the percentual amount of fall damage applied, relative to normal fall damage being 100%. Valid
|
/// Gets or sets the percentual amount of fall damage applied, relative to normal fall damage being 100%. Valid
|
||||||
@ -155,7 +155,6 @@ namespace Syroot.Worms.Armageddon
|
|||||||
{
|
{
|
||||||
if (value < 0 || value > 508)
|
if (value < 0 || value > 508)
|
||||||
throw new ArgumentOutOfRangeException(nameof(value), "Fall damage must be between 0-508.");
|
throw new ArgumentOutOfRangeException(nameof(value), "Fall damage must be between 0-508.");
|
||||||
|
|
||||||
_fallDamage = value >> 2 << 2;
|
_fallDamage = value >> 2 << 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,12 +174,12 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating the stockpiling of armory between game rounds.
|
/// Gets or sets a value indicating the stockpiling of armory between game rounds.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public StockpilingMode StockpilingMode { get; set; }
|
public Stockpiling Stockpiling { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating the worm selection order determining the next worm to be played.
|
/// Gets or sets a value indicating the worm selection order determining the next worm to be played.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WormSelect WormSelectMode { get; set; }
|
public WormSelect WormSelect { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating the action triggered when Sudden Death starts.
|
/// Gets or sets a value indicating the action triggered when Sudden Death starts.
|
||||||
@ -218,7 +217,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// Gets or sets the percentual probability of a weapon crate to drop between turns. Negative values might crash
|
/// Gets or sets the percentual probability of a weapon crate to drop between turns. Negative values might crash
|
||||||
/// the game.
|
/// the game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sbyte WeaponCrateProbability { get; set; }
|
public sbyte WeaponCrateProb { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether donor cards can spawn upon a worm's death.
|
/// Gets or sets a value indicating whether donor cards can spawn upon a worm's death.
|
||||||
@ -229,7 +228,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// Gets or sets the percentual probability of a health crate to drop between turns. Negative values might crash
|
/// Gets or sets the percentual probability of a health crate to drop between turns. Negative values might crash
|
||||||
/// the game.
|
/// the game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sbyte HealthCrateProbability { get; set; }
|
public sbyte HealthCrateProb { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the amount of health included in a health crate added to the collecting worm's energy.
|
/// Gets or sets the amount of health included in a health crate added to the collecting worm's energy.
|
||||||
@ -240,7 +239,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// Gets or sets the percentual probability of a utility crate to drop between turns. Negative values might
|
/// Gets or sets the percentual probability of a utility crate to drop between turns. Negative values might
|
||||||
/// crash the game.
|
/// crash the game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sbyte UtilityCrateProbability { get; set; }
|
public sbyte UtilityCrateProb { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the type of objects which can be placed on the map.
|
/// Gets or sets the type of objects which can be placed on the map.
|
||||||
@ -273,7 +272,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the number of seconds a mine requires to explode. Can be 1-3 and 5-127 seconds.
|
/// Gets or sets the number of seconds a mine requires to explode. Can be 0-3 and 5-127 seconds.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <exception cref="ArgumentOutOfRangeException">Value is not between 0-127 or 4.</exception>
|
/// <exception cref="ArgumentOutOfRangeException">Value is not between 0-127 or 4.</exception>
|
||||||
public byte MineDelay
|
public byte MineDelay
|
||||||
@ -289,7 +288,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the mine fuse will be randomly chosen between fractions of 1 to 3
|
/// Gets or sets a value indicating whether the mine fuse will be randomly chosen between fractions of 1 to 3
|
||||||
/// seconds. If <c>true</c>, the <see cref="MineDelay"/> setting will be ignored.
|
/// seconds. If <see langword="true"/>, the <see cref="MineDelay"/> setting will be ignored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool MineDelayRandom { get; set; }
|
public bool MineDelayRandom { get; set; }
|
||||||
|
|
||||||
@ -325,7 +324,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the turn time is unlimited. If <c>true</c>, the
|
/// Gets or sets a value indicating whether the turn time is unlimited. If <see langword="true"/>, the
|
||||||
/// <see cref="TurnTime"/> setting will be ignored.
|
/// <see cref="TurnTime"/> setting will be ignored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TurnTimeInfinite { get; set; }
|
public bool TurnTimeInfinite { get; set; }
|
||||||
@ -393,40 +392,40 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether terrain cannot be destroyed by explosions.
|
/// Gets or sets a value indicating whether terrain cannot be destroyed by explosions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IndestructibleLand { get; set; }
|
public bool IndiLand { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the Grenade weapon is more powerful.
|
/// Gets or sets a value indicating whether the Grenade weapon is more powerful.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UpgradedGrenade { get; set; }
|
public bool UpgradeGrenade { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the Shotgun weapon shoots twice for each of the two shots.
|
/// Gets or sets a value indicating whether the Shotgun weapon shoots twice for each of the two shots.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UpgradedShotgun { get; set; }
|
public bool UpgradeShotgun { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether cluster weapon explode into more clusters.
|
/// Gets or sets a value indicating whether cluster weapon explode into more clusters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UpgradedCluster { get; set; }
|
public bool UpgradeCluster { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the Longbow weapon is more powerful.
|
/// Gets or sets a value indicating whether the Longbow weapon is more powerful.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UpgradedLongbow { get; set; }
|
public bool UpgradeLongbow { get; set; }
|
||||||
|
|
||||||
|
// ---- Weapons ----
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether team weapons will be given to the teams, overriding the default
|
/// Gets or sets a value indicating whether team weapons will be given to the teams, overriding the default
|
||||||
/// weapon settings for them.
|
/// weapon settings for them.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool EnableTeamWeapons { get; set; }
|
public bool TeamWeapons { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether super weapons can be collected from crates.
|
/// Gets or sets a value indicating whether super weapons can be collected from crates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool EnableSuperWeapons { get; set; }
|
public bool SuperWeapons { get; set; }
|
||||||
|
|
||||||
// ---- Weapons ----
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the list of <see cref="SchemeWeapon"/> instances, of which each weapon can be accessed by index or
|
/// Gets the list of <see cref="SchemeWeapon"/> instances, of which each weapon can be accessed by index or
|
||||||
@ -440,7 +439,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets <see cref="SchemeVersion.Version3"/> or RubberWorm settings.
|
/// Gets or sets <see cref="SchemeVersion.Version3"/> or RubberWorm settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ref SchemeExtendedOptions Extended => ref _extended;
|
public ref ExtendedOptions Extended => ref _extended;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets trailing bytes which could not be parsed and will be attached when saving the scheme anew.
|
/// Gets or sets trailing bytes which could not be parsed and will be attached when saving the scheme anew.
|
||||||
@ -454,12 +453,12 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Configurable with the /version command in RubberWorm.
|
/// <remarks>Configurable with the /version command in RubberWorm.
|
||||||
/// S. http://worms2d.info/List_of_Worms_Armageddon_logic_versions.</remarks>
|
/// S. http://worms2d.info/List_of_Worms_Armageddon_logic_versions.</remarks>
|
||||||
public ushort RwVersionOverride
|
public ushort RwVersion
|
||||||
{
|
{
|
||||||
get => _rwVersionOverride;
|
get => _rwVersion;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_rwVersionOverride = value;
|
_rwVersion = value;
|
||||||
|
|
||||||
// Configure V3 settings according to selected version.
|
// Configure V3 settings according to selected version.
|
||||||
void setBattyRope1To2() => Extended.BattyRope = true;
|
void setBattyRope1To2() => Extended.BattyRope = true;
|
||||||
@ -535,7 +534,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
Extended.WormSelectKeepHotSeat = true;
|
Extended.WormSelectKeepHotSeat = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (_rwVersionOverride)
|
switch (_rwVersion)
|
||||||
{
|
{
|
||||||
case 11: // 3.5 Beta 3pre15[BattyRope1]
|
case 11: // 3.5 Beta 3pre15[BattyRope1]
|
||||||
case 12: // 3.5 Beta 3pre15[BattyRope2]
|
case 12: // 3.5 Beta 3pre15[BattyRope2]
|
||||||
@ -845,30 +844,29 @@ namespace Syroot.Worms.Armageddon
|
|||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override bool Equals(object obj) => Equals(obj as Scheme);
|
public override bool Equals(object? obj) => Equals(obj as Scheme);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool Equals(Scheme other)
|
public bool Equals(Scheme? other)
|
||||||
=> other == this
|
=> other != null
|
||||||
|| other != null
|
|
||||||
&& Version == other.Version
|
&& Version == other.Version
|
||||||
&& HotSeatDelay == other.HotSeatDelay
|
&& HotSeatTime == other.HotSeatTime
|
||||||
&& RetreatTime == other.RetreatTime
|
&& RetreatTime == other.RetreatTime
|
||||||
&& RetreatTimeRope == other.RetreatTimeRope
|
&& RetreatTimeRope == other.RetreatTimeRope
|
||||||
&& ShowRoundTime == other.ShowRoundTime
|
&& ShowRoundTime == other.ShowRoundTime
|
||||||
&& AutomaticReplays == other.AutomaticReplays
|
&& Replays == other.Replays
|
||||||
&& FallDamage == other.FallDamage
|
&& FallDamage == other.FallDamage
|
||||||
&& ArtilleryMode == other.ArtilleryMode
|
&& ArtilleryMode == other.ArtilleryMode
|
||||||
&& SchemeEditor == other.SchemeEditor
|
&& SchemeEditor == other.SchemeEditor
|
||||||
&& StockpilingMode == other.StockpilingMode
|
&& Stockpiling == other.Stockpiling
|
||||||
&& WormSelectMode == other.WormSelectMode
|
&& WormSelect == other.WormSelect
|
||||||
&& SuddenDeathEvent == other.SuddenDeathEvent
|
&& SuddenDeathEvent == other.SuddenDeathEvent
|
||||||
&& WaterRiseRate == other.WaterRiseRate
|
&& WaterRiseRate == other.WaterRiseRate
|
||||||
&& WeaponCrateProbability == other.WeaponCrateProbability
|
&& WeaponCrateProb == other.WeaponCrateProb
|
||||||
&& DonorCards == other.DonorCards
|
&& DonorCards == other.DonorCards
|
||||||
&& HealthCrateProbability == other.HealthCrateProbability
|
&& HealthCrateProb == other.HealthCrateProb
|
||||||
&& HealthCrateEnergy == other.HealthCrateEnergy
|
&& HealthCrateEnergy == other.HealthCrateEnergy
|
||||||
&& UtilityCrateProbability == other.UtilityCrateProbability
|
&& UtilityCrateProb == other.UtilityCrateProb
|
||||||
&& ObjectTypes == other.ObjectTypes
|
&& ObjectTypes == other.ObjectTypes
|
||||||
&& ObjectCount == other.ObjectCount
|
&& ObjectCount == other.ObjectCount
|
||||||
&& MineDelay == other.MineDelay
|
&& MineDelay == other.MineDelay
|
||||||
@ -885,40 +883,40 @@ namespace Syroot.Worms.Armageddon
|
|||||||
&& AquaSheep == other.AquaSheep
|
&& AquaSheep == other.AquaSheep
|
||||||
&& SheepHeaven == other.SheepHeaven
|
&& SheepHeaven == other.SheepHeaven
|
||||||
&& GodWorms == other.GodWorms
|
&& GodWorms == other.GodWorms
|
||||||
&& IndestructibleLand == other.IndestructibleLand
|
&& IndiLand == other.IndiLand
|
||||||
&& UpgradedGrenade == other.UpgradedGrenade
|
&& UpgradeGrenade == other.UpgradeGrenade
|
||||||
&& UpgradedShotgun == other.UpgradedShotgun
|
&& UpgradeShotgun == other.UpgradeShotgun
|
||||||
&& UpgradedCluster == other.UpgradedCluster
|
&& UpgradeCluster == other.UpgradeCluster
|
||||||
&& UpgradedLongbow == other.UpgradedLongbow
|
&& UpgradeLongbow == other.UpgradeLongbow
|
||||||
&& EnableTeamWeapons == other.EnableTeamWeapons
|
&& TeamWeapons == other.TeamWeapons
|
||||||
&& EnableSuperWeapons == other.EnableSuperWeapons
|
&& SuperWeapons == other.SuperWeapons
|
||||||
&& Weapons.SequenceEqual(other.Weapons)
|
&& Weapons.SequenceEqual(other.Weapons)
|
||||||
&& Extended.Equals(ref other.Extended)
|
&& Extended.Equals(other.Extended)
|
||||||
&& Attachment.SequenceEqual(other.Attachment)
|
&& Attachment.SequenceEqual(other.Attachment)
|
||||||
&& RwVersionOverride == other.RwVersionOverride;
|
&& RwVersion == other.RwVersion;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
HashCode hash = new HashCode();
|
HashCode hash = new HashCode();
|
||||||
hash.Add(Version);
|
hash.Add(Version);
|
||||||
hash.Add(HotSeatDelay);
|
hash.Add(HotSeatTime);
|
||||||
hash.Add(RetreatTime);
|
hash.Add(RetreatTime);
|
||||||
hash.Add(RetreatTimeRope);
|
hash.Add(RetreatTimeRope);
|
||||||
hash.Add(ShowRoundTime);
|
hash.Add(ShowRoundTime);
|
||||||
hash.Add(AutomaticReplays);
|
hash.Add(Replays);
|
||||||
hash.Add(FallDamage);
|
hash.Add(FallDamage);
|
||||||
hash.Add(ArtilleryMode);
|
hash.Add(ArtilleryMode);
|
||||||
hash.Add(SchemeEditor);
|
hash.Add(SchemeEditor);
|
||||||
hash.Add(StockpilingMode);
|
hash.Add(Stockpiling);
|
||||||
hash.Add(WormSelectMode);
|
hash.Add(WormSelect);
|
||||||
hash.Add(SuddenDeathEvent);
|
hash.Add(SuddenDeathEvent);
|
||||||
hash.Add(WaterRiseRate);
|
hash.Add(WaterRiseRate);
|
||||||
hash.Add(WeaponCrateProbability);
|
hash.Add(WeaponCrateProb);
|
||||||
hash.Add(DonorCards);
|
hash.Add(DonorCards);
|
||||||
hash.Add(HealthCrateProbability);
|
hash.Add(HealthCrateProb);
|
||||||
hash.Add(HealthCrateEnergy);
|
hash.Add(HealthCrateEnergy);
|
||||||
hash.Add(UtilityCrateProbability);
|
hash.Add(UtilityCrateProb);
|
||||||
hash.Add(ObjectTypes);
|
hash.Add(ObjectTypes);
|
||||||
hash.Add(ObjectCount);
|
hash.Add(ObjectCount);
|
||||||
hash.Add(MineDelay);
|
hash.Add(MineDelay);
|
||||||
@ -935,19 +933,19 @@ namespace Syroot.Worms.Armageddon
|
|||||||
hash.Add(AquaSheep);
|
hash.Add(AquaSheep);
|
||||||
hash.Add(SheepHeaven);
|
hash.Add(SheepHeaven);
|
||||||
hash.Add(GodWorms);
|
hash.Add(GodWorms);
|
||||||
hash.Add(IndestructibleLand);
|
hash.Add(IndiLand);
|
||||||
hash.Add(UpgradedGrenade);
|
hash.Add(UpgradeGrenade);
|
||||||
hash.Add(UpgradedShotgun);
|
hash.Add(UpgradeShotgun);
|
||||||
hash.Add(UpgradedCluster);
|
hash.Add(UpgradeCluster);
|
||||||
hash.Add(UpgradedLongbow);
|
hash.Add(UpgradeLongbow);
|
||||||
hash.Add(EnableTeamWeapons);
|
hash.Add(TeamWeapons);
|
||||||
hash.Add(EnableSuperWeapons);
|
hash.Add(SuperWeapons);
|
||||||
foreach (SchemeWeapon weapon in Weapons)
|
foreach (SchemeWeapon weapon in Weapons)
|
||||||
hash.Add(weapon);
|
hash.Add(weapon);
|
||||||
hash.Add(Extended);
|
hash.Add(Extended);
|
||||||
foreach (byte attachmentByte in Attachment)
|
foreach (byte attachmentByte in Attachment)
|
||||||
hash.Add(attachmentByte);
|
hash.Add(attachmentByte);
|
||||||
hash.Add(RwVersionOverride);
|
hash.Add(RwVersion);
|
||||||
return hash.ToHashCode();
|
return hash.ToHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1030,23 +1028,23 @@ namespace Syroot.Worms.Armageddon
|
|||||||
Version = reader.ReadEnum<SchemeVersion>(true);
|
Version = reader.ReadEnum<SchemeVersion>(true);
|
||||||
|
|
||||||
// Read the options.
|
// Read the options.
|
||||||
HotSeatDelay = reader.Read1Byte();
|
HotSeatTime = reader.Read1Byte();
|
||||||
RetreatTime = reader.Read1Byte();
|
RetreatTime = reader.Read1Byte();
|
||||||
RetreatTimeRope = reader.Read1Byte();
|
RetreatTimeRope = reader.Read1Byte();
|
||||||
ShowRoundTime = reader.ReadBoolean();
|
ShowRoundTime = reader.ReadBoolean();
|
||||||
AutomaticReplays = reader.ReadBoolean();
|
Replays = reader.ReadBoolean();
|
||||||
FallDamage = reader.ReadByte() * 50 % 256 * 2;
|
FallDamage = reader.ReadByte() * 50 % 256 * 2;
|
||||||
ArtilleryMode = reader.ReadBoolean();
|
ArtilleryMode = reader.ReadBoolean();
|
||||||
SchemeEditor = reader.ReadEnum<SchemeEditor>(false);
|
SchemeEditor = reader.ReadEnum<SchemeEditor>(false);
|
||||||
StockpilingMode = reader.ReadEnum<StockpilingMode>(true);
|
Stockpiling = reader.ReadEnum<Stockpiling>(true);
|
||||||
WormSelectMode = reader.ReadEnum<WormSelect>(true);
|
WormSelect = reader.ReadEnum<WormSelect>(true);
|
||||||
SuddenDeathEvent = reader.ReadEnum<SuddenDeathEvent>(true);
|
SuddenDeathEvent = reader.ReadEnum<SuddenDeathEvent>(true);
|
||||||
_waterRiseIndex = reader.Read1Byte();
|
_waterRiseIndex = reader.Read1Byte();
|
||||||
WeaponCrateProbability = reader.ReadSByte();
|
WeaponCrateProb = reader.ReadSByte();
|
||||||
DonorCards = reader.ReadBoolean();
|
DonorCards = reader.ReadBoolean();
|
||||||
HealthCrateProbability = reader.ReadSByte();
|
HealthCrateProb = reader.ReadSByte();
|
||||||
HealthCrateEnergy = reader.Read1Byte();
|
HealthCrateEnergy = reader.Read1Byte();
|
||||||
UtilityCrateProbability = reader.ReadSByte();
|
UtilityCrateProb = reader.ReadSByte();
|
||||||
readObjectCombo();
|
readObjectCombo();
|
||||||
readMineDelay();
|
readMineDelay();
|
||||||
DudMines = reader.ReadBoolean();
|
DudMines = reader.ReadBoolean();
|
||||||
@ -1059,19 +1057,17 @@ namespace Syroot.Worms.Armageddon
|
|||||||
AquaSheep = reader.ReadBoolean();
|
AquaSheep = reader.ReadBoolean();
|
||||||
SheepHeaven = reader.ReadBoolean();
|
SheepHeaven = reader.ReadBoolean();
|
||||||
GodWorms = reader.ReadBoolean();
|
GodWorms = reader.ReadBoolean();
|
||||||
IndestructibleLand = reader.ReadBoolean();
|
IndiLand = reader.ReadBoolean();
|
||||||
UpgradedGrenade = reader.ReadBoolean();
|
UpgradeGrenade = reader.ReadBoolean();
|
||||||
UpgradedShotgun = reader.ReadBoolean();
|
UpgradeShotgun = reader.ReadBoolean();
|
||||||
UpgradedCluster = reader.ReadBoolean();
|
UpgradeCluster = reader.ReadBoolean();
|
||||||
UpgradedLongbow = reader.ReadBoolean();
|
UpgradeLongbow = reader.ReadBoolean();
|
||||||
EnableTeamWeapons = reader.ReadBoolean();
|
TeamWeapons = reader.ReadBoolean();
|
||||||
EnableSuperWeapons = reader.ReadBoolean();
|
SuperWeapons = reader.ReadBoolean();
|
||||||
|
|
||||||
// Read the weapons.
|
// Read the weapons.
|
||||||
Weapons = new WeaponList();
|
Weapons = new WeaponList();
|
||||||
int weaponCount = GetWeaponCount(Version);
|
reader.ReadStructs(Weapons.AsSpan(GetWeaponCount(Version)));
|
||||||
for (int i = 0; i < weaponCount; i++)
|
|
||||||
Weapons[i] = reader.ReadStruct<SchemeWeapon>();
|
|
||||||
|
|
||||||
// Read available extended settings or deserialize them from RubberWorm settings encoded in probabilities.
|
// Read available extended settings or deserialize them from RubberWorm settings encoded in probabilities.
|
||||||
switch (Version)
|
switch (Version)
|
||||||
@ -1163,23 +1159,23 @@ namespace Syroot.Worms.Armageddon
|
|||||||
writer.WriteEnum(version);
|
writer.WriteEnum(version);
|
||||||
|
|
||||||
// Write the options.
|
// Write the options.
|
||||||
writer.Write(HotSeatDelay);
|
writer.Write(HotSeatTime);
|
||||||
writer.Write(RetreatTime);
|
writer.Write(RetreatTime);
|
||||||
writer.Write(RetreatTimeRope);
|
writer.Write(RetreatTimeRope);
|
||||||
writer.Write(ShowRoundTime);
|
writer.Write(ShowRoundTime);
|
||||||
writer.Write(AutomaticReplays);
|
writer.Write(Replays);
|
||||||
writer.Write((byte)(FallDamage / 4 * 41 % 128));
|
writer.Write((byte)(FallDamage / 4 * 41 % 128));
|
||||||
writer.Write(ArtilleryMode);
|
writer.Write(ArtilleryMode);
|
||||||
writer.WriteEnum(SchemeEditor, false);
|
writer.WriteEnum(SchemeEditor, false);
|
||||||
writer.WriteEnum(StockpilingMode, true);
|
writer.WriteEnum(Stockpiling, true);
|
||||||
writer.WriteEnum(WormSelectMode, true);
|
writer.WriteEnum(WormSelect, true);
|
||||||
writer.WriteEnum(SuddenDeathEvent, true);
|
writer.WriteEnum(SuddenDeathEvent, true);
|
||||||
writer.Write(_waterRiseIndex);
|
writer.Write(_waterRiseIndex);
|
||||||
writer.Write(WeaponCrateProbability);
|
writer.Write(WeaponCrateProb);
|
||||||
writer.Write(DonorCards);
|
writer.Write(DonorCards);
|
||||||
writer.Write(HealthCrateProbability);
|
writer.Write(HealthCrateProb);
|
||||||
writer.Write(HealthCrateEnergy);
|
writer.Write(HealthCrateEnergy);
|
||||||
writer.Write(UtilityCrateProbability);
|
writer.Write(UtilityCrateProb);
|
||||||
saveObjectTypesAndCount();
|
saveObjectTypesAndCount();
|
||||||
saveMineDelayConfig();
|
saveMineDelayConfig();
|
||||||
writer.Write(DudMines);
|
writer.Write(DudMines);
|
||||||
@ -1192,22 +1188,20 @@ namespace Syroot.Worms.Armageddon
|
|||||||
writer.Write(AquaSheep);
|
writer.Write(AquaSheep);
|
||||||
writer.Write(SheepHeaven);
|
writer.Write(SheepHeaven);
|
||||||
writer.Write(GodWorms);
|
writer.Write(GodWorms);
|
||||||
writer.Write(IndestructibleLand);
|
writer.Write(IndiLand);
|
||||||
writer.Write(UpgradedGrenade);
|
writer.Write(UpgradeGrenade);
|
||||||
writer.Write(UpgradedShotgun);
|
writer.Write(UpgradeShotgun);
|
||||||
writer.Write(UpgradedCluster);
|
writer.Write(UpgradeCluster);
|
||||||
writer.Write(UpgradedLongbow);
|
writer.Write(UpgradeLongbow);
|
||||||
writer.Write(EnableTeamWeapons);
|
writer.Write(TeamWeapons);
|
||||||
writer.Write(EnableSuperWeapons);
|
writer.Write(SuperWeapons);
|
||||||
|
|
||||||
// Serialize RubberWorm settings encoded in weapon probabilities.
|
// Serialize RubberWorm settings encoded in weapon probabilities.
|
||||||
if (version == SchemeVersion.Version2)
|
if (version == SchemeVersion.Version2)
|
||||||
SaveRubberWorm();
|
SaveRubberWorm();
|
||||||
|
|
||||||
// Write the weapons.
|
// Write the weapons.
|
||||||
int weaponCount = GetWeaponCount(version);
|
writer.WriteStructs<SchemeWeapon>(Weapons.AsSpan(GetWeaponCount(version)));
|
||||||
for (int i = 0; i < weaponCount; i++)
|
|
||||||
writer.WriteStruct(Weapons[i]);
|
|
||||||
|
|
||||||
// Clear RubberWorm probabilities again or write available extended settings.
|
// Clear RubberWorm probabilities again or write available extended settings.
|
||||||
switch (version)
|
switch (version)
|
||||||
@ -1228,10 +1222,10 @@ namespace Syroot.Worms.Armageddon
|
|||||||
{
|
{
|
||||||
// Reset all super weapon probabilities to remove any settings made by RubberWorm.
|
// Reset all super weapon probabilities to remove any settings made by RubberWorm.
|
||||||
for (Weapon superWeapon = Weapon.Freeze; superWeapon <= Weapon.Armageddon; superWeapon++)
|
for (Weapon superWeapon = Weapon.Freeze; superWeapon <= Weapon.Armageddon; superWeapon++)
|
||||||
Weapons[superWeapon].Crates = 0;
|
Weapons[superWeapon].Prob = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearExtendedOptions() => Extended = SchemeExtendedOptions.Default;
|
private void ClearExtendedOptions() => Extended = ExtendedOptions.Default;
|
||||||
|
|
||||||
private unsafe void LoadExtendedOptions(BinaryStream reader)
|
private unsafe void LoadExtendedOptions(BinaryStream reader)
|
||||||
{
|
{
|
||||||
@ -1242,7 +1236,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Span<byte> bytes = new Span<byte>(
|
Span<byte> bytes = new Span<byte>(
|
||||||
Unsafe.AsPointer(ref _extended), Unsafe.SizeOf<SchemeExtendedOptions>());
|
Unsafe.AsPointer(ref _extended), Unsafe.SizeOf<ExtendedOptions>());
|
||||||
#if NETSTANDARD2_0
|
#if NETSTANDARD2_0
|
||||||
// Cannot prevent copy in .NET Standard 2.0.
|
// Cannot prevent copy in .NET Standard 2.0.
|
||||||
byte[] buffer = new byte[(int)Math.Min(bytes.Length, reader.Length - reader.Position)];
|
byte[] buffer = new byte[(int)Math.Min(bytes.Length, reader.Length - reader.Position)];
|
||||||
@ -1259,7 +1253,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
unchecked
|
unchecked
|
||||||
{
|
{
|
||||||
// Earthquake flags.
|
// Earthquake flags.
|
||||||
byte prob = (byte)Weapons[Weapon.Earthquake].Crates;
|
byte prob = (byte)Weapons[Weapon.Earthquake].Prob;
|
||||||
Extended.AutoReaim = prob.GetBit(0);
|
Extended.AutoReaim = prob.GetBit(0);
|
||||||
Extended.CircularAim = prob.GetBit(1);
|
Extended.CircularAim = prob.GetBit(1);
|
||||||
Extended.AntiLockPower = prob.GetBit(2);
|
Extended.AntiLockPower = prob.GetBit(2);
|
||||||
@ -1267,19 +1261,19 @@ namespace Syroot.Worms.Armageddon
|
|||||||
Extended.KaosMod = prob.DecodeByte(4, 4);
|
Extended.KaosMod = prob.DecodeByte(4, 4);
|
||||||
|
|
||||||
// Antisink.
|
// Antisink.
|
||||||
Extended.AntiSink = Weapons[Weapon.SheepStrike].Crates != 0;
|
Extended.AntiSink = Weapons[Weapon.SheepStrike].Prob != 0;
|
||||||
|
|
||||||
// Crate limit and rate.
|
// Crate limit and rate.
|
||||||
prob = (byte)Weapons[Weapon.MagicBullet].Crates;
|
prob = (byte)Weapons[Weapon.MagicBullet].Prob;
|
||||||
Extended.CrateLimit = prob switch
|
Extended.CrateLimit = prob switch
|
||||||
{
|
{
|
||||||
0 => SchemeExtendedOptions.Default.CrateLimit,
|
0 => ExtendedOptions.Default.CrateLimit,
|
||||||
_ => prob
|
_ => prob
|
||||||
};
|
};
|
||||||
Extended.CrateRate = (byte)Weapons[Weapon.NuclearTest].Crates;
|
Extended.CrateRate = (byte)Weapons[Weapon.NuclearTest].Prob;
|
||||||
|
|
||||||
// Mole squadron flags.
|
// Mole squadron flags.
|
||||||
prob = (byte)Weapons[Weapon.MoleSquadron].Crates;
|
prob = (byte)Weapons[Weapon.MoleSquadron].Prob;
|
||||||
Extended.ShotDoesntEndTurn = prob.GetBit(0);
|
Extended.ShotDoesntEndTurn = prob.GetBit(0);
|
||||||
Extended.LoseControlDoesntEndTurn = prob.GetBit(1);
|
Extended.LoseControlDoesntEndTurn = prob.GetBit(1);
|
||||||
Extended.FiringPausesTimer = !prob.GetBit(2);
|
Extended.FiringPausesTimer = !prob.GetBit(2);
|
||||||
@ -1290,21 +1284,21 @@ namespace Syroot.Worms.Armageddon
|
|||||||
Extended.ExtendedFuse = prob.GetBit(7);
|
Extended.ExtendedFuse = prob.GetBit(7);
|
||||||
|
|
||||||
// Flame limit.
|
// Flame limit.
|
||||||
prob = (byte)Weapons[Weapon.ScalesOfJustice].Crates;
|
prob = (byte)Weapons[Weapon.ScalesOfJustice].Prob;
|
||||||
if (prob > 0)
|
if (prob > 0)
|
||||||
Extended.FlameLimit = (ushort)(prob * 100);
|
Extended.FlameLimit = (ushort)(prob * 100);
|
||||||
|
|
||||||
// Friction.
|
// Friction.
|
||||||
prob = (byte)Weapons[Weapon.SalvationArmy].Crates;
|
prob = (byte)Weapons[Weapon.SalvationArmy].Prob;
|
||||||
if (prob > 0)
|
if (prob > 0)
|
||||||
Extended.Friction = prob / 100f;
|
Extended.Friction = prob / 100f;
|
||||||
|
|
||||||
// Gravity - 8th and 7th bit control constant / proportional black hole, otherwise normal gravity.
|
// Gravity - 8th and 7th bit control constant / proportional black hole, otherwise normal gravity.
|
||||||
prob = (byte)Weapons[Weapon.MailStrike].Crates;
|
prob = (byte)Weapons[Weapon.MailStrike].Prob;
|
||||||
if (prob == 0)
|
if (prob == 0)
|
||||||
{
|
{
|
||||||
Extended.RwGravityType = RwGravityType.None;
|
Extended.RwGravityType = RwGravityType.None;
|
||||||
Extended.RwGravity = SchemeExtendedOptions.Default.RwGravity;
|
Extended.RwGravity = ExtendedOptions.Default.RwGravity;
|
||||||
}
|
}
|
||||||
else if (prob.GetBit(7) && prob.GetBit(6))
|
else if (prob.GetBit(7) && prob.GetBit(6))
|
||||||
{
|
{
|
||||||
@ -1323,7 +1317,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rope-knocking force.
|
// Rope-knocking force.
|
||||||
prob = (byte)Weapons[Weapon.SuperBananaBomb].Crates;
|
prob = (byte)Weapons[Weapon.SuperBananaBomb].Prob;
|
||||||
Extended.RopeKnockForce = prob switch
|
Extended.RopeKnockForce = prob switch
|
||||||
{
|
{
|
||||||
0 => null,
|
0 => null,
|
||||||
@ -1332,35 +1326,35 @@ namespace Syroot.Worms.Armageddon
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Maximum rope speed.
|
// Maximum rope speed.
|
||||||
prob = (byte)Weapons[Weapon.MineStrike].Crates;
|
prob = (byte)Weapons[Weapon.MineStrike].Prob;
|
||||||
Extended.RopeMaxSpeed = prob switch
|
Extended.RopeMaxSpeed = prob switch
|
||||||
{
|
{
|
||||||
0 => SchemeExtendedOptions.Default.RopeMaxSpeed,
|
0 => ExtendedOptions.Default.RopeMaxSpeed,
|
||||||
Byte.MaxValue => 0,
|
Byte.MaxValue => 0,
|
||||||
_ => prob
|
_ => prob
|
||||||
};
|
};
|
||||||
|
|
||||||
// Select worm any time.
|
// Select worm any time.
|
||||||
Extended.WormSelectAnytime = ((byte)Weapons[Weapon.MBBomb].Crates).GetBit(0);
|
Extended.WormSelectAnytime = ((byte)Weapons[Weapon.MBBomb].Prob).GetBit(0);
|
||||||
|
|
||||||
// Viscosity.
|
// Viscosity.
|
||||||
prob = (byte)Weapons[Weapon.ConcreteDonkey].Crates;
|
prob = (byte)Weapons[Weapon.ConcreteDonkey].Prob;
|
||||||
Extended.Viscosity = prob / 255f;
|
Extended.Viscosity = prob / 255f;
|
||||||
Extended.ViscosityWorms = (prob & 1) == 1;
|
Extended.ViscosityWorms = (prob & 1) == 1;
|
||||||
|
|
||||||
// Wind.
|
// Wind.
|
||||||
prob = (byte)Weapons[Weapon.SuicideBomber].Crates;
|
prob = (byte)Weapons[Weapon.SuicideBomber].Prob;
|
||||||
Extended.RwWind = (byte)Weapons[Weapon.SuicideBomber].Crates / 255f;
|
Extended.RwWind = (byte)Weapons[Weapon.SuicideBomber].Prob / 255f;
|
||||||
Extended.RwWindWorms = (prob & 1) == 1;
|
Extended.RwWindWorms = (prob & 1) == 1;
|
||||||
|
|
||||||
// Worm bounce.
|
// Worm bounce.
|
||||||
Extended.WormBounce = (byte)Weapons[Weapon.Armageddon].Crates / 255f;
|
Extended.WormBounce = (byte)Weapons[Weapon.Armageddon].Prob / 255f;
|
||||||
|
|
||||||
// Version override.
|
// Version override.
|
||||||
RwVersionOverride = BinaryPrimitives.ReadUInt16BigEndian(stackalloc[]
|
RwVersion = BinaryPrimitives.ReadUInt16BigEndian(stackalloc[]
|
||||||
{
|
{
|
||||||
(byte)Weapons[Weapon.SelectWorm].Crates,
|
(byte)Weapons[Weapon.SelectWorm].Prob,
|
||||||
(byte)Weapons[Weapon.Freeze].Crates
|
(byte)Weapons[Weapon.Freeze].Prob
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1371,7 +1365,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
private unsafe void SaveExtendedOptions(BinaryStream writer)
|
private unsafe void SaveExtendedOptions(BinaryStream writer)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> bytes = new ReadOnlySpan<byte>(
|
ReadOnlySpan<byte> bytes = new ReadOnlySpan<byte>(
|
||||||
Unsafe.AsPointer(ref _extended), Unsafe.SizeOf<SchemeExtendedOptions>());
|
Unsafe.AsPointer(ref _extended), Unsafe.SizeOf<ExtendedOptions>());
|
||||||
#if NETSTANDARD2_0
|
#if NETSTANDARD2_0
|
||||||
// Cannot prevent copy in .NET Standard 2.0.
|
// Cannot prevent copy in .NET Standard 2.0.
|
||||||
writer.Write(bytes.ToArray());
|
writer.Write(bytes.ToArray());
|
||||||
@ -1391,15 +1385,15 @@ namespace Syroot.Worms.Armageddon
|
|||||||
prob = prob.SetBit(2, Extended.AntiLockPower);
|
prob = prob.SetBit(2, Extended.AntiLockPower);
|
||||||
prob = prob.SetBit(3, Extended.ShotDoesntEndTurnAll);
|
prob = prob.SetBit(3, Extended.ShotDoesntEndTurnAll);
|
||||||
prob = prob.Encode(Extended.KaosMod, 4, 4);
|
prob = prob.Encode(Extended.KaosMod, 4, 4);
|
||||||
Weapons[Weapon.Earthquake].Crates = (sbyte)prob;
|
Weapons[Weapon.Earthquake].Prob = (sbyte)prob;
|
||||||
|
|
||||||
// Antisink
|
// Antisink
|
||||||
Weapons[Weapon.SheepStrike].Crates = (sbyte)(Extended.AntiSink ? 1 : 0);
|
Weapons[Weapon.SheepStrike].Prob = (sbyte)(Extended.AntiSink ? 1 : 0);
|
||||||
|
|
||||||
// Crate limit and rate.
|
// Crate limit and rate.
|
||||||
if (Extended.CrateLimit != SchemeExtendedOptions.Default.CrateLimit)
|
if (Extended.CrateLimit != ExtendedOptions.Default.CrateLimit)
|
||||||
Weapons[Weapon.MagicBullet].Crates = (sbyte)Math.Min(Byte.MaxValue, Extended.CrateLimit);
|
Weapons[Weapon.MagicBullet].Prob = (sbyte)Math.Min(Byte.MaxValue, Extended.CrateLimit);
|
||||||
Weapons[Weapon.NuclearTest].Crates = (sbyte)Extended.CrateRate;
|
Weapons[Weapon.NuclearTest].Prob = (sbyte)Extended.CrateRate;
|
||||||
|
|
||||||
// Mole squadron flags.
|
// Mole squadron flags.
|
||||||
prob = 0;
|
prob = 0;
|
||||||
@ -1411,15 +1405,15 @@ namespace Syroot.Worms.Armageddon
|
|||||||
prob = prob.SetBit(5, Extended.ObjectPushByExplosion == true);
|
prob = prob.SetBit(5, Extended.ObjectPushByExplosion == true);
|
||||||
prob = prob.SetBit(6, Extended.WeaponsDontChange);
|
prob = prob.SetBit(6, Extended.WeaponsDontChange);
|
||||||
prob = prob.SetBit(7, Extended.ExtendedFuse);
|
prob = prob.SetBit(7, Extended.ExtendedFuse);
|
||||||
Weapons[Weapon.MoleSquadron].Crates = (sbyte)prob;
|
Weapons[Weapon.MoleSquadron].Prob = (sbyte)prob;
|
||||||
|
|
||||||
// Flame limit.
|
// Flame limit.
|
||||||
if (Extended.FlameLimit != SchemeExtendedOptions.Default.FlameLimit)
|
if (Extended.FlameLimit != ExtendedOptions.Default.FlameLimit)
|
||||||
Weapons[Weapon.ScalesOfJustice].Crates = (sbyte)(Extended.FlameLimit / 100);
|
Weapons[Weapon.ScalesOfJustice].Prob = (sbyte)(Extended.FlameLimit / 100);
|
||||||
|
|
||||||
// Friction.
|
// Friction.
|
||||||
if (Extended.Friction != SchemeExtendedOptions.Default.Friction)
|
if (Extended.Friction != ExtendedOptions.Default.Friction)
|
||||||
Weapons[Weapon.SalvationArmy].Crates = (sbyte)Math.Round(Extended.Friction * 100, 0);
|
Weapons[Weapon.SalvationArmy].Prob = (sbyte)Math.Round(Extended.Friction * 100, 0);
|
||||||
|
|
||||||
// Gravity - 8th and 7th bit control constant / proportional black hole, otherwise normal gravity.
|
// Gravity - 8th and 7th bit control constant / proportional black hole, otherwise normal gravity.
|
||||||
prob = 0;
|
prob = 0;
|
||||||
@ -1438,10 +1432,10 @@ namespace Syroot.Worms.Armageddon
|
|||||||
prob = prob.Encode((sbyte)(Extended.RwGravity / 512), 6);
|
prob = prob.Encode((sbyte)(Extended.RwGravity / 512), 6);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Weapons[Weapon.MailStrike].Crates = (sbyte)prob;
|
Weapons[Weapon.MailStrike].Prob = (sbyte)prob;
|
||||||
|
|
||||||
// Rope-knocking force.
|
// Rope-knocking force.
|
||||||
Weapons[Weapon.SuperBananaBomb].Crates = (sbyte)(Extended.RopeKnockForce switch
|
Weapons[Weapon.SuperBananaBomb].Prob = (sbyte)(Extended.RopeKnockForce switch
|
||||||
{
|
{
|
||||||
null => 0,
|
null => 0,
|
||||||
0 => Byte.MaxValue,
|
0 => Byte.MaxValue,
|
||||||
@ -1449,7 +1443,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Maximum rope speed.
|
// Maximum rope speed.
|
||||||
Weapons[Weapon.MineStrike].Crates = (sbyte)(Extended.RopeMaxSpeed switch
|
Weapons[Weapon.MineStrike].Prob = (sbyte)(Extended.RopeMaxSpeed switch
|
||||||
{
|
{
|
||||||
16/*SchemeExtendedOptions.Default.RopeMaxSpeed*/ => 0,
|
16/*SchemeExtendedOptions.Default.RopeMaxSpeed*/ => 0,
|
||||||
0 => Byte.MaxValue,
|
0 => Byte.MaxValue,
|
||||||
@ -1457,27 +1451,27 @@ namespace Syroot.Worms.Armageddon
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Select worm any time.
|
// Select worm any time.
|
||||||
prob = ((byte)Weapons[Weapon.MBBomb].Crates).SetBit(0, Extended.WormSelectAnytime);
|
prob = ((byte)Weapons[Weapon.MBBomb].Prob).SetBit(0, Extended.WormSelectAnytime);
|
||||||
Weapons[Weapon.MBBomb].Crates = (sbyte)prob;
|
Weapons[Weapon.MBBomb].Prob = (sbyte)prob;
|
||||||
|
|
||||||
// Viscosity.
|
// Viscosity.
|
||||||
prob = (byte)Math.Round(Extended.Viscosity * 255, 0);
|
prob = (byte)Math.Round(Extended.Viscosity * 255, 0);
|
||||||
prob.SetBit(0, Extended.ViscosityWorms);
|
prob.SetBit(0, Extended.ViscosityWorms);
|
||||||
Weapons[Weapon.ConcreteDonkey].Crates = (sbyte)prob;
|
Weapons[Weapon.ConcreteDonkey].Prob = (sbyte)prob;
|
||||||
|
|
||||||
// Wind.
|
// Wind.
|
||||||
prob = (byte)Math.Round(Extended.RwWind * 255, 0);
|
prob = (byte)Math.Round(Extended.RwWind * 255, 0);
|
||||||
prob.SetBit(0, Extended.RwWindWorms);
|
prob.SetBit(0, Extended.RwWindWorms);
|
||||||
Weapons[Weapon.SuicideBomber].Crates = (sbyte)prob;
|
Weapons[Weapon.SuicideBomber].Prob = (sbyte)prob;
|
||||||
|
|
||||||
// Worm bounce.
|
// Worm bounce.
|
||||||
Weapons[Weapon.Armageddon].Crates = (sbyte)Math.Round(Extended.WormBounce * 255, 0);
|
Weapons[Weapon.Armageddon].Prob = (sbyte)Math.Round(Extended.WormBounce * 255, 0);
|
||||||
|
|
||||||
// Version override.
|
// Version override.
|
||||||
Span<byte> versionBytes = stackalloc byte[sizeof(ushort)];
|
Span<byte> versionBytes = stackalloc byte[sizeof(ushort)];
|
||||||
BinaryPrimitives.WriteUInt16BigEndian(versionBytes, RwVersionOverride);
|
BinaryPrimitives.WriteUInt16BigEndian(versionBytes, RwVersion);
|
||||||
Weapons[Weapon.SelectWorm].Crates = (sbyte)versionBytes[0];
|
Weapons[Weapon.SelectWorm].Prob = (sbyte)versionBytes[0];
|
||||||
Weapons[Weapon.Freeze].Crates = (sbyte)versionBytes[1];
|
Weapons[Weapon.Freeze].Prob = (sbyte)versionBytes[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,7 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// values represent infinity.</summary>
|
/// values represent infinity.</summary>
|
||||||
public sbyte Delay;
|
public sbyte Delay;
|
||||||
/// <summary>Probability of this weapon to appear in crates. Has no effect on super weapons.</summary>
|
/// <summary>Probability of this weapon to appear in crates. Has no effect on super weapons.</summary>
|
||||||
public sbyte Crates;
|
public sbyte Prob;
|
||||||
|
|
||||||
// ---- OPERATORS ----------------------------------------------------------------------------------------------
|
// ---- OPERATORS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -39,9 +39,9 @@ namespace Syroot.Worms.Armageddon
|
|||||||
=> Ammo == other.Ammo
|
=> Ammo == other.Ammo
|
||||||
&& Power == other.Power
|
&& Power == other.Power
|
||||||
&& Delay == other.Delay
|
&& Delay == other.Delay
|
||||||
&& Crates == other.Crates;
|
&& Prob == other.Prob;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override int GetHashCode() => HashCode.Combine(Ammo, Power, Delay, Crates);
|
public override int GetHashCode() => HashCode.Combine(Ammo, Power, Delay, Prob);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<AssemblyName>Syroot.Worms.Armageddon</AssemblyName>
|
<AssemblyName>Syroot.Worms.Armageddon</AssemblyName>
|
||||||
<Description>.NET library for loading and modifying files of Team17's Worms Armageddon.</Description>
|
<Description>.NET library for loading and modifying files of Team17's Worms Armageddon.</Description>
|
||||||
<PackageReleaseNotes>Fix issues when loading and saving some formats. Simplify Scheme usage.</PackageReleaseNotes>
|
<PackageReleaseNotes>Overhaul implementation and documentation. Implement W:A V3 scheme format.</PackageReleaseNotes>
|
||||||
<PackageTags>$(PackageTags);worms armageddon</PackageTags>
|
<PackageTags>$(PackageTags);worms armageddon</PackageTags>
|
||||||
<Version>3.3.0-beta001</Version>
|
<Version>4.0.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Syroot.Worms\Syroot.Worms.csproj" />
|
<ProjectReference Include="..\Syroot.Worms\Syroot.Worms.csproj" />
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Armageddon
|
namespace Syroot.Worms.Armageddon
|
||||||
{
|
{
|
||||||
@ -17,33 +18,17 @@ namespace Syroot.Worms.Armageddon
|
|||||||
private const int _missionCount = 33;
|
private const int _missionCount = 33;
|
||||||
private const int _trainingMissionCount = 6;
|
private const int _trainingMissionCount = 6;
|
||||||
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Team"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public Team()
|
|
||||||
{
|
|
||||||
WormNames = new string[8];
|
|
||||||
MissionStatuses = new TeamMissionStatus[_missionCount];
|
|
||||||
TrainingMissionTimes = new int[_trainingMissionCount];
|
|
||||||
Unknown1 = new int[10];
|
|
||||||
TrainingMissionMedals = new byte[_trainingMissionCount];
|
|
||||||
Unknown2 = new byte[10];
|
|
||||||
Unknown3 = new int[7];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name of the team.
|
/// Gets or sets the name of the team.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; } = String.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the 8 worm names.
|
/// Gets the 8 worm names.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string[] WormNames { get; set; }
|
public string[] WormNames { get; } = new string[8];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the AI intelligence difficulty level, from 0-5, where 0 is human-controlled.
|
/// Gets or sets the AI intelligence difficulty level, from 0-5, where 0 is human-controlled.
|
||||||
@ -53,18 +38,18 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name of soundbank for the voice of team worms.
|
/// Gets or sets the name of soundbank for the voice of team worms.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SoundBankName { get; set; }
|
public string SoundBankName { get; set; } = String.Empty;
|
||||||
|
|
||||||
public byte SoundBankLocation { get; set; }
|
public byte SoundBankLocation { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name of the team fanfare.
|
/// Gets or sets the name of the team fanfare.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string FanfareName { get; set; }
|
public string FanfareName { get; set; } = String.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the fanfare with the name stored in <see cref="FanfareName"/>
|
/// Gets or sets a value indicating whether the fanfare with the name stored in <see cref="FanfareName"/>
|
||||||
/// (<c>true</c>) or the player's countries' fanfare should be played (<c>false</c>).
|
/// (<see langword="true"/>) or the player's countries' fanfare should be played (<see langword="false"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte UseCustomFanfare { get; set; }
|
public byte UseCustomFanfare { get; set; }
|
||||||
|
|
||||||
@ -76,12 +61,12 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the file name of the team grave bitmap if it uses a custom one.
|
/// Gets or sets the file name of the team grave bitmap if it uses a custom one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string GraveFileName { get; set; }
|
public string GraveFileName { get; set; } = String.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the team grave bitmap if it uses a custom one.
|
/// Gets or sets the team grave bitmap if it uses a custom one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RawBitmap Grave { get; set; }
|
public RawBitmap Grave { get; set; } = new RawBitmap();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the team's special weapon.
|
/// Gets or sets the team's special weapon.
|
||||||
@ -139,19 +124,19 @@ namespace Syroot.Worms.Armageddon
|
|||||||
public int DeathmatchDeaths { get; set; }
|
public int DeathmatchDeaths { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the array of 33 mission statuses.
|
/// Gets the array of 33 mission statuses.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TeamMissionStatus[] MissionStatuses { get; set; }
|
public TeamMissionStatus[] MissionStatuses { get; } = new TeamMissionStatus[_missionCount];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the file name of the team flag.
|
/// Gets or sets the file name of the team flag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string FlagFileName { get; set; }
|
public string FlagFileName { get; set; } = String.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the bitmap of the team flag.
|
/// Gets or sets the bitmap of the team flag.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RawBitmap Flag { get; set; }
|
public RawBitmap Flag { get; set; } = new RawBitmap();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the deathmatch rank this team reached.
|
/// Gets or sets the deathmatch rank this team reached.
|
||||||
@ -161,31 +146,19 @@ namespace Syroot.Worms.Armageddon
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the seconds the team required to finish all 6 training missions.
|
/// Gets or sets the seconds the team required to finish all 6 training missions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int[] TrainingMissionTimes { get; set; }
|
public int[] TrainingMissionTimes { get; set; } = new int[_trainingMissionCount];
|
||||||
|
|
||||||
/// <summary>
|
public int[] Unknown1 { get; set; } = new int[10];
|
||||||
/// Gets or sets 10 unknown integer values.
|
|
||||||
/// </summary>
|
|
||||||
public int[] Unknown1 { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the medals the team achieved in all 6 training missions.
|
/// Gets or sets the medals the team achieved in all 6 training missions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte[] TrainingMissionMedals { get; set; }
|
public byte[] TrainingMissionMedals { get; set; } = new byte[_trainingMissionCount];
|
||||||
|
|
||||||
/// <summary>
|
public byte[] Unknown2 { get; set; } = new byte[10];
|
||||||
/// Gets or sets 10 unknown bytes.
|
|
||||||
/// </summary>
|
|
||||||
public byte[] Unknown2 { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
public int[] Unknown3 { get; set; } = new int[7];
|
||||||
/// Gets or sets 7 unknown integer values.
|
|
||||||
/// </summary>
|
|
||||||
public int[] Unknown3 { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets an unknown byte.
|
|
||||||
/// </summary>
|
|
||||||
public byte Unknown4 { get; set; }
|
public byte Unknown4 { get; set; }
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
@ -196,7 +169,8 @@ namespace Syroot.Worms.Armageddon
|
|||||||
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
|
|
||||||
Name = reader.ReadString(17);
|
Name = reader.ReadString(17);
|
||||||
WormNames = reader.ReadStrings(8, 17);
|
for (int i = 0; i < WormNames.Length; i++)
|
||||||
|
WormNames[i] = reader.ReadString(17);
|
||||||
CpuLevel = reader.Read1Byte();
|
CpuLevel = reader.Read1Byte();
|
||||||
SoundBankName = reader.ReadString(0x20);
|
SoundBankName = reader.ReadString(0x20);
|
||||||
SoundBankLocation = reader.Read1Byte();
|
SoundBankLocation = reader.Read1Byte();
|
||||||
@ -227,7 +201,8 @@ namespace Syroot.Worms.Armageddon
|
|||||||
DeathmatchKills = reader.ReadInt32();
|
DeathmatchKills = reader.ReadInt32();
|
||||||
Deaths = reader.ReadInt32();
|
Deaths = reader.ReadInt32();
|
||||||
DeathmatchDeaths = reader.ReadInt32();
|
DeathmatchDeaths = reader.ReadInt32();
|
||||||
MissionStatuses = reader.ReadStructs<TeamMissionStatus>(_missionCount);
|
for (int i = 0; i < _missionCount; i++)
|
||||||
|
MissionStatuses[i] = reader.ReadStruct<TeamMissionStatus>();
|
||||||
|
|
||||||
FlagFileName = reader.ReadString(0x20);
|
FlagFileName = reader.ReadString(0x20);
|
||||||
Flag = new RawBitmap()
|
Flag = new RawBitmap()
|
||||||
@ -252,19 +227,20 @@ namespace Syroot.Worms.Armageddon
|
|||||||
{
|
{
|
||||||
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
|
|
||||||
writer.WriteString(Name, 17);
|
writer.WriteFixedString(Name, 17);
|
||||||
writer.WriteStrings(WormNames, 17);
|
for (int i = 0; i < WormNames.Length; i++)
|
||||||
|
writer.WriteFixedString(WormNames[i], 17);
|
||||||
writer.Write(CpuLevel);
|
writer.Write(CpuLevel);
|
||||||
writer.WriteString(SoundBankName, 0x20);
|
writer.WriteFixedString(SoundBankName, 0x20);
|
||||||
writer.Write(SoundBankLocation);
|
writer.Write(SoundBankLocation);
|
||||||
writer.WriteString(FanfareName, 0x20);
|
writer.WriteFixedString(FanfareName, 0x20);
|
||||||
writer.Write(UseCustomFanfare);
|
writer.Write(UseCustomFanfare);
|
||||||
|
|
||||||
writer.Write(GraveSprite);
|
writer.Write(GraveSprite);
|
||||||
if (GraveSprite < 0)
|
if (GraveSprite < 0)
|
||||||
{
|
{
|
||||||
writer.WriteString(GraveFileName, 0x20);
|
writer.WriteFixedString(GraveFileName, 0x20);
|
||||||
writer.WriteColors(Grave.Palette);
|
writer.WriteColors(Grave.Palette!);
|
||||||
writer.Write(Grave.Data);
|
writer.Write(Grave.Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,10 +255,11 @@ namespace Syroot.Worms.Armageddon
|
|||||||
writer.Write(DeathmatchKills);
|
writer.Write(DeathmatchKills);
|
||||||
writer.Write(Deaths);
|
writer.Write(Deaths);
|
||||||
writer.Write(DeathmatchDeaths);
|
writer.Write(DeathmatchDeaths);
|
||||||
writer.WriteStructs(MissionStatuses);
|
for (int i = 0; i < MissionStatuses.Length; i++)
|
||||||
|
writer.WriteStruct(ref MissionStatuses[i]);
|
||||||
|
|
||||||
writer.WriteString(FlagFileName, 0x20);
|
writer.WriteFixedString(FlagFileName, 0x20);
|
||||||
writer.WriteColors(Flag.Palette);
|
writer.WriteColors(Flag.Palette!);
|
||||||
writer.Write(Flag.Data);
|
writer.Write(Flag.Data);
|
||||||
|
|
||||||
writer.Write(DeathmatchRank);
|
writer.Write(DeathmatchRank);
|
||||||
|
@ -1,154 +1,156 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Armageddon
|
namespace Syroot.Worms.Armageddon
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the list of teams and unlocked game features stored in WGT files.
|
/// Represents the list of teams and unlocked game features stored in WGT files.
|
||||||
/// Used by WA. S. https://worms2d.info/Team_file.
|
/// Used by WA. S. https://worms2d.info/Team_file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TeamContainer : ILoadableFile, ISaveableFile
|
public class TeamContainer : ILoadableFile, ISaveableFile
|
||||||
{
|
{
|
||||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private const string _signature = "WGT"; // 0-terminated.
|
private const string _signature = "WGT"; // 0-terminated.
|
||||||
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TeamContainer"/> class.
|
/// Initializes a new instance of the <see cref="TeamContainer"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TeamContainer() => Teams = new List<Team>();
|
public TeamContainer() { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TeamContainer"/> class, loading the data from the given
|
/// Initializes a new instance of the <see cref="TeamContainer"/> class, loading the data from the given
|
||||||
/// <see cref="Stream"/>.
|
/// <see cref="Stream"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
public TeamContainer(Stream stream) => Load(stream);
|
public TeamContainer(Stream stream) => Load(stream);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TeamContainer"/> class, loading the data from the given file.
|
/// Initializes a new instance of the <see cref="TeamContainer"/> class, loading the data from the given file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
public TeamContainer(string fileName) => Load(fileName);
|
public TeamContainer(string fileName) => Load(fileName);
|
||||||
|
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value possibly indicating a version of the file format.
|
/// Gets or sets a value possibly indicating a version of the file format.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte Version { get; set; }
|
public byte Version { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the unlocked utilities, weapon upgrades, and game cheats.
|
/// Gets or sets the unlocked utilities, weapon upgrades, and game cheats.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public UnlockedFeatures UnlockedFeatures { get; set; }
|
public UnlockedFeatures UnlockedFeatures { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets an unknown value.
|
/// Gets or sets an unknown value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte Unknown { get; set; }
|
public byte Unknown { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the list of <see cref="Team"/> instances stored.
|
/// Gets or sets the list of <see cref="Team"/> instances stored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<Team> Teams { get; set; }
|
public IList<Team> Teams { get; set; } = new List<Team>();
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Load(Stream stream)
|
public void Load(Stream stream)
|
||||||
{
|
{
|
||||||
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
|
|
||||||
// Read the header.
|
// Read the header.
|
||||||
if (reader.ReadString(StringCoding.ZeroTerminated) != _signature)
|
if (reader.ReadString(StringCoding.ZeroTerminated) != _signature)
|
||||||
throw new InvalidDataException("Invalid WGT file signature.");
|
throw new InvalidDataException("Invalid WGT file signature.");
|
||||||
Version = reader.Read1Byte(); // Really version?
|
Version = reader.Read1Byte(); // Really version?
|
||||||
|
|
||||||
// Read global settings.
|
// Read global settings.
|
||||||
byte teamCount = reader.Read1Byte();
|
byte teamCount = reader.Read1Byte();
|
||||||
UnlockedFeatures = reader.ReadEnum<UnlockedFeatures>(false);
|
UnlockedFeatures = reader.ReadEnum<UnlockedFeatures>(false);
|
||||||
Unknown = reader.Read1Byte();
|
Unknown = reader.Read1Byte();
|
||||||
|
|
||||||
// Read the teams.
|
// Read the teams.
|
||||||
Teams = new List<Team>(reader.Load<Team>(teamCount));
|
Teams = new List<Team>();
|
||||||
}
|
while (teamCount-- > 0)
|
||||||
|
Teams.Add(reader.Load<Team>());
|
||||||
/// <inheritdoc/>
|
}
|
||||||
public void Load(string fileName)
|
|
||||||
{
|
/// <inheritdoc/>
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
public void Load(string fileName)
|
||||||
Load(stream);
|
{
|
||||||
}
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
Load(stream);
|
||||||
/// <inheritdoc/>
|
}
|
||||||
public void Save(Stream stream)
|
|
||||||
{
|
/// <inheritdoc/>
|
||||||
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
public void Save(Stream stream)
|
||||||
|
{
|
||||||
// Write the header.
|
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
writer.Write(_signature, StringCoding.ZeroTerminated);
|
|
||||||
writer.Write(Version);
|
// Write the header.
|
||||||
|
writer.Write(_signature, StringCoding.ZeroTerminated);
|
||||||
// Write global settings.
|
writer.Write(Version);
|
||||||
writer.Write((byte)Teams.Count);
|
|
||||||
writer.WriteEnum(UnlockedFeatures, false);
|
// Write global settings.
|
||||||
writer.Write(Unknown);
|
writer.Write((byte)Teams.Count);
|
||||||
|
writer.WriteEnum(UnlockedFeatures, false);
|
||||||
// Write the teams.
|
writer.Write(Unknown);
|
||||||
foreach (Team team in Teams)
|
|
||||||
team.Save(writer.BaseStream);
|
// Write the teams.
|
||||||
}
|
foreach (Team team in Teams)
|
||||||
|
team.Save(writer.BaseStream);
|
||||||
/// <inheritdoc/>
|
}
|
||||||
public void Save(string fileName)
|
|
||||||
{
|
/// <inheritdoc/>
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
public void Save(string fileName)
|
||||||
Save(stream);
|
{
|
||||||
}
|
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
}
|
Save(stream);
|
||||||
|
}
|
||||||
/// <summary>
|
}
|
||||||
/// Represents unlockable features of the game.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
[Flags]
|
/// Represents unlockable features of the game.
|
||||||
public enum UnlockedFeatures : int
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum UnlockedFeatures : int
|
||||||
{
|
{
|
||||||
/// <summary>No features have been unlocked yet.</summary>
|
/// <summary>No features have been unlocked yet.</summary>
|
||||||
None,
|
None,
|
||||||
/// <summary>The utility weapon Laser Sight can be configured.</summary>
|
/// <summary>The utility weapon Laser Sight can be configured.</summary>
|
||||||
UtilityLaserSight = 1 << 0,
|
UtilityLaserSight = 1 << 0,
|
||||||
/// <summary>The utility weapon Fast Walk can be configured.</summary>
|
/// <summary>The utility weapon Fast Walk can be configured.</summary>
|
||||||
UtilityFastWalk = 1 << 1,
|
UtilityFastWalk = 1 << 1,
|
||||||
/// <summary>The utility weapon Invisibility can be configured.</summary>
|
/// <summary>The utility weapon Invisibility can be configured.</summary>
|
||||||
UtilityInvisibility = 1 << 2,
|
UtilityInvisibility = 1 << 2,
|
||||||
/// <summary>The utility weapon Low Gravity can be configured.</summary>
|
/// <summary>The utility weapon Low Gravity can be configured.</summary>
|
||||||
UtilityLowGravity = 1 << 3,
|
UtilityLowGravity = 1 << 3,
|
||||||
/// <summary>The utility weapon Jetpack can be configured.</summary>
|
/// <summary>The utility weapon Jetpack can be configured.</summary>
|
||||||
UtilityJetpack = 1 << 4,
|
UtilityJetpack = 1 << 4,
|
||||||
/// <summary>The Grenade upgrade can be enabled.</summary>
|
/// <summary>The Grenade upgrade can be enabled.</summary>
|
||||||
UpgradedGrenade = 1 << 8,
|
UpgradedGrenade = 1 << 8,
|
||||||
/// <summary>The Shotgun upgrade can be enabled.</summary>
|
/// <summary>The Shotgun upgrade can be enabled.</summary>
|
||||||
UpgradedShotgun = 1 << 9,
|
UpgradedShotgun = 1 << 9,
|
||||||
/// <summary>The cluster upgrade can be enabled.</summary>
|
/// <summary>The cluster upgrade can be enabled.</summary>
|
||||||
UpgradedClusters = 1 << 10,
|
UpgradedClusters = 1 << 10,
|
||||||
/// <summary>The Longbow upgrade can be enabled.</summary>
|
/// <summary>The Longbow upgrade can be enabled.</summary>
|
||||||
UpgradedLongbow = 1 << 11,
|
UpgradedLongbow = 1 << 11,
|
||||||
/// <summary>The upgrade of Super Sheeps to become Aqua Sheeps can be enabled.</summary>
|
/// <summary>The upgrade of Super Sheeps to become Aqua Sheeps can be enabled.</summary>
|
||||||
AquaSheep = 1 << 12,
|
AquaSheep = 1 << 12,
|
||||||
/// <summary>Worms can have infinite health and are killable only by drowning them.</summary>
|
/// <summary>Worms can have infinite health and are killable only by drowning them.</summary>
|
||||||
GodWorms = 1 << 16,
|
GodWorms = 1 << 16,
|
||||||
/// <summary>Blood effects when hitting worms can be enabled.</summary>
|
/// <summary>Blood effects when hitting worms can be enabled.</summary>
|
||||||
BloodFx = 1 << 17,
|
BloodFx = 1 << 17,
|
||||||
/// <summary>Every crate explodes with a sheep.</summary>
|
/// <summary>Every crate explodes with a sheep.</summary>
|
||||||
SheepHeaven = 1 << 18,
|
SheepHeaven = 1 << 18,
|
||||||
/// <summary>Map terrain can be indestructible and Full Wormage scheme is accessible.</summary>
|
/// <summary>Map terrain can be indestructible and Full Wormage scheme is accessible.</summary>
|
||||||
IndestructibleAndFullWormage = 1 << 24
|
IndestructibleAndFullWormage = 1 << 24
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,135 +1,132 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core;
|
using Syroot.Worms.Core;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame
|
namespace Syroot.Worms.Mgame
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents an IGD image container.
|
/// Represents an IGD image container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Igd : ILoadableFile
|
public class Igd : ILoadableFile
|
||||||
{
|
{
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Igd"/> class, loading data from the file with the given
|
/// Initializes a new instance of the <see cref="Igd"/> class, loading data from the file with the given
|
||||||
/// <paramref name="fileName"/>.
|
/// <paramref name="fileName"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
public Igd(string fileName) => Load(fileName);
|
public Igd(string fileName) => Load(fileName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Igd"/> class, loading data from the given
|
/// Initializes a new instance of the <see cref="Igd"/> class, loading data from the given
|
||||||
/// <paramref name="stream"/>.
|
/// <paramref name="stream"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
public Igd(Stream stream) => Load(stream);
|
public Igd(Stream stream) => Load(stream);
|
||||||
|
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
public int UnknownA { get; set; }
|
public int UnknownA { get; set; }
|
||||||
public int UnknownB { get; set; }
|
public int UnknownB { get; set; }
|
||||||
public byte[] UnknownC { get; set; }
|
public byte[] UnknownC { get; set; } = new byte[8];
|
||||||
public Point Center { get; set; }
|
public Point Center { get; set; }
|
||||||
public Size Size { get; set; }
|
public Size Size { get; set; }
|
||||||
public IList<IgdImage> Images { get; set; }
|
public IList<IgdImage> Images { get; set; } = new List<IgdImage>();
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Load(string fileName)
|
public void Load(string fileName)
|
||||||
{
|
{
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
Load(stream);
|
Load(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Load(Stream stream)
|
public void Load(Stream stream)
|
||||||
{
|
{
|
||||||
UnknownA = stream.ReadInt32();
|
UnknownA = stream.ReadInt32();
|
||||||
UnknownB = stream.ReadInt32();
|
UnknownB = stream.ReadInt32();
|
||||||
UnknownC = stream.ReadBytes(8);
|
UnknownC = stream.ReadBytes(8);
|
||||||
Center = new Point(stream.ReadInt32(), stream.ReadInt32());
|
Center = new Point(stream.ReadInt32(), stream.ReadInt32());
|
||||||
Size = new Size(stream.ReadInt32(), stream.ReadInt32());
|
Size = new Size(stream.ReadInt32(), stream.ReadInt32());
|
||||||
|
|
||||||
// Load the palette.
|
// Load the palette.
|
||||||
int colorCount = stream.ReadInt32();
|
int colorCount = stream.ReadInt32();
|
||||||
Color[] palette = new Color[colorCount];
|
Color[] palette = new Color[colorCount];
|
||||||
for (int i = 0; i < colorCount; i++)
|
for (int i = 0; i < colorCount; i++)
|
||||||
{
|
{
|
||||||
palette[i] = Color.FromArgb(stream.Read1Byte(), stream.Read1Byte(), stream.Read1Byte());
|
palette[i] = Color.FromArgb(stream.Read1Byte(), stream.Read1Byte(), stream.Read1Byte());
|
||||||
stream.Seek(1); // Ignore empty alpha.
|
stream.Seek(1); // Ignore empty alpha.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the images.
|
// Load the images.
|
||||||
int imageCount = stream.ReadInt32();
|
int imageCount = stream.ReadInt32();
|
||||||
Images = new List<IgdImage>(imageCount);
|
Images = new List<IgdImage>(imageCount);
|
||||||
for (int i = 0; i < imageCount; i++)
|
for (int i = 0; i < imageCount; i++)
|
||||||
{
|
{
|
||||||
IgdImage image = new IgdImage();
|
IgdImage image = new IgdImage();
|
||||||
image.UnknownA = stream.ReadInt32();
|
image.UnknownA = stream.ReadInt32();
|
||||||
int index = stream.ReadInt32();
|
int index = stream.ReadInt32();
|
||||||
if (index != i)
|
if (index != i)
|
||||||
throw new InvalidDataException("Read index does not match image index.");
|
throw new InvalidDataException("Read index does not match image index.");
|
||||||
image.UnknownB = stream.ReadInt32();
|
image.UnknownB = stream.ReadInt32();
|
||||||
image.UnknownC = stream.ReadInt32();
|
image.UnknownC = stream.ReadInt32();
|
||||||
image.Size = new Size(stream.ReadInt32(), stream.ReadInt32());
|
image.Size = new Size(stream.ReadInt32(), stream.ReadInt32());
|
||||||
image.Center = new Point(stream.ReadInt32(), stream.ReadInt32());
|
image.Center = new Point(stream.ReadInt32(), stream.ReadInt32());
|
||||||
|
|
||||||
// Decompress the data.
|
// Decompress the data.
|
||||||
int dataSize = stream.ReadInt32();
|
int dataSize = stream.ReadInt32();
|
||||||
int dataSizeCompressed = stream.ReadInt32();
|
int dataSizeCompressed = stream.ReadInt32();
|
||||||
image.RawBitmap = new RawBitmap
|
image.RawBitmap.Size = new Size(Algebra.NextMultiple(image.Size.Width, 4), image.Size.Height);
|
||||||
{
|
image.RawBitmap.Palette = palette;
|
||||||
Size = new Size(Algebra.NextMultiple(image.Size.Width, 4), image.Size.Height),
|
image.RawBitmap.Data = Decompress(stream, dataSizeCompressed, dataSize);
|
||||||
Palette = palette,
|
Images.Add(image);
|
||||||
Data = Decompress(stream, dataSizeCompressed, dataSize)
|
}
|
||||||
};
|
}
|
||||||
Images.Add(image);
|
|
||||||
}
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
}
|
|
||||||
|
private static byte[] Decompress(Stream stream, int compressedSize, int decompressedSize)
|
||||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
{
|
||||||
|
// Each input byte is either a byte of decompressed data or a marker for a following command (0xFF).
|
||||||
private static byte[] Decompress(Stream stream, int compressedSize, int decompressedSize)
|
// A command consists of 4 bytes which specify the range of bytes to copy from already decompressed data.
|
||||||
{
|
byte[] decompressed = new byte[decompressedSize];
|
||||||
// Each input byte is either a byte of decompressed data or a marker for a following command (0xFF).
|
int i = 0;
|
||||||
// A command consists of 4 bytes which specify the range of bytes to copy from already decompressed data.
|
|
||||||
byte[] decompressed = new byte[decompressedSize];
|
long endPosition = stream.Position + compressedSize - 2;
|
||||||
int i = 0;
|
while (stream.Position < endPosition)
|
||||||
|
{
|
||||||
long endPosition = stream.Position + compressedSize - 2;
|
byte b = stream.Read1Byte();
|
||||||
while (stream.Position < endPosition)
|
if (b == 0xFF)
|
||||||
{
|
{
|
||||||
byte b = stream.Read1Byte();
|
// Copy existing data.
|
||||||
if (b == 0xFF)
|
byte mask1 = stream.Read1Byte();
|
||||||
{
|
byte mask2 = stream.Read1Byte();
|
||||||
// Copy existing data.
|
int offset = mask2 & 0x0FFF | ((mask1 & 0x000F) << 8);
|
||||||
byte mask1 = stream.Read1Byte();
|
int bytesToCopy = stream.Read1Byte();
|
||||||
byte mask2 = stream.Read1Byte();
|
for (int j = 0; j < bytesToCopy; j++)
|
||||||
int offset = mask2 & 0x0FFF | ((mask1 & 0x000F) << 8);
|
{
|
||||||
int bytesToCopy = stream.Read1Byte();
|
int outIndex = i + j;
|
||||||
for (int j = 0; j < bytesToCopy; j++)
|
decompressed[outIndex] = decompressed[outIndex - offset];
|
||||||
{
|
}
|
||||||
int outIndex = i + j;
|
i += bytesToCopy;
|
||||||
decompressed[outIndex] = decompressed[outIndex - offset];
|
}
|
||||||
}
|
else
|
||||||
i += bytesToCopy;
|
{
|
||||||
}
|
// Raw transfer next byte.
|
||||||
else
|
decompressed[i++] = b;
|
||||||
{
|
}
|
||||||
// Raw transfer next byte.
|
}
|
||||||
decompressed[i++] = b;
|
|
||||||
}
|
// Validate remaining 2 check bytes and return the decompressed data if valid.
|
||||||
}
|
if (stream.ReadUInt16() != 0x0080)
|
||||||
|
throw new InvalidDataException("Invalid check bytes at end of compressed image data.");
|
||||||
// Validate remaining 2 check bytes and return the decompressed data if valid.
|
return decompressed;
|
||||||
if (stream.ReadUInt16() != 0x0080)
|
}
|
||||||
throw new InvalidDataException("Invalid check bytes at end of compressed image data.");
|
}
|
||||||
return decompressed;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -14,6 +14,6 @@ namespace Syroot.Worms.Mgame
|
|||||||
public int UnknownC { get; set; }
|
public int UnknownC { get; set; }
|
||||||
public Size Size { get; set; }
|
public Size Size { get; set; }
|
||||||
public Point Center { get; set; }
|
public Point Center { get; set; }
|
||||||
public RawBitmap RawBitmap { get; set; }
|
public RawBitmap RawBitmap { get; } = new RawBitmap { BitsPerPixel = 8 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,104 +1,105 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame
|
namespace Syroot.Worms.Mgame
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a KSF image container.
|
/// Represents a KSF image container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Ksf // TODO: Implement ILoadableFile
|
public class Ksf // TODO: Implement ILoadableFile
|
||||||
{
|
{
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Ksf"/> class.
|
/// Initializes a new instance of the <see cref="Ksf"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Ksf() { }
|
public Ksf() { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Ksf"/> class, loading data from the file with the given
|
/// Initializes a new instance of the <see cref="Ksf"/> class, loading data from the file with the given
|
||||||
/// <paramref name="fileName"/> and the colors in the provided <paramref name="palette"/>.
|
/// <paramref name="fileName"/> and the colors in the provided <paramref name="palette"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
/// <param name="palette">The color palette which is indexed by the image data.</param>
|
/// <param name="palette">The color palette which is indexed by the image data.</param>
|
||||||
public Ksf(string fileName, Palette palette) => Load(fileName, palette);
|
public Ksf(string fileName, KsfPalette palette) => Load(fileName, palette);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Ksf"/> class, loading data from the given
|
/// Initializes a new instance of the <see cref="Ksf"/> class, loading data from the given
|
||||||
/// <paramref name="stream"/> and the colors in the provided <paramref name="palette"/>.
|
/// <paramref name="stream"/> and the colors in the provided <paramref name="palette"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
/// <param name="palette">The color palette which is indexed by the image data.</param>
|
/// <param name="palette">The color palette which is indexed by the image data.</param>
|
||||||
public Ksf(Stream stream, Palette palette) => Load(stream, palette);
|
public Ksf(Stream stream, KsfPalette palette) => Load(stream, palette);
|
||||||
|
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the list of converted <see cref="Bitmap"/> instances stored in this KSF.
|
/// Gets or sets the list of converted <see cref="Bitmap"/> instances stored in this KSF.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<KsfImage> Images { get; set; } = new List<KsfImage>();
|
public IList<KsfImage> Images { get; set; } = new List<KsfImage>();
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads the data from the file with the given <paramref name="fileName"/> and the colors in the provided
|
/// Loads the data from the file with the given <paramref name="fileName"/> and the colors in the provided
|
||||||
/// <paramref name="palette"/>.
|
/// <paramref name="palette"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
/// <param name="palette">The color palette which is indexed by the image data.</param>
|
/// <param name="palette">The color palette which is indexed by the image data.</param>
|
||||||
public void Load(string fileName, Palette palette)
|
public void Load(string fileName, KsfPalette palette)
|
||||||
{
|
{
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
Load(stream, palette);
|
Load(stream, palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads the data from the given <paramref name="stream"/> and the colors in the provided
|
/// Loads the data from the given <paramref name="stream"/> and the colors in the provided
|
||||||
/// <paramref name="palette"/>.
|
/// <paramref name="palette"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
/// <param name="palette">The color palette which is indexed by the image data.</param>
|
/// <param name="palette">The color palette which is indexed by the image data.</param>
|
||||||
public void Load(Stream stream, Palette palette)
|
public void Load(Stream stream, KsfPalette palette)
|
||||||
{
|
{
|
||||||
int imageCount = stream.ReadInt32(); // Includes terminator.
|
int imageCount = stream.ReadInt32(); // Includes terminator.
|
||||||
_ = stream.ReadInt32(); // data size
|
_ = stream.ReadInt32(); // data size
|
||||||
|
|
||||||
// Read image headers. Terminating image is of 0 size and data offset at end of data block.
|
// Read image headers. Terminating image is of 0 size and data offset at end of data block.
|
||||||
KsfImage[] images = new KsfImage[imageCount];
|
KsfImage[] images = new KsfImage[imageCount];
|
||||||
int[] offsets = new int[imageCount];
|
int[] offsets = new int[imageCount];
|
||||||
Size[] sizes = new Size[imageCount];
|
Size[] sizes = new Size[imageCount];
|
||||||
for (int i = 0; i < imageCount; i++)
|
for (int i = 0; i < imageCount; i++)
|
||||||
{
|
{
|
||||||
KsfImage image = new KsfImage();
|
KsfImage image = new KsfImage();
|
||||||
offsets[i] = stream.ReadInt32();
|
offsets[i] = stream.ReadInt32();
|
||||||
image.Center = new Point(stream.ReadInt32(), stream.ReadInt32());
|
image.Center = new Point(stream.ReadInt32(), stream.ReadInt32());
|
||||||
sizes[i] = new Size(stream.ReadInt16(), stream.ReadInt16());
|
sizes[i] = new Size(stream.ReadInt16(), stream.ReadInt16());
|
||||||
images[i] = image;
|
images[i] = image;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert images.
|
// Convert images.
|
||||||
Images = new List<KsfImage>(imageCount);
|
Images = new List<KsfImage>(imageCount);
|
||||||
long dataStart = stream.Position;
|
long dataStart = stream.Position;
|
||||||
for (int i = 0; i < imageCount - 1; i++)
|
for (int i = 0; i < imageCount - 1; i++)
|
||||||
{
|
{
|
||||||
int offset = offsets[i];
|
int offset = offsets[i];
|
||||||
stream.Position = dataStart + offset;
|
stream.Position = dataStart + offset;
|
||||||
int dataLength = offsets[i + 1] - offset;
|
int dataLength = offsets[i + 1] - offset;
|
||||||
Size size = sizes[i];
|
Size size = sizes[i];
|
||||||
if (!size.IsEmpty)
|
if (!size.IsEmpty)
|
||||||
{
|
{
|
||||||
images[i].RawBitmap = new RawBitmap
|
images[i].RawBitmap = new RawBitmap
|
||||||
{
|
{
|
||||||
Size = size,
|
BitsPerPixel = 8,
|
||||||
Palette = palette.Colors,
|
Size = size,
|
||||||
Data = stream.ReadBytes(dataLength)
|
Palette = palette.Colors,
|
||||||
};
|
Data = stream.ReadBytes(dataLength)
|
||||||
}
|
};
|
||||||
|
}
|
||||||
Images.Add(images[i]);
|
|
||||||
}
|
Images.Add(images[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -10,6 +10,6 @@ namespace Syroot.Worms.Mgame
|
|||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
public Point Center { get; set; }
|
public Point Center { get; set; }
|
||||||
public RawBitmap RawBitmap { get; set; }
|
public RawBitmap? RawBitmap { get; set; } = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,69 +1,68 @@
|
|||||||
using System.Drawing;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.Drawing;
|
||||||
using Syroot.BinaryData;
|
using System.IO;
|
||||||
using Syroot.Worms.Core.Graphics;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.Graphics;
|
||||||
|
using Syroot.Worms.IO;
|
||||||
namespace Syroot.Worms.Mgame
|
|
||||||
{
|
namespace Syroot.Worms.Mgame
|
||||||
/// <summary>
|
{
|
||||||
/// Represents a PAL color palette.
|
/// <summary>
|
||||||
/// </summary>
|
/// Represents a color palette referenced by <see cref="KsfImage"/> data.
|
||||||
public class Palette : ILoadableFile, IPalette
|
/// </summary>
|
||||||
{
|
public class KsfPalette : ILoadableFile, IPalette
|
||||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
{
|
||||||
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
/// <summary>
|
|
||||||
/// The number of colors stored in a palette.
|
/// <summary>Number of colors stored in a palette.</summary>
|
||||||
/// </summary>
|
internal const int ColorCount = 256;
|
||||||
internal const int ColorCount = 256;
|
|
||||||
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
|
||||||
|
/// <summary>
|
||||||
/// <summary>
|
/// Initializes a new instance of the <see cref="KsfPalette"/> class.
|
||||||
/// Initializes a new instance of the <see cref="Palette"/> class.
|
/// </summary>
|
||||||
/// </summary>
|
public KsfPalette() { }
|
||||||
public Palette() { }
|
|
||||||
|
/// <summary>
|
||||||
/// <summary>
|
/// Initializes a new instance of the <see cref="KsfPalette"/> class, loading data from the file with the given
|
||||||
/// Initializes a new instance of the <see cref="Palette"/> class, loading data from the file with the given
|
/// <paramref name="fileName"/>.
|
||||||
/// <paramref name="fileName"/>.
|
/// </summary>
|
||||||
/// </summary>
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
public KsfPalette(string fileName) => Load(fileName);
|
||||||
public Palette(string fileName) => Load(fileName);
|
|
||||||
|
/// <summary>
|
||||||
/// <summary>
|
/// Initializes a new instance of the <see cref="KsfPalette"/> class, loading data from the given
|
||||||
/// Initializes a new instance of the <see cref="Palette"/> class, loading data from the given
|
/// <paramref name="stream"/>.
|
||||||
/// <paramref name="stream"/>.
|
/// </summary>
|
||||||
/// </summary>
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
public KsfPalette(Stream stream) => Load(stream);
|
||||||
public Palette(Stream stream) => Load(stream);
|
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
|
||||||
|
/// <summary>
|
||||||
/// <summary>
|
/// Gets the array of 256 colors stored in this palette.
|
||||||
/// Gets the array of 256 colors stored in this palette.
|
/// </summary>
|
||||||
/// </summary>
|
public IList<Color> Colors { get; set; } = new Color[ColorCount];
|
||||||
public Color[] Colors { get; set; } = new Color[ColorCount];
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
|
||||||
|
/// <inheritdoc/>
|
||||||
/// <inheritdoc/>
|
public void Load(string fileName)
|
||||||
public void Load(string fileName)
|
{
|
||||||
{
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
Load(stream);
|
||||||
Load(stream);
|
}
|
||||||
}
|
|
||||||
|
/// <inheritdoc/>
|
||||||
/// <inheritdoc/>
|
public void Load(Stream stream)
|
||||||
public void Load(Stream stream)
|
{
|
||||||
{
|
byte[] data = stream.ReadBytes(ColorCount * 3);
|
||||||
byte[] data = stream.ReadBytes(ColorCount * 3);
|
for (int i = 0; i < ColorCount; i++)
|
||||||
for (int i = 0; i < ColorCount; i++)
|
{
|
||||||
{
|
int offset = i * 3;
|
||||||
int offset = i * 3;
|
Colors[i] = Color.FromArgb(data[offset], data[offset + 1], data[offset + 2]);
|
||||||
Colors[i] = Color.FromArgb(data[offset], data[offset + 1], data[offset + 2]);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
@ -1,88 +1,86 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.MemoryMappedFiles;
|
using System.IO.MemoryMappedFiles;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame
|
namespace Syroot.Worms.Mgame
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the memory mapped file passed to the game executable, configuring the server address.
|
/// Represents the memory mapped file passed to the game executable, configuring the server address.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class LaunchConfig
|
public class LaunchConfig
|
||||||
{
|
{
|
||||||
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private string _passwordString;
|
private string _passwordString = String.Empty;
|
||||||
|
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name of the publisher providing the launch configuration. Must not exceed 15 characters.
|
/// Gets or sets the name of the publisher providing the launch configuration. Must not exceed 15 characters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Publisher { get; set; } = "MGAME";
|
public string Publisher { get; set; } = "MGAME";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets an unknown value. Must not exceed 19 characters.
|
/// Gets or sets an unknown value. Must not exceed 19 characters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Unknown { get; set; }
|
public string Unknown { get; set; } = String.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the address of the game server.
|
/// Gets or sets the address of the game server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPEndPoint ServerEndPoint { get; set; }
|
public IPEndPoint ServerEndPoint { get; set; } = new IPEndPoint(IPAddress.Any, 0);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the initially entered user name. Must not exceed 250 characters.
|
/// Gets or sets the initially entered user name. Must not exceed 250 characters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string UserName { get; set; }
|
public string UserName { get; set; } = String.Empty;
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="MemoryMappedFile"/> which is then read by the game client.
|
/// Creates a <see cref="MemoryMappedFile"/> which is then read by the game client.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mapName">The name under which the game client can access the mapping.</param>
|
/// <param name="mapName">The name under which the game client can access the mapping.</param>
|
||||||
/// <returns>The <see cref="MemoryMappedFile"/>.</returns>
|
/// <returns>The <see cref="MemoryMappedFile"/>.</returns>
|
||||||
public MemoryMappedFile CreateMappedFile(string mapName)
|
public MemoryMappedFile CreateMappedFile(string mapName)
|
||||||
{
|
{
|
||||||
MemoryMappedFile mappedFile = MemoryMappedFile.CreateNew(mapName, 1266,
|
MemoryMappedFile mappedFile = MemoryMappedFile.CreateNew(mapName, 1266,
|
||||||
MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None, HandleInheritability.Inheritable);
|
MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None, HandleInheritability.Inheritable);
|
||||||
using (BinaryStream stream = new BinaryStream(mappedFile.CreateViewStream(),
|
using (BinaryStream stream = new BinaryStream(mappedFile.CreateViewStream(),
|
||||||
encoding: Encodings.Korean, stringCoding: StringCoding.ZeroTerminated))
|
encoding: Encodings.Korean, stringCoding: StringCoding.ZeroTerminated))
|
||||||
{
|
{
|
||||||
stream.WriteString(Publisher, 16);
|
stream.WriteFixedString(Publisher, 16);
|
||||||
stream.WriteString(Unknown, 20);
|
stream.WriteFixedString(Unknown, 20);
|
||||||
stream.WriteString(_passwordString, 30);
|
stream.WriteFixedString(_passwordString, 30);
|
||||||
stream.WriteString($"UID={UserName}", 256);
|
stream.WriteFixedString($"UID={UserName}", 256);
|
||||||
stream.WriteString(ServerEndPoint.Address.ToString(), 30);
|
stream.WriteFixedString(ServerEndPoint.Address.ToString(), 30);
|
||||||
stream.WriteString(ServerEndPoint.Port.ToString(), 914);
|
stream.WriteFixedString(ServerEndPoint.Port.ToString(), 914);
|
||||||
}
|
}
|
||||||
return mappedFile;
|
return mappedFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Decrypts the password stored in the configuration as previously set through
|
/// Decrypts the password stored in the configuration as previously set through
|
||||||
/// <see cref="SetPassword(String, Int32)"/>.
|
/// <see cref="SetPassword(String, Int32)"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string GetPassword()
|
public string GetPassword()
|
||||||
{
|
{
|
||||||
if (_passwordString == null)
|
if (String.IsNullOrEmpty(_passwordString))
|
||||||
return null;
|
return String.Empty;
|
||||||
string[] parts = _passwordString.Split(';');
|
string[] parts = _passwordString.Split(';');
|
||||||
return PasswordCrypto.Decrypt(parts[1], UInt32.Parse(parts[2]));
|
return PasswordCrypto.Decrypt(parts[1], UInt32.Parse(parts[2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Encrypts the given <paramref name="password"/> with the provided initial encryption <paramref name="key"/>
|
/// Encrypts the given <paramref name="password"/> with the provided initial encryption <paramref name="key"/>
|
||||||
/// and stores the result in the launch config.
|
/// and stores the result in the launch config.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="password">The password to store encrypted.</param>
|
/// <param name="password">The password to store encrypted.</param>
|
||||||
/// <param name="key">The key to encrypt with.</param>
|
/// <param name="key">The key to encrypt with.</param>
|
||||||
public void SetPassword(string password, int key = 1000)
|
public void SetPassword(string password, int key = 1000)
|
||||||
{
|
=> _passwordString = $";{PasswordCrypto.Encrypt(password, (uint)key)};{key}";
|
||||||
_passwordString = $";{PasswordCrypto.Encrypt(password, (uint)key)};{key}";
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Mgame
|
namespace Syroot.Worms.Mgame
|
||||||
{
|
{
|
||||||
@ -32,7 +32,7 @@ namespace Syroot.Worms.Mgame
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the list of <see cref="LpdElement"/> instances stored by this class.
|
/// Gets or sets the list of <see cref="LpdElement"/> instances stored by this class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<LpdElement> Elements { get; set; }
|
public IList<LpdElement> Elements { get; set; } = new List<LpdElement>();
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -1,32 +1,30 @@
|
|||||||
using System.Drawing;
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
namespace Syroot.Worms.Mgame
|
|
||||||
{
|
namespace Syroot.Worms.Mgame
|
||||||
/// <summary>
|
{
|
||||||
/// Represents a single UI element described in an <see cref="Lpd"/> file.
|
/// <summary>
|
||||||
/// </summary>
|
/// Represents a single UI element described in an <see cref="Lpd"/> file.
|
||||||
public class LpdElement
|
/// </summary>
|
||||||
{
|
public class LpdElement
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
{
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a the relative path to the IGD storing the image of this element.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets a the relative path to the IGD storing the images of this element.
|
||||||
public string FileName { get; set; }
|
/// </summary>
|
||||||
|
public string FileName { get; set; } = String.Empty;
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a the area at which the UI element appears.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets a the area at which the UI element appears.
|
||||||
public Rectangle Rectangle { get; set; }
|
/// </summary>
|
||||||
|
public Rectangle Rectangle { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets an unknown value.
|
public int Properties { get; set; }
|
||||||
/// </summary>
|
|
||||||
public int Properties { get; set; }
|
/// <summary>
|
||||||
|
/// Gets or sets a value which must lie between 0-4 (inclusive) for the game to accept the element.
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// Gets or sets a value which must lie between 0-4 (inclusive) for the game to accept the element.
|
public int Version { get; set; }
|
||||||
/// </summary>
|
}
|
||||||
public int Version { get; set; }
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>Syroot.Worms.Mgame</AssemblyName>
|
<AssemblyName>Syroot.Worms.Mgame</AssemblyName>
|
||||||
<Description>.NET library for loading and modifying files of Mgame Worms clients.</Description>
|
<Description>.NET library for loading and modifying files of Mgame Worms clients.</Description>
|
||||||
<PackageReleaseNotes>Fix issues when loading and saving some formats.</PackageReleaseNotes>
|
<PackageReleaseNotes>Overhaul implementation and documentation.</PackageReleaseNotes>
|
||||||
<PackageTags>$(PackageTags);online worms;worms world party aqua</PackageTags>
|
<PackageTags>$(PackageTags);online worms;worms world party aqua</PackageTags>
|
||||||
<Version>3.2.0</Version>
|
<Version>4.0.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Syroot.Worms\Syroot.Worms.csproj" />
|
<ProjectReference Include="..\Syroot.Worms\Syroot.Worms.csproj" />
|
||||||
|
@ -1,160 +0,0 @@
|
|||||||
using System.Drawing;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text;
|
|
||||||
using Syroot.BinaryData;
|
|
||||||
using Syroot.Worms.Core.IO;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.WorldParty
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents map configuration stored by the land generator in LAND.DAT files.
|
|
||||||
/// Used by WWP. S. https://worms2d.info/Land_Data_file.
|
|
||||||
/// </summary>
|
|
||||||
public class LandData : ILoadableFile, ISaveableFile
|
|
||||||
{
|
|
||||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
private const int _signature = 0x1A444E4C; // "LND", 0x1A
|
|
||||||
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="LandData"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public LandData() { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given
|
|
||||||
/// <see cref="Stream"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
|
||||||
public LandData(Stream stream) => Load(stream);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given file.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
|
||||||
public LandData(string fileName) => Load(fileName);
|
|
||||||
|
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the size of the landscape in pixels.
|
|
||||||
/// </summary>
|
|
||||||
public Size Size { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether an indestructible top border will be enabled.
|
|
||||||
/// </summary>
|
|
||||||
public bool TopBorder { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the height of the water in pixels.
|
|
||||||
/// </summary>
|
|
||||||
public int WaterHeight { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets an array of coordinates at which objects can be placed.
|
|
||||||
/// </summary>
|
|
||||||
public Point[] ObjectLocations { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the visual foreground image.
|
|
||||||
/// </summary>
|
|
||||||
public Img Foreground { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the collision mask of the landscape.
|
|
||||||
/// </summary>
|
|
||||||
public Img CollisionMask { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the visual background image.
|
|
||||||
/// </summary>
|
|
||||||
public Img Background { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the path to the land image file.
|
|
||||||
/// </summary>
|
|
||||||
public string LandTexturePath { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the path to the Water.dir file.
|
|
||||||
/// </summary>
|
|
||||||
public string WaterDirPath { get; set; }
|
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Load(Stream stream)
|
|
||||||
{
|
|
||||||
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
|
||||||
|
|
||||||
// Read the header.
|
|
||||||
if (reader.ReadInt32() != _signature)
|
|
||||||
throw new InvalidDataException("Invalid LND file signature.");
|
|
||||||
int fileSize = reader.ReadInt32();
|
|
||||||
|
|
||||||
// Read the data.
|
|
||||||
Size = reader.ReadStruct<Size>();
|
|
||||||
TopBorder = reader.ReadBoolean(BooleanCoding.Dword);
|
|
||||||
WaterHeight = reader.ReadInt32();
|
|
||||||
|
|
||||||
// Read the possible object coordinate array.
|
|
||||||
ObjectLocations = reader.ReadStructs<Point>(reader.ReadInt32());
|
|
||||||
|
|
||||||
// Read the image data.
|
|
||||||
Foreground = new Img(stream, true);
|
|
||||||
CollisionMask = new Img(stream, true);
|
|
||||||
Background = new Img(stream, true);
|
|
||||||
|
|
||||||
// Read the file paths.
|
|
||||||
LandTexturePath = reader.ReadString(StringCoding.ByteCharCount);
|
|
||||||
WaterDirPath = reader.ReadString(StringCoding.ByteCharCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Load(string fileName)
|
|
||||||
{
|
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
|
||||||
Load(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Save(Stream stream)
|
|
||||||
{
|
|
||||||
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
|
||||||
|
|
||||||
// Write the header.
|
|
||||||
writer.Write(_signature);
|
|
||||||
uint fileSizeOffset = writer.ReserveOffset();
|
|
||||||
|
|
||||||
// Write the data.
|
|
||||||
writer.WriteStruct(Size);
|
|
||||||
writer.Write(TopBorder, BooleanCoding.Dword);
|
|
||||||
writer.Write(WaterHeight);
|
|
||||||
|
|
||||||
// Write the possible object coordinate array.
|
|
||||||
writer.Write(ObjectLocations.Length);
|
|
||||||
writer.WriteStructs(ObjectLocations);
|
|
||||||
|
|
||||||
// Write the image data.
|
|
||||||
Foreground.Save(writer.BaseStream, false, true);
|
|
||||||
CollisionMask.Save(writer.BaseStream, false, true);
|
|
||||||
Background.Save(writer.BaseStream, false, true);
|
|
||||||
|
|
||||||
// Write the file paths.
|
|
||||||
writer.Write(LandTexturePath, StringCoding.ByteCharCount);
|
|
||||||
writer.Write(WaterDirPath, StringCoding.ByteCharCount);
|
|
||||||
|
|
||||||
writer.SatisfyOffset(fileSizeOffset, (int)writer.Position);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Save(string fileName)
|
|
||||||
{
|
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
|
||||||
Save(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,9 +3,9 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>Syroot.Worms.WorldParty</AssemblyName>
|
<AssemblyName>Syroot.Worms.WorldParty</AssemblyName>
|
||||||
<Description>.NET library for loading and modifying files of Team17's Worms World Party.</Description>
|
<Description>.NET library for loading and modifying files of Team17's Worms World Party.</Description>
|
||||||
<PackageReleaseNotes>Fix issues when loading and saving some formats.</PackageReleaseNotes>
|
<PackageReleaseNotes>Overhaul implementation and documentation.</PackageReleaseNotes>
|
||||||
<PackageTags>$(PackageTags);worms world party</PackageTags>
|
<PackageTags>$(PackageTags);worms world party</PackageTags>
|
||||||
<Version>3.2.0</Version>
|
<Version>4.0.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Syroot.Worms\Syroot.Worms.csproj" />
|
<ProjectReference Include="..\Syroot.Worms\Syroot.Worms.csproj" />
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,122 +1,118 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.WorldParty
|
namespace Syroot.Worms.WorldParty
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the list of teams and unlocked game features stored in WGT files.
|
/// Represents the list of teams and unlocked game features stored in WGT files.
|
||||||
/// Used by WWP. See https://worms2d.info/File_formats.
|
/// Used by WWP. See https://worms2d.info/File_formats.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TeamContainer : ILoadableFile, ISaveableFile
|
public class TeamContainer : ILoadableFile, ISaveableFile
|
||||||
{
|
{
|
||||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
private const string _signature = "WWP"; // 0-terminated.
|
private const uint _signature = 0x00505757; // "WWP\0"
|
||||||
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TeamContainer"/> class.
|
/// Initializes a new instance of the <see cref="TeamContainer"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TeamContainer() => Teams = new List<Team>();
|
public TeamContainer() { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TeamContainer"/> class, loading the data from the given
|
/// Initializes a new instance of the <see cref="TeamContainer"/> class, loading the data from the given
|
||||||
/// <see cref="Stream"/>.
|
/// <see cref="Stream"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
public TeamContainer(Stream stream) => Load(stream);
|
public TeamContainer(Stream stream) => Load(stream);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TeamContainer"/> class, loading the data from the given file.
|
/// Initializes a new instance of the <see cref="TeamContainer"/> class, loading the data from the given file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
public TeamContainer(string fileName) => Load(fileName);
|
public TeamContainer(string fileName) => Load(fileName);
|
||||||
|
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value possibly indicating a version of the file format.
|
/// Gets or sets a value possibly indicating a version of the file format.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte Version { get; set; }
|
public byte Version { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
public byte Unknown1 { get; set; }
|
||||||
/// Gets or sets an unknown value.
|
|
||||||
/// </summary>
|
public byte Unknown2 { get; set; }
|
||||||
public byte Unknown1 { get; set; }
|
|
||||||
|
/// <summary>
|
||||||
/// <summary>
|
/// Gets or sets 840 unknown bytes, all possibly 0.
|
||||||
/// Gets or sets an unknown value.
|
/// </summary>
|
||||||
/// </summary>
|
public byte[] Unknown3 { get; set; } = new byte[840];
|
||||||
public byte Unknown2 { get; set; }
|
|
||||||
|
/// <summary>
|
||||||
/// <summary>
|
/// Gets or sets the list of <see cref="Team"/> instances stored.
|
||||||
/// Gets or sets 840 unknown bytes, all possibly 0.
|
/// </summary>
|
||||||
/// </summary>
|
public IList<Team> Teams { get; set; } = new List<Team>();
|
||||||
public byte[] Unknown3 { get; set; }
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the list of <see cref="Team"/> instances stored.
|
/// <inheritdoc/>
|
||||||
/// </summary>
|
public void Load(Stream stream)
|
||||||
public List<Team> Teams { get; set; }
|
{
|
||||||
|
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
|
||||||
|
// Read the header.
|
||||||
/// <inheritdoc/>
|
if (reader.ReadUInt32() != _signature)
|
||||||
public void Load(Stream stream)
|
throw new InvalidDataException("Invalid WWP file signature.");
|
||||||
{
|
Version = reader.Read1Byte(); // Really version?
|
||||||
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
|
||||||
|
// Read global settings.
|
||||||
// Read the header.
|
byte teamCount = reader.Read1Byte();
|
||||||
if (reader.ReadString(StringCoding.ZeroTerminated) != _signature)
|
Unknown1 = reader.Read1Byte();
|
||||||
throw new InvalidDataException("Invalid WWP file signature.");
|
Unknown2 = reader.Read1Byte();
|
||||||
Version = reader.Read1Byte(); // Really version?
|
Unknown3 = reader.ReadBytes(840);
|
||||||
|
|
||||||
// Read global settings.
|
// Read the teams.
|
||||||
byte teamCount = reader.Read1Byte();
|
Teams = new List<Team>(teamCount);
|
||||||
Unknown1 = reader.Read1Byte();
|
while (teamCount-- > 0)
|
||||||
Unknown2 = reader.Read1Byte();
|
Teams.Add(reader.Load<Team>());
|
||||||
Unknown3 = reader.ReadBytes(840);
|
}
|
||||||
|
|
||||||
// Read the teams.
|
/// <inheritdoc/>
|
||||||
Teams = new List<Team>(reader.Load<Team>(teamCount));
|
public void Load(string fileName)
|
||||||
}
|
{
|
||||||
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
/// <inheritdoc/>
|
Load(stream);
|
||||||
public void Load(string fileName)
|
}
|
||||||
{
|
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
/// <inheritdoc/>
|
||||||
Load(stream);
|
public void Save(Stream stream)
|
||||||
}
|
{
|
||||||
|
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Save(Stream stream)
|
// Write the header.
|
||||||
{
|
writer.Write(_signature);
|
||||||
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
writer.Write(Version);
|
||||||
|
|
||||||
// Write the header.
|
// Write global settings.
|
||||||
writer.Write(_signature, StringCoding.ZeroTerminated);
|
writer.Write((byte)Teams.Count);
|
||||||
writer.Write(Version);
|
writer.Write(Unknown1);
|
||||||
|
writer.Write(Unknown2);
|
||||||
// Write global settings.
|
writer.Write(Unknown3);
|
||||||
writer.Write((byte)Teams.Count);
|
|
||||||
writer.Write(Unknown1);
|
// Write the teams.
|
||||||
writer.Write(Unknown2);
|
foreach (Team team in Teams)
|
||||||
writer.Write(Unknown3);
|
team.Save(writer.BaseStream);
|
||||||
|
}
|
||||||
// Write the teams.
|
|
||||||
foreach (Team team in Teams)
|
/// <inheritdoc/>
|
||||||
team.Save(writer.BaseStream);
|
public void Save(string fileName)
|
||||||
}
|
{
|
||||||
|
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
/// <inheritdoc/>
|
Save(stream);
|
||||||
public void Save(string fileName)
|
}
|
||||||
{
|
}
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
}
|
||||||
Save(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,167 +1,173 @@
|
|||||||
using System.Drawing;
|
using System;
|
||||||
using System.IO;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Drawing;
|
||||||
using Syroot.BinaryData;
|
using System.IO;
|
||||||
using Syroot.Worms.Core.IO;
|
using System.Text;
|
||||||
|
using Syroot.BinaryData;
|
||||||
namespace Syroot.Worms.Worms2
|
using Syroot.Worms.IO;
|
||||||
{
|
|
||||||
/// <summary>
|
namespace Syroot.Worms.Worms2
|
||||||
/// Represents map configuration stored by the land generator in LAND.DAT files.
|
{
|
||||||
/// Used by W2. S. https://worms2d.info/Land_Data_file.
|
/// <summary>
|
||||||
/// </summary>
|
/// Represents map configuration stored by the land generator in LAND.DAT files.
|
||||||
public class LandData : ILoadableFile, ISaveableFile
|
/// Used by W2 and OW. S. https://worms2d.info/Land_Data_file.
|
||||||
{
|
/// </summary>
|
||||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
public class LandData : ILoadableFile, ISaveableFile
|
||||||
|
{
|
||||||
private const int _signature = 0x1A444E4C; // "LND", 0x1A
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
private const int _signature = 0x1A444E4C; // "LND\x1A"
|
||||||
|
|
||||||
/// <summary>
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
/// Initializes a new instance of the <see cref="LandData"/> class.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public LandData() { }
|
/// Initializes a new instance of the <see cref="LandData"/> class.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public LandData() { }
|
||||||
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given
|
|
||||||
/// <see cref="Stream"/>.
|
/// <summary>
|
||||||
/// </summary>
|
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// <see cref="Stream"/>.
|
||||||
public LandData(Stream stream) => Load(stream);
|
/// </summary>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
/// <summary>
|
public LandData(Stream stream) => Load(stream);
|
||||||
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given file.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// Initializes a new instance of the <see cref="LandData"/> class, loading the data from the given file.
|
||||||
public LandData(string fileName) => Load(fileName);
|
/// </summary>
|
||||||
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
public LandData(string fileName) => Load(fileName);
|
||||||
|
|
||||||
/// <summary>
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
/// Gets or sets the size of the landscape in pixels.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public Size Size { get; set; }
|
/// Gets or sets the size of the landscape in pixels.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public Size Size { get; set; }
|
||||||
/// Gets or sets a value indicating whether an indestructible top border will be enabled.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public bool TopBorder { get; set; }
|
/// Gets or sets a value indicating whether an indestructible top border will be enabled.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public bool TopBorder { get; set; }
|
||||||
/// Gets or sets an array of coordinates at which objects can be placed.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public Point[] ObjectLocations { get; set; }
|
/// Gets or sets an array of coordinates at which objects can be placed.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public IList<Point> ObjectLocations { get; set; } = new List<Point>();
|
||||||
/// Gets or sets an unknown value, seeming to be 0 most of the time.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public int Unknown { get; set; }
|
/// Gets or sets an unknown value, seeming to be 0 most of the time.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public int Unknown { get; set; }
|
||||||
/// Gets or sets the visual foreground image.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public Img Foreground { get; set; }
|
/// Gets or sets the visual foreground image.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public Img Foreground { get; set; } = new Img();
|
||||||
/// Gets or sets the collision mask of the landscape.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public Img CollisionMask { get; set; }
|
/// Gets or sets the collision mask of the landscape.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public Img CollisionMask { get; set; } = new Img();
|
||||||
/// Gets or sets the visual background image.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public Img Background { get; set; }
|
/// Gets or sets the visual background image.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public Img Background { get; set; } = new Img();
|
||||||
/// Gets or sets an image of unknown use.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public Img UnknownImage { get; set; }
|
/// Gets or sets an image of unknown use.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public Img UnknownImage { get; set; } = new Img();
|
||||||
/// Gets or sets the path to the land image file.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public string LandTexturePath { get; set; }
|
/// Gets or sets the path to the land image file.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public string LandTexturePath { get; set; } = String.Empty;
|
||||||
/// Gets or sets the path to the Water.dir file.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public string WaterDirPath { get; set; }
|
/// Gets or sets the path to the Water.dir file.
|
||||||
|
/// </summary>
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
public string WaterDirPath { get; set; } = String.Empty;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
public void Load(Stream stream)
|
|
||||||
{
|
/// <inheritdoc/>
|
||||||
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
public void Load(Stream stream)
|
||||||
|
{
|
||||||
// Read the header.
|
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
if (reader.ReadInt32() != _signature)
|
|
||||||
throw new InvalidDataException("Invalid LND file signature.");
|
// Read the header.
|
||||||
int fileSize = reader.ReadInt32();
|
if (reader.ReadInt32() != _signature)
|
||||||
|
throw new InvalidDataException("Invalid LND file signature.");
|
||||||
// Read the data.
|
int fileSize = reader.ReadInt32();
|
||||||
Size = reader.ReadStruct<Size>();
|
|
||||||
TopBorder = reader.ReadBoolean(BooleanCoding.Dword);
|
// Read the data.
|
||||||
|
Size = reader.ReadStruct<Size>();
|
||||||
// Read the possible object coordinate array.
|
TopBorder = reader.ReadBoolean(BooleanCoding.Dword);
|
||||||
ObjectLocations = reader.ReadStructs<Point>(reader.ReadInt32());
|
|
||||||
Unknown = reader.ReadInt32();
|
// Read the possible object coordinate array.
|
||||||
|
ObjectLocations = new List<Point>();
|
||||||
// Read the image data.
|
int locationCount = reader.ReadInt32();
|
||||||
Foreground = reader.Load<Img>();
|
for (int i = 0; i < locationCount; i++)
|
||||||
CollisionMask = reader.Load<Img>();
|
ObjectLocations.Add(reader.ReadStruct<Point>());
|
||||||
Background = reader.Load<Img>();
|
Unknown = reader.ReadInt32();
|
||||||
UnknownImage = reader.Load<Img>();
|
|
||||||
|
// Read the image data.
|
||||||
// Read the file paths.
|
Foreground = reader.Load<Img>();
|
||||||
LandTexturePath = reader.ReadString(StringCoding.ByteCharCount);
|
CollisionMask = reader.Load<Img>();
|
||||||
WaterDirPath = reader.ReadString(StringCoding.ByteCharCount);
|
Background = reader.Load<Img>();
|
||||||
}
|
UnknownImage = reader.Load<Img>();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
// Read the file paths.
|
||||||
public void Load(string fileName)
|
LandTexturePath = reader.ReadString(StringCoding.ByteCharCount);
|
||||||
{
|
WaterDirPath = reader.ReadString(StringCoding.ByteCharCount);
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
}
|
||||||
Load(stream);
|
|
||||||
}
|
/// <inheritdoc/>
|
||||||
|
public void Load(string fileName)
|
||||||
/// <inheritdoc/>
|
{
|
||||||
public void Save(Stream stream)
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
{
|
Load(stream);
|
||||||
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
}
|
||||||
|
|
||||||
// Write the header.
|
/// <inheritdoc/>
|
||||||
writer.Write(_signature);
|
public void Save(Stream stream)
|
||||||
uint fileSizeOffset = writer.ReserveOffset();
|
{
|
||||||
|
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
// Write the data.
|
|
||||||
writer.WriteStruct(Size);
|
// Write the header.
|
||||||
writer.Write(TopBorder, BooleanCoding.Dword);
|
writer.Write(_signature);
|
||||||
|
uint fileSizeOffset = writer.ReserveOffset();
|
||||||
// Write the possible object coordinate array.
|
|
||||||
writer.Write(ObjectLocations.Length);
|
// Write the data.
|
||||||
writer.WriteStructs(ObjectLocations);
|
writer.WriteStruct(Size);
|
||||||
writer.Write(Unknown);
|
writer.Write(TopBorder, BooleanCoding.Dword);
|
||||||
|
|
||||||
// Write the image data.
|
// Write the possible object coordinate array.
|
||||||
Foreground.Save(writer.BaseStream);
|
writer.Write(ObjectLocations.Count);
|
||||||
CollisionMask.Save(writer.BaseStream);
|
for (int i = 0; i < ObjectLocations.Count; i++)
|
||||||
Background.Save(writer.BaseStream);
|
writer.WriteStruct(ObjectLocations[i]);
|
||||||
UnknownImage.Save(writer.BaseStream);
|
writer.Write(Unknown);
|
||||||
|
|
||||||
// Write the file paths.
|
// Write the image data.
|
||||||
writer.Write(LandTexturePath, StringCoding.ByteCharCount);
|
Foreground.Save(writer.BaseStream);
|
||||||
writer.Write(WaterDirPath, StringCoding.ByteCharCount);
|
CollisionMask.Save(writer.BaseStream);
|
||||||
|
Background.Save(writer.BaseStream);
|
||||||
writer.SatisfyOffset(fileSizeOffset, (int)writer.Position);
|
UnknownImage.Save(writer.BaseStream);
|
||||||
}
|
|
||||||
|
// Write the file paths.
|
||||||
/// <inheritdoc/>
|
writer.Write(LandTexturePath, StringCoding.ByteCharCount);
|
||||||
public void Save(string fileName)
|
writer.Write(WaterDirPath, StringCoding.ByteCharCount);
|
||||||
{
|
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
writer.SatisfyOffset(fileSizeOffset, (uint)writer.Position);
|
||||||
Save(stream);
|
}
|
||||||
}
|
|
||||||
}
|
/// <inheritdoc/>
|
||||||
}
|
public void Save(string fileName)
|
||||||
|
{
|
||||||
|
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
|
Save(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,222 +3,98 @@ namespace Syroot.Worms.Worms2
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the method to determine the next turn's worm.
|
/// Represents the method to determine the next turn's worm.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum SchemeWormSelect : int
|
public enum WormSelect : int
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>Worms are selected in the order in which they appear in the team.</summary>
|
||||||
/// Worms are selected in the order in which they appear in the team.
|
Sequential,
|
||||||
/// </summary>
|
/// <summary>Worms are selected randomly.</summary>
|
||||||
Sequential = 0,
|
Random,
|
||||||
|
/// <summary>Worms are selected by a computed rating system.</summary>
|
||||||
/// <summary>
|
Intelligent,
|
||||||
/// Worms are selected randomly.
|
/// <summary>Worms are selected by the player.</summary>
|
||||||
/// </summary>
|
Manual
|
||||||
Random = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Worms are selected by a computed rating system.
|
|
||||||
/// </summary>
|
|
||||||
Intelligent = 2,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Worms are selected by the player.
|
|
||||||
/// </summary>
|
|
||||||
Manual = 3
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the weapons in the game.
|
/// Represents the weapons in the game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum SchemeWeapon
|
public enum Weapon
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>The Bazooka weapon.</summary>
|
||||||
/// The Bazooka weapon.
|
|
||||||
/// </summary>
|
|
||||||
Bazooka,
|
Bazooka,
|
||||||
|
/// <summary>The Homing Missile weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Homing Missile weapon.
|
|
||||||
/// </summary>
|
|
||||||
HomingMissile,
|
HomingMissile,
|
||||||
|
/// <summary>The Grenade weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Grenade weapon.
|
|
||||||
/// </summary>
|
|
||||||
Grenade,
|
Grenade,
|
||||||
|
/// <summary>The Cluster Bomb weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Cluster Bomb weapon.
|
|
||||||
/// </summary>
|
|
||||||
ClusterBomb,
|
ClusterBomb,
|
||||||
|
/// <summary>The Banana Bomb weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Banana Bomb weapon.
|
|
||||||
/// </summary>
|
|
||||||
BananaBomb,
|
BananaBomb,
|
||||||
|
/// <summary>The Holy Hand Grenade weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Holy Hand Grenade weapon.
|
|
||||||
/// </summary>
|
|
||||||
HolyHandGrenade,
|
HolyHandGrenade,
|
||||||
|
/// <summary>The Homing Cluster Bomb weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Homing Cluster Bomb weapon.
|
|
||||||
/// </summary>
|
|
||||||
HomingClusterBomb,
|
HomingClusterBomb,
|
||||||
|
/// <summary>The Petrol Bomb weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Petrol Bomb weapon.
|
|
||||||
/// </summary>
|
|
||||||
PetrolBomb,
|
PetrolBomb,
|
||||||
|
/// <summary>The Shotgun weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Shotgun weapon.
|
|
||||||
/// </summary>
|
|
||||||
Shotgun,
|
Shotgun,
|
||||||
|
/// <summary>The Handgun weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Handgun weapon.
|
|
||||||
/// </summary>
|
|
||||||
Handgun,
|
Handgun,
|
||||||
|
/// <summary>The Uzi weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Uzi weapon.
|
|
||||||
/// </summary>
|
|
||||||
Uzi,
|
Uzi,
|
||||||
|
/// <summary>The Minigun weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Minigun weapon.
|
|
||||||
/// </summary>
|
|
||||||
Minigun,
|
Minigun,
|
||||||
|
/// <summary>The Firepunch weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Firepunch weapon.
|
|
||||||
/// </summary>
|
|
||||||
Firepunch,
|
Firepunch,
|
||||||
|
/// <summary>The Dragonball weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Dragonball weapon.
|
|
||||||
/// </summary>
|
|
||||||
Dragonball,
|
Dragonball,
|
||||||
|
/// <summary>The Kamikaze weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Kamikaze weapon.
|
|
||||||
/// </summary>
|
|
||||||
Kamikaze,
|
Kamikaze,
|
||||||
|
/// <summary>The Dynamite weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Dynamite weapon.
|
|
||||||
/// </summary>
|
|
||||||
Dynamite,
|
Dynamite,
|
||||||
|
/// <summary>The Mine weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Mine weapon.
|
|
||||||
/// </summary>
|
|
||||||
Mine,
|
Mine,
|
||||||
|
/// <summary>The Ming Vase weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Ming Vase weapon.
|
|
||||||
/// </summary>
|
|
||||||
MingVase,
|
MingVase,
|
||||||
|
/// <summary>The Airstrike weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Airstrike weapon.
|
|
||||||
/// </summary>
|
|
||||||
Airstrike,
|
Airstrike,
|
||||||
|
/// <summary>The Homing Airstrike weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Homing Airstrike weapon.
|
|
||||||
/// </summary>
|
|
||||||
HomingAirstrike,
|
HomingAirstrike,
|
||||||
|
/// <summary>The Napalm Strike weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Napalm Strike weapon.
|
|
||||||
/// </summary>
|
|
||||||
NapalmStrike,
|
NapalmStrike,
|
||||||
|
/// <summary>The Mail Strike weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Mail Strike weapon.
|
|
||||||
/// </summary>
|
|
||||||
MailStrike,
|
MailStrike,
|
||||||
|
/// <summary>The Girder weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Girder weapon.
|
|
||||||
/// </summary>
|
|
||||||
Girder,
|
Girder,
|
||||||
|
/// <summary>The Pneumatic Drill weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Pneumatic Drill weapon.
|
|
||||||
/// </summary>
|
|
||||||
PneumaticDrill,
|
PneumaticDrill,
|
||||||
|
/// <summary>The Baseball Bat weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Baseball Bat weapon.
|
|
||||||
/// </summary>
|
|
||||||
BaseballBat,
|
BaseballBat,
|
||||||
|
/// <summary>The Prod weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Prod weapon.
|
|
||||||
/// </summary>
|
|
||||||
Prod,
|
Prod,
|
||||||
|
/// <summary>The Teleport weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Teleport weapon.
|
|
||||||
/// </summary>
|
|
||||||
Teleport,
|
Teleport,
|
||||||
|
/// <summary>The Ninja Rope weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Ninja Rope weapon.
|
|
||||||
/// </summary>
|
|
||||||
NinjaRope,
|
NinjaRope,
|
||||||
|
/// <summary>The Bungee weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Bungee weapon.
|
|
||||||
/// </summary>
|
|
||||||
Bungee,
|
Bungee,
|
||||||
|
/// <summary>The Parachute weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Parachute weapon.
|
|
||||||
/// </summary>
|
|
||||||
Parachute,
|
Parachute,
|
||||||
|
/// <summary>The Sheep weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Sheep weapon.
|
|
||||||
/// </summary>
|
|
||||||
Sheep,
|
Sheep,
|
||||||
|
/// <summary>The Mad Cow weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Mad Cow weapon.
|
|
||||||
/// </summary>
|
|
||||||
MadCow,
|
MadCow,
|
||||||
|
/// <summary>The Old Woman weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Old Woman weapon.
|
|
||||||
/// </summary>
|
|
||||||
OldWoman,
|
OldWoman,
|
||||||
|
/// <summary>The Mortar weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Mortar weapon.
|
|
||||||
/// </summary>
|
|
||||||
Mortar,
|
Mortar,
|
||||||
|
/// <summary>The Blowtorch weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Blowtorch weapon.
|
|
||||||
/// </summary>
|
|
||||||
Blowtorch,
|
Blowtorch,
|
||||||
|
/// <summary>The Homing Pigeon weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Homing Pigeon weapon.
|
|
||||||
/// </summary>
|
|
||||||
HomingPigeon,
|
HomingPigeon,
|
||||||
|
/// <summary>The Super Sheep weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Super Sheep weapon.
|
|
||||||
/// </summary>
|
|
||||||
SuperSheep,
|
SuperSheep,
|
||||||
|
/// <summary>The Super Banana Bomb weapon.</summary>
|
||||||
/// <summary>
|
|
||||||
/// The Super Banana Bomb weapon.
|
|
||||||
/// </summary>
|
|
||||||
SuperBananaBomb
|
SuperBananaBomb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Worms2
|
namespace Syroot.Worms.Worms2
|
||||||
{
|
{
|
||||||
@ -140,7 +140,7 @@ namespace Syroot.Worms.Worms2
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating the worm selection order determining the next worm to be played.
|
/// Gets or sets a value indicating the worm selection order determining the next worm to be played.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SchemeWormSelect WormSelectMode { get; set; }
|
public WormSelect WormSelectMode { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the chat box will be closed upon starting to move a worm or stays
|
/// Gets or sets a value indicating whether the chat box will be closed upon starting to move a worm or stays
|
||||||
@ -236,7 +236,7 @@ namespace Syroot.Worms.Worms2
|
|||||||
SuddenDeathHealthDrop = reader.ReadBoolean(BooleanCoding.Dword);
|
SuddenDeathHealthDrop = reader.ReadBoolean(BooleanCoding.Dword);
|
||||||
IndestructibleBorder = reader.ReadBoolean(BooleanCoding.Dword);
|
IndestructibleBorder = reader.ReadBoolean(BooleanCoding.Dword);
|
||||||
RestrictGirders = reader.ReadBoolean(BooleanCoding.Dword);
|
RestrictGirders = reader.ReadBoolean(BooleanCoding.Dword);
|
||||||
WormSelectMode = reader.ReadEnum<SchemeWormSelect>(true);
|
WormSelectMode = reader.ReadEnum<WormSelect>(true);
|
||||||
ExtendedChatControls = reader.ReadBoolean(BooleanCoding.Dword);
|
ExtendedChatControls = reader.ReadBoolean(BooleanCoding.Dword);
|
||||||
HotSeatDelay = reader.ReadInt32();
|
HotSeatDelay = reader.ReadInt32();
|
||||||
EnableStockpiling = reader.ReadBoolean(BooleanCoding.Dword);
|
EnableStockpiling = reader.ReadBoolean(BooleanCoding.Dword);
|
||||||
|
171
src/library/Syroot.Worms.Worms2/SchemeWeapon.cs
Normal file
171
src/library/Syroot.Worms.Worms2/SchemeWeapon.cs
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.Worms2
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the configuration of a weapon.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct SchemeWeapon : IEquatable<SchemeWeapon>
|
||||||
|
{
|
||||||
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>Amount of this weapon with which a team is equipped at game start. 10 and negative values represent
|
||||||
|
/// infinity.</summary>
|
||||||
|
public int Ammo;
|
||||||
|
/// <summary>Number of turns required to be taken by each team before this weapon becomes available.</summary>
|
||||||
|
public int Delay;
|
||||||
|
/// <summary>Retreat time after using this weapon. 0 uses the setting from the game options.</summary>
|
||||||
|
public int RetreatTime;
|
||||||
|
/// <summary><see langword="true"/> to preselect this weapon in the next turn; otherwise <see langword="false"/>.</summary>
|
||||||
|
public bool Remember;
|
||||||
|
public int Unused1;
|
||||||
|
/// <summary>Amount of this weapon added to the team armory when collected from a crate.</summary>
|
||||||
|
public int CrateAmmo;
|
||||||
|
/// <summary>Amount of bullets shot at once.</summary>
|
||||||
|
public int BulletCount;
|
||||||
|
/// <summary>Percentual chance of this weapon to appear in crates.</summary>
|
||||||
|
public int Probability;
|
||||||
|
/// <summary>Damage measured in health points which also determines the blast radius.</summary>
|
||||||
|
public int Damage;
|
||||||
|
/// <summary>Pushing power measured in percent.</summary>
|
||||||
|
public int ExplosionPower;
|
||||||
|
/// <summary>Offset to the bottom of an explosion, measured in percent.</summary>
|
||||||
|
public int ExplosionBias;
|
||||||
|
/// <summary>Milliseconds required before this weapon starts flying towards its target.</summary>
|
||||||
|
public int HomingDelay;
|
||||||
|
/// <summary>Length in milliseconds this weapon flies towards its target before giving up.</summary>
|
||||||
|
public int HomingTime;
|
||||||
|
/// <summary>Percentual amount this weaopn is affected by wind.</summary>
|
||||||
|
public int WindResponse;
|
||||||
|
public int Unused2;
|
||||||
|
/// <summary>Number of clusters into which this weapon explodes.</summary>
|
||||||
|
public int ClusterCount;
|
||||||
|
/// <summary>Speed in which clusters are dispersed in percent.</summary>
|
||||||
|
public int ClusterLaunchPower;
|
||||||
|
/// <summary>Angle in which clusters are dispersed in degrees.</summary>
|
||||||
|
public int ClusterLaunchAngle;
|
||||||
|
/// <summary>Damage of clusters measured in health points which also determines the blast radius.</summary>
|
||||||
|
public int ClusterDamage;
|
||||||
|
/// <summary>Overrides the fuse of this weapon, 0 for default.</summary>
|
||||||
|
public int Fuse;
|
||||||
|
/// <summary>Amount of fire created.</summary>
|
||||||
|
public int FireAmount;
|
||||||
|
/// <summary>Speed in which fire spreads, measured in percent.</summary>
|
||||||
|
public int FireSpreadSpeed;
|
||||||
|
/// <summary>Period in which fire burns, measured in percent.</summary>
|
||||||
|
public int FireTime;
|
||||||
|
/// <summary>Melee impact force in percent.</summary>
|
||||||
|
public int MeleeForce;
|
||||||
|
/// <summary>Melee impact angle in degrees.</summary>
|
||||||
|
public int MeleeAngle;
|
||||||
|
/// <summary>Melee damage in health points.</summary>
|
||||||
|
public int MeleeDamage;
|
||||||
|
/// <summary>Height of the fire punch jump, measured in percent.</summary>
|
||||||
|
public int FirepunchHeight;
|
||||||
|
/// <summary>Damage a dragon ball causes, measured in health points.</summary>
|
||||||
|
public int DragonballDamage;
|
||||||
|
/// <summary>Power in which a dragon ball launches hit worms, measured in percent.</summary>
|
||||||
|
public int DragonballPower;
|
||||||
|
/// <summary>Angle in which a dragon ball launches hit worms, measured in degrees.</summary>
|
||||||
|
public int DragonballAngle;
|
||||||
|
/// <summary>Lifetime of a launched dragon ball measured in milliseconds.</summary>
|
||||||
|
public int DragonballTime;
|
||||||
|
/// <summary>Length of digging measured in milliseconds. Applies to Kamikaze and digging tools.</summary>
|
||||||
|
public int DiggingTime;
|
||||||
|
/// <summary>Amount of airstrike clusters thrown.</summary>
|
||||||
|
public int StrikeClusterCount;
|
||||||
|
/// <summary>Angle in which bullets are dispersed, measured in degrees.</summary>
|
||||||
|
public int BulletSpreadAngle;
|
||||||
|
|
||||||
|
// ---- OPERATORS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public static bool operator ==(SchemeWeapon left, SchemeWeapon right) => left.Equals(right);
|
||||||
|
|
||||||
|
public static bool operator !=(SchemeWeapon left, SchemeWeapon right) => !(left == right);
|
||||||
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override bool Equals(object? obj) => obj is SchemeWeapon weapon && Equals(weapon);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool Equals(SchemeWeapon other)
|
||||||
|
=> Ammo == other.Ammo
|
||||||
|
&& Delay == other.Delay
|
||||||
|
&& RetreatTime == other.RetreatTime
|
||||||
|
&& Remember == other.Remember
|
||||||
|
&& Unused1 == other.Unused1
|
||||||
|
&& CrateAmmo == other.CrateAmmo
|
||||||
|
&& BulletCount == other.BulletCount
|
||||||
|
&& Probability == other.Probability
|
||||||
|
&& Damage == other.Damage
|
||||||
|
&& ExplosionPower == other.ExplosionPower
|
||||||
|
&& ExplosionBias == other.ExplosionBias
|
||||||
|
&& HomingDelay == other.HomingDelay
|
||||||
|
&& HomingTime == other.HomingTime
|
||||||
|
&& WindResponse == other.WindResponse
|
||||||
|
&& Unused2 == other.Unused2
|
||||||
|
&& ClusterCount == other.ClusterCount
|
||||||
|
&& ClusterLaunchPower == other.ClusterLaunchPower
|
||||||
|
&& ClusterLaunchAngle == other.ClusterLaunchAngle
|
||||||
|
&& ClusterDamage == other.ClusterDamage
|
||||||
|
&& Fuse == other.Fuse
|
||||||
|
&& FireAmount == other.FireAmount
|
||||||
|
&& FireSpreadSpeed == other.FireSpreadSpeed
|
||||||
|
&& FireTime == other.FireTime
|
||||||
|
&& MeleeForce == other.MeleeForce
|
||||||
|
&& MeleeAngle == other.MeleeAngle
|
||||||
|
&& MeleeDamage == other.MeleeDamage
|
||||||
|
&& FirepunchHeight == other.FirepunchHeight
|
||||||
|
&& DragonballDamage == other.DragonballDamage
|
||||||
|
&& DragonballPower == other.DragonballPower
|
||||||
|
&& DragonballAngle == other.DragonballAngle
|
||||||
|
&& DragonballTime == other.DragonballTime
|
||||||
|
&& DiggingTime == other.DiggingTime
|
||||||
|
&& StrikeClusterCount == other.StrikeClusterCount
|
||||||
|
&& BulletSpreadAngle == other.BulletSpreadAngle;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
HashCode hash = new HashCode();
|
||||||
|
hash.Add(Ammo);
|
||||||
|
hash.Add(Delay);
|
||||||
|
hash.Add(RetreatTime);
|
||||||
|
hash.Add(Remember);
|
||||||
|
hash.Add(Unused1);
|
||||||
|
hash.Add(CrateAmmo);
|
||||||
|
hash.Add(BulletCount);
|
||||||
|
hash.Add(Probability);
|
||||||
|
hash.Add(Damage);
|
||||||
|
hash.Add(ExplosionPower);
|
||||||
|
hash.Add(ExplosionBias);
|
||||||
|
hash.Add(HomingDelay);
|
||||||
|
hash.Add(HomingTime);
|
||||||
|
hash.Add(WindResponse);
|
||||||
|
hash.Add(Unused2);
|
||||||
|
hash.Add(ClusterCount);
|
||||||
|
hash.Add(ClusterLaunchPower);
|
||||||
|
hash.Add(ClusterLaunchAngle);
|
||||||
|
hash.Add(ClusterDamage);
|
||||||
|
hash.Add(Fuse);
|
||||||
|
hash.Add(FireAmount);
|
||||||
|
hash.Add(FireSpreadSpeed);
|
||||||
|
hash.Add(FireTime);
|
||||||
|
hash.Add(MeleeForce);
|
||||||
|
hash.Add(MeleeAngle);
|
||||||
|
hash.Add(MeleeDamage);
|
||||||
|
hash.Add(FirepunchHeight);
|
||||||
|
hash.Add(DragonballDamage);
|
||||||
|
hash.Add(DragonballPower);
|
||||||
|
hash.Add(DragonballAngle);
|
||||||
|
hash.Add(DragonballTime);
|
||||||
|
hash.Add(DiggingTime);
|
||||||
|
hash.Add(StrikeClusterCount);
|
||||||
|
hash.Add(BulletSpreadAngle);
|
||||||
|
return hash.ToHashCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,182 +0,0 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.Worms2
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the configuration of a weapon.
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct SchemeWeaponSetting
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of this weapon with which a team is equipped at game start. 10 and negative values represent
|
|
||||||
/// infinity.
|
|
||||||
/// </summary>
|
|
||||||
public int Ammunition;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of turns required to be taken by each team before this weapon becomes available.
|
|
||||||
/// </summary>
|
|
||||||
public int Delay;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retreat time after using this weapon. 0 uses the setting from the game options.
|
|
||||||
/// </summary>
|
|
||||||
public int RetreatTime;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <c>true</c> to preselect this weapon in the next turn; otherwise <c>false</c>.
|
|
||||||
/// </summary>
|
|
||||||
public bool Remember;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An unused field with unknown value.
|
|
||||||
/// </summary>
|
|
||||||
public int Unused1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of this weapon added to the team armory when collected from a crate.
|
|
||||||
/// </summary>
|
|
||||||
public int CrateAmmunition;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of bullets shot at once.
|
|
||||||
/// </summary>
|
|
||||||
public int BulletCount;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The percentual chance of this weapon to appear in crates.
|
|
||||||
/// </summary>
|
|
||||||
public int Probability;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The damage measured in health points which also determines the blast radius.
|
|
||||||
/// </summary>
|
|
||||||
public int Damage;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The pushing power measured in percent.
|
|
||||||
/// </summary>
|
|
||||||
public int ExplosionPower;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The offset to the bottom of an explosion, measured in percent.
|
|
||||||
/// </summary>
|
|
||||||
public int ExplosionBias;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The milliseconds required before this weapon starts flying towards its target.
|
|
||||||
/// </summary>
|
|
||||||
public int HomingDelay;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The length in milliseconds this weapon flies towards its target before giving up.
|
|
||||||
/// </summary>
|
|
||||||
public int HomingTime;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The percentual amount this weaopn is affected by wind.
|
|
||||||
/// </summary>
|
|
||||||
public int WindResponse;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An unused field with unknown value.
|
|
||||||
/// </summary>
|
|
||||||
public int Unused2;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The number of clusters into which this weapon explodes.
|
|
||||||
/// </summary>
|
|
||||||
public int ClusterCount;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The speed in which clusters are dispersed in percent.
|
|
||||||
/// </summary>
|
|
||||||
public int ClusterLaunchPower;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The angle in which clusters are dispersed in degrees.
|
|
||||||
/// </summary>
|
|
||||||
public int ClusterLaunchAngle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The damage of clusters measured in health points which also determines the blast radius.
|
|
||||||
/// </summary>
|
|
||||||
public int ClusterDamage;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Overrides the fuse of this weapon, 0 for default.
|
|
||||||
/// </summary>
|
|
||||||
public int Fuse;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of fire created.
|
|
||||||
/// </summary>
|
|
||||||
public int FireAmount;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The speed in which fire spreads, measured in percent.
|
|
||||||
/// </summary>
|
|
||||||
public int FireSpreadSpeed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The period in which fire burns, measured in percent.
|
|
||||||
/// </summary>
|
|
||||||
public int FireTime;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The melee impact force in percent.
|
|
||||||
/// </summary>
|
|
||||||
public int MeleeForce;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The melee impact angle in degrees.
|
|
||||||
/// </summary>
|
|
||||||
public int MeleeAngle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The melee damage in health points.
|
|
||||||
/// </summary>
|
|
||||||
public int MeleeDamage;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The height of the fire punch jump, measured in percent.
|
|
||||||
/// </summary>
|
|
||||||
public int FirepunchHeight;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The damage a dragon ball causes, measured in health points.
|
|
||||||
/// </summary>
|
|
||||||
public int DragonballDamage;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The power in which a dragon ball launches hit worms, measured in percent.
|
|
||||||
/// </summary>
|
|
||||||
public int DragonballPower;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The angle in which a dragon ball launches hit worms, measured in degrees.
|
|
||||||
/// </summary>
|
|
||||||
public int DragonballAngle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The life time of a launched dragon ball measured in milliseconds.
|
|
||||||
/// </summary>
|
|
||||||
public int DragonballTime;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The length of digging measured in milliseconds. Applies to Kamikaze and digging tools.
|
|
||||||
/// </summary>
|
|
||||||
public int DiggingTime;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of airstrike clusters thrown.
|
|
||||||
/// </summary>
|
|
||||||
public int StrikeClusterCount;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The angle in which bullets are dispersed, measured in degrees.
|
|
||||||
/// </summary>
|
|
||||||
public int BulletSpreadAngle;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,94 +1,107 @@
|
|||||||
using System.IO;
|
using System;
|
||||||
using System.Text;
|
using System.IO;
|
||||||
using Syroot.BinaryData;
|
using System.Linq;
|
||||||
using Syroot.Worms.Core.IO;
|
using System.Text;
|
||||||
|
using Syroot.BinaryData;
|
||||||
namespace Syroot.Worms.Worms2
|
using Syroot.Worms.IO;
|
||||||
{
|
|
||||||
/// <summary>
|
namespace Syroot.Worms.Worms2
|
||||||
/// Represents scheme weapons stored in an WEP file which contains armory configuration.
|
{
|
||||||
/// Used by W2. S. https://worms2d.info/Weapons_file.
|
/// <summary>
|
||||||
/// </summary>
|
/// Represents scheme weapons stored in an WEP file which contains armory configuration.
|
||||||
public class SchemeWeapons : ILoadableFile, ISaveableFile
|
/// Used by W2. S. https://worms2d.info/Weapons_file.
|
||||||
{
|
/// </summary>
|
||||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
public class SchemeWeapons : ILoadableFile, ISaveableFile, IEquatable<SchemeWeapons?>
|
||||||
|
{
|
||||||
private const int _trashLength = 16;
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
private const string _signature = "WEPFILE"; // Zero-terminated.
|
|
||||||
private const int _weaponCount = 38;
|
private const int _trashLength = 16;
|
||||||
|
private const string _signature = "WEPFILE"; // Zero-terminated.
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
private const int _weaponCount = 38;
|
||||||
|
|
||||||
/// <summary>
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
/// Initializes a new instance of the <see cref="SchemeWeapons"/> class.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public SchemeWeapons() => Weapons = new SchemeWeaponSetting[_weaponCount];
|
/// Initializes a new instance of the <see cref="SchemeWeapons"/> class.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public SchemeWeapons() { }
|
||||||
/// Initializes a new instance of the <see cref="SchemeWeapons"/> class, loading the data from the given
|
|
||||||
/// <see cref="Stream"/>.
|
/// <summary>
|
||||||
/// </summary>
|
/// Initializes a new instance of the <see cref="SchemeWeapons"/> class, loading the data from the given
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// <see cref="Stream"/>.
|
||||||
public SchemeWeapons(Stream stream) => Load(stream);
|
/// </summary>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
/// <summary>
|
public SchemeWeapons(Stream stream) => Load(stream);
|
||||||
/// Initializes a new instance of the <see cref="SchemeWeapons"/> class, loading the data from the given file.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// Initializes a new instance of the <see cref="SchemeWeapons"/> class, loading the data from the given file.
|
||||||
public SchemeWeapons(string fileName) => Load(fileName);
|
/// </summary>
|
||||||
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
public SchemeWeapons(string fileName) => Load(fileName);
|
||||||
|
|
||||||
/// <summary>
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
/// Gets the array of <see cref="SchemeWeaponSetting"/> instances, each mapping to one weapon at the index of
|
|
||||||
/// the <see cref="SchemeWeapon"/> enumeration.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets the array of <see cref="SchemeWeapon"/> instances, each mapping to one weapon at the index of
|
||||||
public SchemeWeaponSetting[] Weapons { get; set; }
|
/// the <see cref="Weapon"/> enumeration.
|
||||||
|
/// </summary>
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
public SchemeWeapon[] Weapons { get; set; } = new SchemeWeapon[_weaponCount];
|
||||||
|
|
||||||
/// <inheritdoc/>
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
public void Load(Stream stream)
|
|
||||||
{
|
/// <inheritdoc/>
|
||||||
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
public override bool Equals(object? obj) => Equals(obj as SchemeWeapons);
|
||||||
|
|
||||||
// Read the header.
|
/// <inheritdoc/>
|
||||||
reader.Seek(_trashLength);
|
public bool Equals(SchemeWeapons? other)
|
||||||
if (reader.ReadString(StringCoding.ZeroTerminated) != _signature)
|
=> other != null
|
||||||
throw new InvalidDataException("Invalid WEP file signature.");
|
&& Weapons.SequenceEqual(other.Weapons);
|
||||||
|
|
||||||
// Read the weapon settings.
|
/// <inheritdoc/>
|
||||||
Weapons = new SchemeWeaponSetting[_weaponCount];
|
public override int GetHashCode() => HashCode.Combine(Weapons);
|
||||||
for (int i = 0; i < _weaponCount; i++)
|
|
||||||
Weapons[i] = reader.ReadStruct<SchemeWeaponSetting>();
|
/// <inheritdoc/>
|
||||||
}
|
public void Load(Stream stream)
|
||||||
|
{
|
||||||
/// <inheritdoc/>
|
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
public void Load(string fileName)
|
|
||||||
{
|
// Read the header.
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
reader.Seek(_trashLength);
|
||||||
Load(stream);
|
if (reader.ReadString(StringCoding.ZeroTerminated) != _signature)
|
||||||
}
|
throw new InvalidDataException("Invalid WEP file signature.");
|
||||||
|
|
||||||
/// <inheritdoc/>
|
// Read the weapon settings.
|
||||||
public void Save(Stream stream)
|
Weapons = new SchemeWeapon[_weaponCount];
|
||||||
{
|
for (int i = 0; i < _weaponCount; i++)
|
||||||
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
Weapons[i] = reader.ReadStruct<SchemeWeapon>();
|
||||||
|
}
|
||||||
// Write the header.
|
|
||||||
writer.Write(new byte[_trashLength]);
|
/// <inheritdoc/>
|
||||||
writer.Write(_signature, StringCoding.ZeroTerminated);
|
public void Load(string fileName)
|
||||||
|
{
|
||||||
// Write the weapon settings.
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
foreach (SchemeWeaponSetting weapon in Weapons)
|
Load(stream);
|
||||||
writer.WriteStruct(weapon);
|
}
|
||||||
}
|
|
||||||
|
/// <inheritdoc/>
|
||||||
/// <inheritdoc/>
|
public void Save(Stream stream)
|
||||||
public void Save(string fileName)
|
{
|
||||||
{
|
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
|
||||||
Save(stream);
|
// Write the header.
|
||||||
}
|
writer.Write(new byte[_trashLength]);
|
||||||
}
|
writer.Write(_signature, StringCoding.ZeroTerminated);
|
||||||
}
|
|
||||||
|
// Write the weapon settings.
|
||||||
|
foreach (SchemeWeapon weapon in Weapons)
|
||||||
|
writer.WriteStruct(weapon);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Save(string fileName)
|
||||||
|
{
|
||||||
|
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
|
Save(stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<AssemblyName>Syroot.Worms.Worms2</AssemblyName>
|
<AssemblyName>Syroot.Worms.Worms2</AssemblyName>
|
||||||
<Description>.NET library for loading and modifying files of Team17's Worms 2.</Description>
|
<Description>.NET library for loading and modifying files of Team17's Worms 2.</Description>
|
||||||
<PackageReleaseNotes>Fix issues when loading and saving some formats.</PackageReleaseNotes>
|
<PackageReleaseNotes>Overhaul implementation and documentation.</PackageReleaseNotes>
|
||||||
<PackageTags>$(PackageTags);worms 2</PackageTags>
|
<PackageTags>$(PackageTags);worms 2</PackageTags>
|
||||||
<Version>3.2.0</Version>
|
<Version>4.0.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Syroot.Worms\Syroot.Worms.csproj" />
|
<ProjectReference Include="..\Syroot.Worms\Syroot.Worms.csproj" />
|
||||||
|
@ -1,202 +1,205 @@
|
|||||||
using System.IO;
|
using System;
|
||||||
using System.Text;
|
using System.IO;
|
||||||
using Syroot.BinaryData;
|
using System.Text;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.BinaryData;
|
||||||
|
using Syroot.Worms.IO;
|
||||||
namespace Syroot.Worms.Worms2
|
|
||||||
{
|
namespace Syroot.Worms.Worms2
|
||||||
/// <summary>
|
{
|
||||||
/// Represents a team stored in a <see cref="TeamContainer"/> file.
|
/// <summary>
|
||||||
/// </summary>
|
/// Represents a team stored in a <see cref="TeamContainer"/> file.
|
||||||
public class Team : ILoadable, ISaveable
|
/// </summary>
|
||||||
{
|
public class Team : ILoadable, ISaveable
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
{
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
public short Unknown1 { get; set; }
|
|
||||||
|
public short Unknown1 { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the name of the team.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets the name of the team.
|
||||||
public string Name { get; set; }
|
/// </summary>
|
||||||
|
public string Name { get; set; } = String.Empty;
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the name of soundbank for the voice of team worms.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets the name of soundbank for the voice of team worms.
|
||||||
public string SoundBankName { get; set; }
|
/// </summary>
|
||||||
|
public string SoundBankName { get; set; } = String.Empty;
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the 8 worm names.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets the 8 worm names.
|
||||||
public string[] WormNames { get; set; }
|
/// </summary>
|
||||||
|
public string[] WormNames { get; } = new string[8];
|
||||||
public int Unknown2 { get; set; }
|
|
||||||
public int Unknown3 { get; set; }
|
public int Unknown2 { get; set; }
|
||||||
public int Unknown4 { get; set; }
|
public int Unknown3 { get; set; }
|
||||||
public int Unknown5 { get; set; }
|
public int Unknown4 { get; set; }
|
||||||
public int Unknown6 { get; set; }
|
public int Unknown5 { get; set; }
|
||||||
public int Unknown7 { get; set; }
|
public int Unknown6 { get; set; }
|
||||||
public int Unknown8 { get; set; }
|
public int Unknown7 { get; set; }
|
||||||
public int Unknown9 { get; set; }
|
public int Unknown8 { get; set; }
|
||||||
public int Unknown10 { get; set; }
|
public int Unknown9 { get; set; }
|
||||||
public int Unknown11 { get; set; }
|
public int Unknown10 { get; set; }
|
||||||
public int Unknown12 { get; set; }
|
public int Unknown11 { get; set; }
|
||||||
public int Unknown13 { get; set; }
|
public int Unknown12 { get; set; }
|
||||||
public int Unknown14 { get; set; }
|
public int Unknown13 { get; set; }
|
||||||
public int Unknown15 { get; set; }
|
public int Unknown14 { get; set; }
|
||||||
public int Unknown16 { get; set; }
|
public int Unknown15 { get; set; }
|
||||||
public int Unknown17 { get; set; }
|
public int Unknown16 { get; set; }
|
||||||
public int Unknown18 { get; set; }
|
public int Unknown17 { get; set; }
|
||||||
public int Unknown19 { get; set; }
|
public int Unknown18 { get; set; }
|
||||||
public int Unknown20 { get; set; }
|
public int Unknown19 { get; set; }
|
||||||
public int Unknown21 { get; set; }
|
public int Unknown20 { get; set; }
|
||||||
public int Unknown22 { get; set; }
|
public int Unknown21 { get; set; }
|
||||||
public int Unknown23 { get; set; }
|
public int Unknown22 { get; set; }
|
||||||
public int Unknown24 { get; set; }
|
public int Unknown23 { get; set; }
|
||||||
public int Unknown25 { get; set; }
|
public int Unknown24 { get; set; }
|
||||||
|
public int Unknown25 { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the number of games lost.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets the number of games lost.
|
||||||
public int GamesLost { get; set; }
|
/// </summary>
|
||||||
|
public int GamesLost { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the number of games won.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets the number of games won.
|
||||||
public int GamesWon { get; set; }
|
/// </summary>
|
||||||
|
public int GamesWon { get; set; }
|
||||||
public int Unknown26 { get; set; }
|
|
||||||
public int Unknown27 { get; set; }
|
public int Unknown26 { get; set; }
|
||||||
|
public int Unknown27 { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the number of opponent worms killed by this team.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets the number of opponent worms killed by this team.
|
||||||
public int Kills { get; set; }
|
/// </summary>
|
||||||
|
public int Kills { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the number of worms which got killed in this team.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets the number of worms which got killed in this team.
|
||||||
public int Deaths { get; set; }
|
/// </summary>
|
||||||
|
public int Deaths { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the AI intelligence difficulty level, from 0-100, where 0 is human-controlled.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets the AI intelligence difficulty level, from 0-100, where 0 is human-controlled.
|
||||||
public int CpuLevel { get; set; }
|
/// </summary>
|
||||||
|
public int CpuLevel { get; set; }
|
||||||
public int Unknown28 { get; set; }
|
|
||||||
public int Unknown29 { get; set; }
|
public int Unknown28 { get; set; }
|
||||||
public int Unknown30 { get; set; }
|
public int Unknown29 { get; set; }
|
||||||
|
public int Unknown30 { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the "difference" statistics value.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets the "difference" statistics value.
|
||||||
public int Difference { get; set; }
|
/// </summary>
|
||||||
|
public int Difference { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the number of games played, always being 0 for AI controlled teams.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets the number of games played, always being 0 for AI controlled teams.
|
||||||
public int GamesPlayed { get; set; }
|
/// </summary>
|
||||||
|
public int GamesPlayed { get; set; }
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the points gained by this team.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets the points gained by this team.
|
||||||
public int Points { get; set; }
|
/// </summary>
|
||||||
|
public int Points { get; set; }
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
/// <inheritdoc/>
|
|
||||||
public void Load(Stream stream)
|
/// <inheritdoc/>
|
||||||
{
|
public void Load(Stream stream)
|
||||||
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
{
|
||||||
Unknown1 = reader.ReadInt16();
|
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
Name = reader.ReadString(66);
|
Unknown1 = reader.ReadInt16();
|
||||||
SoundBankName = reader.ReadString(36);
|
Name = reader.ReadString(66);
|
||||||
WormNames = reader.ReadStrings(8, 20);
|
SoundBankName = reader.ReadString(36);
|
||||||
Unknown2 = reader.ReadInt32();
|
for (int i = 0; i < WormNames.Length; i++)
|
||||||
Unknown3 = reader.ReadInt32();
|
WormNames[i] = reader.ReadString(20);
|
||||||
Unknown4 = reader.ReadInt32();
|
Unknown2 = reader.ReadInt32();
|
||||||
Unknown5 = reader.ReadInt32();
|
Unknown3 = reader.ReadInt32();
|
||||||
Unknown6 = reader.ReadInt32();
|
Unknown4 = reader.ReadInt32();
|
||||||
Unknown7 = reader.ReadInt32();
|
Unknown5 = reader.ReadInt32();
|
||||||
Unknown8 = reader.ReadInt32();
|
Unknown6 = reader.ReadInt32();
|
||||||
Unknown9 = reader.ReadInt32();
|
Unknown7 = reader.ReadInt32();
|
||||||
Unknown10 = reader.ReadInt32();
|
Unknown8 = reader.ReadInt32();
|
||||||
Unknown11 = reader.ReadInt32();
|
Unknown9 = reader.ReadInt32();
|
||||||
Unknown12 = reader.ReadInt32();
|
Unknown10 = reader.ReadInt32();
|
||||||
Unknown13 = reader.ReadInt32();
|
Unknown11 = reader.ReadInt32();
|
||||||
Unknown14 = reader.ReadInt32();
|
Unknown12 = reader.ReadInt32();
|
||||||
Unknown15 = reader.ReadInt32();
|
Unknown13 = reader.ReadInt32();
|
||||||
Unknown16 = reader.ReadInt32();
|
Unknown14 = reader.ReadInt32();
|
||||||
Unknown17 = reader.ReadInt32();
|
Unknown15 = reader.ReadInt32();
|
||||||
Unknown18 = reader.ReadInt32();
|
Unknown16 = reader.ReadInt32();
|
||||||
Unknown19 = reader.ReadInt32();
|
Unknown17 = reader.ReadInt32();
|
||||||
Unknown20 = reader.ReadInt32();
|
Unknown18 = reader.ReadInt32();
|
||||||
Unknown21 = reader.ReadInt32();
|
Unknown19 = reader.ReadInt32();
|
||||||
Unknown22 = reader.ReadInt32();
|
Unknown20 = reader.ReadInt32();
|
||||||
Unknown23 = reader.ReadInt32();
|
Unknown21 = reader.ReadInt32();
|
||||||
Unknown24 = reader.ReadInt32();
|
Unknown22 = reader.ReadInt32();
|
||||||
Unknown25 = reader.ReadInt32();
|
Unknown23 = reader.ReadInt32();
|
||||||
GamesLost = reader.ReadInt32();
|
Unknown24 = reader.ReadInt32();
|
||||||
GamesWon = reader.ReadInt32();
|
Unknown25 = reader.ReadInt32();
|
||||||
Unknown26 = reader.ReadInt32();
|
GamesLost = reader.ReadInt32();
|
||||||
Unknown27 = reader.ReadInt32();
|
GamesWon = reader.ReadInt32();
|
||||||
Kills = reader.ReadInt32();
|
Unknown26 = reader.ReadInt32();
|
||||||
Deaths = reader.ReadInt32();
|
Unknown27 = reader.ReadInt32();
|
||||||
CpuLevel = reader.ReadInt32();
|
Kills = reader.ReadInt32();
|
||||||
Unknown28 = reader.ReadInt32();
|
Deaths = reader.ReadInt32();
|
||||||
Unknown29 = reader.ReadInt32();
|
CpuLevel = reader.ReadInt32();
|
||||||
Unknown30 = reader.ReadInt32();
|
Unknown28 = reader.ReadInt32();
|
||||||
Difference = reader.ReadInt32();
|
Unknown29 = reader.ReadInt32();
|
||||||
GamesPlayed = reader.ReadInt32();
|
Unknown30 = reader.ReadInt32();
|
||||||
Points = reader.ReadInt32();
|
Difference = reader.ReadInt32();
|
||||||
}
|
GamesPlayed = reader.ReadInt32();
|
||||||
|
Points = reader.ReadInt32();
|
||||||
/// <inheritdoc/>
|
}
|
||||||
public void Save(Stream stream)
|
|
||||||
{
|
/// <inheritdoc/>
|
||||||
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
public void Save(Stream stream)
|
||||||
writer.Write(Unknown1);
|
{
|
||||||
writer.WriteString(Name, 66);
|
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
writer.WriteString(SoundBankName, 36);
|
writer.Write(Unknown1);
|
||||||
writer.WriteStrings(WormNames, 20);
|
writer.WriteFixedString(Name, 66);
|
||||||
writer.Write(Unknown2);
|
writer.WriteFixedString(SoundBankName, 36);
|
||||||
writer.Write(Unknown3);
|
for (int i = 0; i < 8; i++)
|
||||||
writer.Write(Unknown4);
|
writer.WriteFixedString(WormNames[i], 20);
|
||||||
writer.Write(Unknown5);
|
writer.Write(Unknown2);
|
||||||
writer.Write(Unknown6);
|
writer.Write(Unknown3);
|
||||||
writer.Write(Unknown7);
|
writer.Write(Unknown4);
|
||||||
writer.Write(Unknown8);
|
writer.Write(Unknown5);
|
||||||
writer.Write(Unknown9);
|
writer.Write(Unknown6);
|
||||||
writer.Write(Unknown10);
|
writer.Write(Unknown7);
|
||||||
writer.Write(Unknown11);
|
writer.Write(Unknown8);
|
||||||
writer.Write(Unknown12);
|
writer.Write(Unknown9);
|
||||||
writer.Write(Unknown13);
|
writer.Write(Unknown10);
|
||||||
writer.Write(Unknown14);
|
writer.Write(Unknown11);
|
||||||
writer.Write(Unknown15);
|
writer.Write(Unknown12);
|
||||||
writer.Write(Unknown16);
|
writer.Write(Unknown13);
|
||||||
writer.Write(Unknown17);
|
writer.Write(Unknown14);
|
||||||
writer.Write(Unknown18);
|
writer.Write(Unknown15);
|
||||||
writer.Write(Unknown19);
|
writer.Write(Unknown16);
|
||||||
writer.Write(Unknown20);
|
writer.Write(Unknown17);
|
||||||
writer.Write(Unknown21);
|
writer.Write(Unknown18);
|
||||||
writer.Write(Unknown22);
|
writer.Write(Unknown19);
|
||||||
writer.Write(Unknown23);
|
writer.Write(Unknown20);
|
||||||
writer.Write(Unknown24);
|
writer.Write(Unknown21);
|
||||||
writer.Write(Unknown25);
|
writer.Write(Unknown22);
|
||||||
writer.Write(GamesLost);
|
writer.Write(Unknown23);
|
||||||
writer.Write(GamesWon);
|
writer.Write(Unknown24);
|
||||||
writer.Write(Unknown26);
|
writer.Write(Unknown25);
|
||||||
writer.Write(Unknown27);
|
writer.Write(GamesLost);
|
||||||
writer.Write(Kills);
|
writer.Write(GamesWon);
|
||||||
writer.Write(Deaths);
|
writer.Write(Unknown26);
|
||||||
writer.Write(CpuLevel);
|
writer.Write(Unknown27);
|
||||||
writer.Write(Unknown28);
|
writer.Write(Kills);
|
||||||
writer.Write(Unknown29);
|
writer.Write(Deaths);
|
||||||
writer.Write(Unknown30);
|
writer.Write(CpuLevel);
|
||||||
writer.Write(Kills);
|
writer.Write(Unknown28);
|
||||||
writer.Write(Deaths);
|
writer.Write(Unknown29);
|
||||||
writer.Write(Difference);
|
writer.Write(Unknown30);
|
||||||
writer.Write(GamesPlayed);
|
writer.Write(Kills);
|
||||||
writer.Write(Points);
|
writer.Write(Deaths);
|
||||||
}
|
writer.Write(Difference);
|
||||||
}
|
writer.Write(GamesPlayed);
|
||||||
}
|
writer.Write(Points);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Worms2
|
namespace Syroot.Worms.Worms2
|
||||||
{
|
{
|
||||||
@ -17,7 +17,7 @@ namespace Syroot.Worms.Worms2
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TeamContainer"/> class.
|
/// Initializes a new instance of the <see cref="TeamContainer"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TeamContainer() => Teams = new List<Team>();
|
public TeamContainer() { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="TeamContainer"/> class, loading the data from the given
|
/// Initializes a new instance of the <see cref="TeamContainer"/> class, loading the data from the given
|
||||||
@ -37,7 +37,7 @@ namespace Syroot.Worms.Worms2
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the list of <see cref="Team"/> instances stored.
|
/// Gets or sets the list of <see cref="Team"/> instances stored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<Team> Teams { get; set; }
|
public IList<Team> Teams { get; set; } = new List<Team>();
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -1,204 +1,266 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections;
|
||||||
using System.IO;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.IO;
|
||||||
using Syroot.BinaryData;
|
using System.Text;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.BinaryData;
|
||||||
|
using Syroot.Worms.IO;
|
||||||
namespace Syroot.Worms
|
|
||||||
{
|
namespace Syroot.Worms
|
||||||
/// <summary>
|
{
|
||||||
/// Represents a directory of files stored in a DIR file, mostly used to store graphics files. Due to the nowadays
|
/// <summary>
|
||||||
/// small size of typical directories, the entries and data are loaded directly into a dictionary to profit from
|
/// Represents a directory of files stored in a DIR file, mostly used to store graphics files. Due to the nowadays
|
||||||
/// optimal performance when accessing and manipulating the directory.
|
/// small size of typical directories, the entries and data are loaded directly into a dictionary to profit from
|
||||||
/// Used by W2, WA and WWP. S. https://worms2d.info/Graphics_directory.
|
/// optimal performance when accessing and manipulating the directory.
|
||||||
/// </summary>
|
/// Used by W2, WA and WWP. S. https://worms2d.info/Graphics_directory.
|
||||||
public class Archive : Dictionary<string, byte[]>, ILoadableFile, ISaveableFile
|
/// </summary>
|
||||||
{
|
public class Archive : IDictionary<string, byte[]>, ILoadableFile, ISaveableFile
|
||||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
{
|
||||||
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
private const int _signature = 0x1A524944; // "DIR", 0x1A
|
|
||||||
private const int _tocSignature = 0x0000000A;
|
private const int _signature = 0x1A524944; // "DIR", 0x1A
|
||||||
|
private const int _tocSignature = 0x0000000A;
|
||||||
private const int _hashBits = 10;
|
|
||||||
private const int _hashSize = 1 << _hashBits;
|
private const int _hashBits = 10;
|
||||||
|
private const int _hashSize = 1 << _hashBits;
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
|
||||||
|
// ---- FIELDS -------------------------------------------------------------------------------------------------
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Archive"/> class.
|
private readonly IDictionary<string, byte[]> _entries = new Dictionary<string, byte[]>();
|
||||||
/// </summary>
|
|
||||||
public Archive() { }
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Archive"/> class, loading the data from the given
|
/// Initializes a new instance of the <see cref="Archive"/> class.
|
||||||
/// <see cref="Stream"/>.
|
/// </summary>
|
||||||
/// </summary>
|
public Archive() { }
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
|
||||||
public Archive(Stream stream) => Load(stream);
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Archive"/> class, loading the data from the given
|
||||||
/// <summary>
|
/// <see cref="Stream"/>.
|
||||||
/// Initializes a new instance of the <see cref="Archive"/> class, loading the data from the given file.
|
/// </summary>
|
||||||
/// </summary>
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
public Archive(Stream stream) => Load(stream);
|
||||||
public Archive(string fileName) => Load(fileName);
|
|
||||||
|
/// <summary>
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
/// Initializes a new instance of the <see cref="Archive"/> class, loading the data from the given file.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
/// Loads the data from the given <see cref="Stream"/>.
|
public Archive(string fileName) => Load(fileName);
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
// ---- OPERATORS ----------------------------------------------------------------------------------------------
|
||||||
public void Load(Stream stream)
|
|
||||||
{
|
/// <inheritdoc/>
|
||||||
if (!stream.CanSeek)
|
public byte[] this[string key]
|
||||||
throw new ArgumentException("Stream requires to be seekable.", nameof(stream));
|
{
|
||||||
|
get => _entries[key];
|
||||||
Clear();
|
set => _entries[key] = value;
|
||||||
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
}
|
||||||
|
|
||||||
// Read the header.
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
if (reader.ReadInt32() != _signature)
|
|
||||||
throw new InvalidDataException("Invalid DIR file signature.");
|
/// <inheritdoc/>
|
||||||
int fileSize = reader.ReadInt32();
|
public ICollection<string> Keys => _entries.Keys;
|
||||||
int tocOffset = reader.ReadInt32();
|
|
||||||
|
/// <inheritdoc/>
|
||||||
// Read the table of contents.
|
public ICollection<byte[]> Values => _entries.Values;
|
||||||
reader.Position = tocOffset;
|
|
||||||
int tocSignature = reader.ReadInt32();
|
/// <inheritdoc/>
|
||||||
if (tocSignature != _tocSignature)
|
public int Count => _entries.Count;
|
||||||
throw new InvalidDataException("Invalid DIR table of contents signature.");
|
|
||||||
// Generate a data dictionary out of the hash table and file entries.
|
/// <inheritdoc/>
|
||||||
int[] hashTable = reader.ReadInt32s(_hashSize);
|
bool ICollection<KeyValuePair<string, byte[]>>.IsReadOnly => false;
|
||||||
foreach (int entryOffset in hashTable)
|
|
||||||
{
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
// If the hash is not 0, it points to a list of files which have a hash being the hash table index.
|
|
||||||
if (entryOffset > 0)
|
/// <inheritdoc/>
|
||||||
{
|
public void Add(string key, byte[] value) => _entries.Add(key, value);
|
||||||
int nextEntryOffset = entryOffset;
|
|
||||||
do
|
/// <inheritdoc/>
|
||||||
{
|
public void Clear() => _entries.Clear();
|
||||||
reader.Position = tocOffset + nextEntryOffset;
|
/// <inheritdoc/>
|
||||||
nextEntryOffset = reader.ReadInt32();
|
public bool ContainsKey(string key) => _entries.ContainsKey(key);
|
||||||
int offset = reader.ReadInt32();
|
|
||||||
int length = reader.ReadInt32();
|
/// <inheritdoc/>
|
||||||
string name = reader.ReadString(StringCoding.ZeroTerminated);
|
public IEnumerator<KeyValuePair<string, byte[]>> GetEnumerator() => _entries.GetEnumerator();
|
||||||
using (reader.TemporarySeek(offset, SeekOrigin.Begin))
|
|
||||||
Add(name, reader.ReadBytes(length));
|
/// <summary>
|
||||||
} while (nextEntryOffset != 0);
|
/// Loads the data from the given <see cref="Stream"/>.
|
||||||
}
|
/// </summary>
|
||||||
}
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
}
|
public void Load(Stream stream)
|
||||||
|
{
|
||||||
/// <summary>
|
if (!stream.CanSeek)
|
||||||
/// Loads the data from the given file.
|
throw new ArgumentException("Stream requires to be seekable.", nameof(stream));
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
Clear();
|
||||||
public void Load(string fileName)
|
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
{
|
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
// Read the header.
|
||||||
Load(stream);
|
if (reader.ReadInt32() != _signature)
|
||||||
}
|
throw new InvalidDataException("Invalid DIR file signature.");
|
||||||
|
int fileSize = reader.ReadInt32();
|
||||||
/// <summary>
|
int tocOffset = reader.ReadInt32();
|
||||||
/// Saves the data into the given <paramref name="stream"/>.
|
|
||||||
/// </summary>
|
// Read the table of contents.
|
||||||
/// <param name="stream">The <see cref="Stream"/> to save the data in.</param>
|
reader.Position = tocOffset;
|
||||||
public void Save(Stream stream)
|
int tocSignature = reader.ReadInt32();
|
||||||
{
|
if (tocSignature != _tocSignature)
|
||||||
using BinaryStream writer = new BinaryStream(stream, leaveOpen: true);
|
throw new InvalidDataException("Invalid DIR table of contents signature.");
|
||||||
|
// Generate a data dictionary out of the hash table and file entries.
|
||||||
// Write the header.
|
int[] hashTable = reader.ReadInt32s(_hashSize);
|
||||||
writer.Write(_signature);
|
foreach (int entryOffset in hashTable)
|
||||||
uint fileSizeOffset = writer.ReserveOffset();
|
{
|
||||||
uint tocOffset = writer.ReserveOffset();
|
// If the hash is not 0, it points to a list of files which have a hash being the hash table index.
|
||||||
|
if (entryOffset > 0)
|
||||||
// Write the data and build the hash table and file entries.
|
{
|
||||||
List<HashTableEntry>[] hashTable = new List<HashTableEntry>[_hashSize];
|
int nextEntryOffset = entryOffset;
|
||||||
foreach (KeyValuePair<string, byte[]> item in this)
|
do
|
||||||
{
|
{
|
||||||
HashTableEntry entry = new HashTableEntry()
|
reader.Position = tocOffset + nextEntryOffset;
|
||||||
{
|
nextEntryOffset = reader.ReadInt32();
|
||||||
Name = item.Key,
|
int offset = reader.ReadInt32();
|
||||||
Offset = (int)writer.Position,
|
int length = reader.ReadInt32();
|
||||||
Length = item.Value.Length
|
string name = reader.ReadString(StringCoding.ZeroTerminated);
|
||||||
};
|
using (reader.TemporarySeek(offset, SeekOrigin.Begin))
|
||||||
writer.Write(item.Value);
|
Add(name, reader.ReadBytes(length));
|
||||||
|
} while (nextEntryOffset != 0);
|
||||||
int hash = CalculateHash(item.Key);
|
}
|
||||||
if (hashTable[hash] == null)
|
}
|
||||||
hashTable[hash] = new List<HashTableEntry>();
|
}
|
||||||
hashTable[hash].Add(entry);
|
|
||||||
}
|
/// <summary>
|
||||||
|
/// Loads the data from the given file.
|
||||||
// Write the hash table and file entries.
|
/// </summary>
|
||||||
int tocStart = (int)writer.Position;
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
int fileEntryOffset = sizeof(int) + _hashSize * sizeof(int);
|
public void Load(string fileName)
|
||||||
writer.SatisfyOffset(tocOffset, tocStart);
|
{
|
||||||
writer.Write(_tocSignature);
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
for (int i = 0; i < _hashSize; i++)
|
Load(stream);
|
||||||
{
|
}
|
||||||
List<HashTableEntry> entries = hashTable[i];
|
|
||||||
if (entries == null)
|
/// <inheritdoc/>
|
||||||
{
|
public bool Remove(string key) => _entries.Remove(key);
|
||||||
writer.Write(0);
|
|
||||||
}
|
/// <summary>
|
||||||
else
|
/// Saves the data into the given <paramref name="stream"/>.
|
||||||
{
|
/// </summary>
|
||||||
// Write the entries resolving to the current hash.
|
/// <param name="stream">The <see cref="Stream"/> to save the data in.</param>
|
||||||
writer.Write(fileEntryOffset);
|
public void Save(Stream stream)
|
||||||
using (writer.TemporarySeek(tocStart + fileEntryOffset, SeekOrigin.Begin))
|
{
|
||||||
{
|
using BinaryStream writer = new BinaryStream(stream, leaveOpen: true);
|
||||||
for (int j = 0; j < entries.Count; j++)
|
|
||||||
{
|
// Write the header.
|
||||||
HashTableEntry entry = entries[j];
|
writer.Write(_signature);
|
||||||
uint nextEntryOffset = writer.ReserveOffset();
|
uint fileSizeOffset = writer.ReserveOffset();
|
||||||
writer.Write(entry.Offset);
|
uint tocOffset = writer.ReserveOffset();
|
||||||
writer.Write(entry.Length);
|
|
||||||
writer.Write(entry.Name, StringCoding.ZeroTerminated);
|
// Write the data and build the hash table and file entries.
|
||||||
writer.Align(4);
|
List<HashTableEntry>[] hashTable = new List<HashTableEntry>[_hashSize];
|
||||||
if (j < entries.Count - 1)
|
foreach (KeyValuePair<string, byte[]> item in this)
|
||||||
writer.SatisfyOffset(nextEntryOffset, (int)writer.Position - tocStart);
|
{
|
||||||
}
|
HashTableEntry entry = new HashTableEntry()
|
||||||
fileEntryOffset = (int)writer.Position - tocStart;
|
{
|
||||||
}
|
Name = item.Key,
|
||||||
}
|
Offset = (int)writer.Position,
|
||||||
}
|
Length = item.Value.Length
|
||||||
|
};
|
||||||
writer.SatisfyOffset(fileSizeOffset, tocStart + fileEntryOffset - 1);
|
writer.Write(item.Value);
|
||||||
}
|
|
||||||
|
int hash = CalculateHash(item.Key);
|
||||||
/// <summary>
|
if (hashTable[hash] == null)
|
||||||
/// Saves the data in the file with the given <paramref name="fileName"/>.
|
hashTable[hash] = new List<HashTableEntry>();
|
||||||
/// </summary>
|
hashTable[hash].Add(entry);
|
||||||
/// <param name="fileName">The name of the file to save the data in.</param>
|
}
|
||||||
public void Save(string fileName)
|
|
||||||
{
|
// Write the hash table and file entries.
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
uint tocStart = (uint)writer.Position;
|
||||||
Save(stream);
|
uint fileEntryOffset = sizeof(int) + _hashSize * sizeof(int);
|
||||||
}
|
writer.SatisfyOffset(tocOffset, tocStart);
|
||||||
|
writer.Write(_tocSignature);
|
||||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
for (int i = 0; i < _hashSize; i++)
|
||||||
|
{
|
||||||
private int CalculateHash(string name)
|
List<HashTableEntry> entries = hashTable[i];
|
||||||
{
|
if (entries == null)
|
||||||
int hash = 0;
|
{
|
||||||
for (int i = 0; i < name.Length; i++)
|
writer.Write(0);
|
||||||
{
|
}
|
||||||
hash = ((hash << 1) % _hashSize) | (hash >> (_hashBits - 1) & 1);
|
else
|
||||||
hash += name[i];
|
{
|
||||||
hash %= _hashSize;
|
// Write the entries resolving to the current hash.
|
||||||
}
|
writer.Write(fileEntryOffset);
|
||||||
return hash;
|
using (writer.TemporarySeek(tocStart + fileEntryOffset, SeekOrigin.Begin))
|
||||||
}
|
{
|
||||||
|
for (int j = 0; j < entries.Count; j++)
|
||||||
// ---- STRUCTURES ---------------------------------------------------------------------------------------------
|
{
|
||||||
|
HashTableEntry entry = entries[j];
|
||||||
private struct HashTableEntry
|
uint nextEntryOffset = writer.ReserveOffset();
|
||||||
{
|
writer.Write(entry.Offset);
|
||||||
internal string Name;
|
writer.Write(entry.Length);
|
||||||
internal int Offset;
|
writer.Write(entry.Name, StringCoding.ZeroTerminated);
|
||||||
internal int Length;
|
writer.Align(4);
|
||||||
}
|
if (j < entries.Count - 1)
|
||||||
}
|
writer.SatisfyOffset(nextEntryOffset, (uint)writer.Position - tocStart);
|
||||||
}
|
}
|
||||||
|
fileEntryOffset = (uint)writer.Position - tocStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.SatisfyOffset(fileSizeOffset, tocStart + fileEntryOffset - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the data in the file with the given <paramref name="fileName"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileName">The name of the file to save the data in.</param>
|
||||||
|
public void Save(string fileName)
|
||||||
|
{
|
||||||
|
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
|
Save(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool TryGetValue(string key, out byte[] value) => _entries.TryGetValue(key, out value);
|
||||||
|
|
||||||
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private int CalculateHash(string name)
|
||||||
|
{
|
||||||
|
int hash = 0;
|
||||||
|
for (int i = 0; i < name.Length; i++)
|
||||||
|
{
|
||||||
|
hash = ((hash << 1) % _hashSize) | (hash >> (_hashBits - 1) & 1);
|
||||||
|
hash += name[i];
|
||||||
|
hash %= _hashSize;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- METHODS ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void ICollection<KeyValuePair<string, byte[]>>.Add(KeyValuePair<string, byte[]> item) => _entries.Add(item);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
bool ICollection<KeyValuePair<string, byte[]>>.Contains(KeyValuePair<string, byte[]> item) => _entries.Contains(item);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void ICollection<KeyValuePair<string, byte[]>>.CopyTo(KeyValuePair<string, byte[]>[] array, int arrayIndex) => _entries.CopyTo(array, arrayIndex);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => _entries.GetEnumerator();
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
bool ICollection<KeyValuePair<string, byte[]>>.Remove(KeyValuePair<string, byte[]> item) => _entries.Remove(item);
|
||||||
|
|
||||||
|
// ---- CLASSES, STRUCTS & ENUMS -------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private struct HashTableEntry
|
||||||
|
{
|
||||||
|
internal string Name;
|
||||||
|
internal int Offset;
|
||||||
|
internal int Length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -31,7 +31,7 @@ namespace Syroot.Worms.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="self">The extended <see cref="Byte"/> instance.</param>
|
/// <param name="self">The extended <see cref="Byte"/> instance.</param>
|
||||||
/// <param name="index">The 0-based index of the bit to check.</param>
|
/// <param name="index">The 0-based index of the bit to check.</param>
|
||||||
/// <returns><c>true</c> when the bit is set; otherwise <c>false</c>.</returns>
|
/// <returns><see langword="true"/> when the bit is set; otherwise <see langword="false"/>.</returns>
|
||||||
public static bool GetBit(this byte self, int index) => (self & (1 << index)) != 0;
|
public static bool GetBit(this byte self, int index) => (self & (1 << index)) != 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -62,7 +62,7 @@ namespace Syroot.Worms.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="self">The extended <see cref="Byte"/> instance.</param>
|
/// <param name="self">The extended <see cref="Byte"/> instance.</param>
|
||||||
/// <param name="index">The 0-based index of the bit to enable or disable.</param>
|
/// <param name="index">The 0-based index of the bit to enable or disable.</param>
|
||||||
/// <param name="enable"><c>true</c> to enable the bit; otherwise <c>false</c>.</param>
|
/// <param name="enable"><see langword="true"/> to enable the bit; otherwise <see langword="false"/>.</param>
|
||||||
/// <returns>The current byte with the bit enabled or disabled.</returns>
|
/// <returns>The current byte with the bit enabled or disabled.</returns>
|
||||||
public static byte SetBit(this byte self, int index, bool enable)
|
public static byte SetBit(this byte self, int index, bool enable)
|
||||||
=> enable ? EnableBit(self, index) : DisableBit(self, index);
|
=> enable ? EnableBit(self, index) : DisableBit(self, index);
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Drawing.Imaging;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.Core.Graphics
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a collection of methods helping with palettized images.
|
|
||||||
/// </summary>
|
|
||||||
public static class BitmapTools
|
|
||||||
{
|
|
||||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an indexed <see cref="Bitmap"/> with the given <paramref name="size"/> and
|
|
||||||
/// <paramref name="palette"/> from the provided raw <paramref name="data"/> array.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="size">The dimensions of the image.</param>
|
|
||||||
/// <param name="palette">The palette as a <see cref="Color"/> array.</param>
|
|
||||||
/// <param name="data">The data array storing bytes indexing the palette color array.</param>
|
|
||||||
/// <returns>The <see cref="Bitmap"/> instance.</returns>
|
|
||||||
public static unsafe Bitmap CreateIndexed(Size size, IList<Color> palette, byte[] data)
|
|
||||||
{
|
|
||||||
// Transfer the pixel data, respecting power-of-2 strides.
|
|
||||||
Bitmap bitmap = new Bitmap(size.Width, size.Height, PixelFormat.Format8bppIndexed);
|
|
||||||
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, size.Width, size.Height),
|
|
||||||
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
|
|
||||||
Span<byte> bitmapSpan = new Span<byte>(bitmapData.Scan0.ToPointer(), bitmapData.Stride * bitmapData.Height);
|
|
||||||
for (int y = 0; y < size.Height; y++)
|
|
||||||
data.AsSpan(y * size.Width, size.Width).CopyTo(bitmapSpan.Slice(y * bitmapData.Stride));
|
|
||||||
bitmap.UnlockBits(bitmapData);
|
|
||||||
|
|
||||||
// Transfer the palette.
|
|
||||||
ColorPalette bitmapPalette = bitmap.Palette;
|
|
||||||
for (int i = 0; i < palette.Count; i++)
|
|
||||||
bitmapPalette.Entries[i] = palette[i];
|
|
||||||
bitmap.Palette = bitmapPalette;
|
|
||||||
|
|
||||||
return bitmap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,265 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using Syroot.BinaryData;
|
|
||||||
|
|
||||||
namespace Syroot.Worms.Core.IO
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents extension methods for <see cref="BinaryStream"/> instances.
|
|
||||||
/// </summary>
|
|
||||||
[DebuggerStepThrough]
|
|
||||||
public static partial class BinaryStreamExtensions
|
|
||||||
{
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// ---- Reading ----
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads an <see cref="ILoadable"/> instance from the current stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the <see cref="ILoadable"/> class to instantiate.</typeparam>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <returns>The <see cref="ILoadable"/> instance.</returns>
|
|
||||||
public static T Load<T>(this BinaryStream self) where T : ILoadable, new()
|
|
||||||
{
|
|
||||||
T instance = new T();
|
|
||||||
instance.Load(self.BaseStream);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads <see cref="ILoadable"/> instances from the current stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the <see cref="ILoadable"/> class to instantiate.</typeparam>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <param name="count">The number of instances to read.</param>
|
|
||||||
/// <returns>The <see cref="ILoadable"/> instances.</returns>
|
|
||||||
public static T[] Load<T>(this BinaryStream self, int count) where T : ILoadable, new()
|
|
||||||
{
|
|
||||||
T[] instances = new T[count];
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
instances[i] = Load<T>(self);
|
|
||||||
return instances;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads an RGBA 32-bit color.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <returns>The read color.</returns>
|
|
||||||
public static Color ReadColor(this BinaryStream self) => Color.FromArgb(self.ReadInt32());
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads <paramref name="count"/> RGBA 32-bit colors.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <param name="count">The number of values to read.</param>
|
|
||||||
/// <returns>The read colors.</returns>
|
|
||||||
public static Color[] ReadColors(this BinaryStream self, int count)
|
|
||||||
{
|
|
||||||
Color[] values = new Color[count];
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
values[i] = Color.FromArgb(self.ReadInt32());
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads a 0-terminated string which is stored in a fixed-size block of <paramref name="length"/> bytes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <param name="length">The number of bytes the fixed-size blocks takes.</param>
|
|
||||||
/// <returns>The read string.</returns>
|
|
||||||
public static string ReadString(this BinaryStream self, int length)
|
|
||||||
{
|
|
||||||
// TODO: This may not work with multi-byte encodings.
|
|
||||||
// Ensure to not try to decode any bytes after the 0 termination.
|
|
||||||
byte[] bytes = self.ReadBytes(length);
|
|
||||||
while (bytes[--length] == 0 && length > 0) { }
|
|
||||||
return self.Encoding.GetString(bytes, 0, length + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads <paramref name="count"/> 0-terminated strings which are stored in a fixed-size block of
|
|
||||||
/// <paramref name="length"/> bytes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <param name="count">The number of values to read.</param>
|
|
||||||
/// <param name="length">The number of bytes the fixed-size blocks takes.</param>
|
|
||||||
/// <returns>The read strings.</returns>
|
|
||||||
public static string[] ReadStrings(this BinaryStream self, int count, int length)
|
|
||||||
{
|
|
||||||
string[] strings = new string[count];
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
strings[i] = ReadString(self, length);
|
|
||||||
return strings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads a raw byte structure from the current stream and returns it.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the structure to read.</typeparam>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <returns>The structure of type <typeparamref name="T"/>.</returns>
|
|
||||||
public static T ReadStruct<T>(this BinaryStream self) where T : unmanaged
|
|
||||||
{
|
|
||||||
// Read the raw bytes of the structure.
|
|
||||||
#if NETSTANDARD2_0
|
|
||||||
byte[] bytes = new byte[Unsafe.SizeOf<T>()];
|
|
||||||
self.Read(bytes, 0, bytes.Length);
|
|
||||||
#else
|
|
||||||
Span<byte> bytes = stackalloc byte[Unsafe.SizeOf<T>()];
|
|
||||||
self.Read(bytes);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Convert them to a structure instance and return it.
|
|
||||||
ReadOnlySpan<T> span = MemoryMarshal.Cast<byte, T>(bytes);
|
|
||||||
return span[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads raw byte structures from the current stream and returns them.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the structure to read.</typeparam>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <param name="count">The number of values to read.</param>
|
|
||||||
/// <returns>The structures of type <typeparamref name="T"/>.</returns>
|
|
||||||
public static T[] ReadStructs<T>(this BinaryStream self, int count) where T : unmanaged
|
|
||||||
{
|
|
||||||
// Read the raw bytes of the structures.
|
|
||||||
#if NETSTANDARD2_0
|
|
||||||
byte[] bytes = new byte[Unsafe.SizeOf<T>()];
|
|
||||||
self.Read(bytes, 0, bytes.Length);
|
|
||||||
#else
|
|
||||||
Span<byte> bytes = stackalloc byte[Unsafe.SizeOf<T>() * count];
|
|
||||||
self.Read(bytes);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Convert them to a structure array and return it.
|
|
||||||
ReadOnlySpan<T> span = MemoryMarshal.Cast<byte, T>(bytes);
|
|
||||||
return span.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the current address of the stream to which a 4-byte placeholder has been written which can be filled
|
|
||||||
/// later.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <returns>The address at which a 4-byte placeholder has been written to.</returns>
|
|
||||||
public static uint ReserveOffset(this BinaryStream self)
|
|
||||||
{
|
|
||||||
uint offset = (uint)self.Position;
|
|
||||||
self.WriteUInt32(0);
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- Writing ----
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes the given <see cref="ISaveable"/> instance into the current stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the <see cref="ISaveable"/> class to write.</typeparam>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <param name="value">The instance to write into the current stream.</param>
|
|
||||||
public static void Save<T>(this BinaryStream self, T value) where T : ISaveable
|
|
||||||
{
|
|
||||||
value.Save(self.BaseStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes the given <see cref="ISaveable"/> instances into the current stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the <see cref="ISaveable"/> class to write.</typeparam>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <param name="values">The instances to write into the current stream.</param>
|
|
||||||
public static void Save<T>(this BinaryStream self, IList<T> values) where T : ISaveable
|
|
||||||
{
|
|
||||||
foreach (T value in values)
|
|
||||||
Save(self, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SatisfyOffset(this BinaryStream self, uint offset, int value)
|
|
||||||
{
|
|
||||||
using (self.TemporarySeek(offset, SeekOrigin.Begin))
|
|
||||||
self.WriteInt32(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes the given color.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <param name="value">The color to write.</param>
|
|
||||||
public static void WriteColor(this BinaryStream self, Color color)
|
|
||||||
=> self.Write(color.R << 24 | color.G << 16 | color.B << 8 | color.A);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes the given colors.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <param name="value">The colors to write.</param>
|
|
||||||
public static void WriteColors(this BinaryStream self, IEnumerable<Color> colors)
|
|
||||||
{
|
|
||||||
foreach (Color color in colors)
|
|
||||||
WriteColor(self, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes the given string into a fixed-size block of <paramref name="length"/> bytes and 0-terminates it.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <param name="value">The string to write.</param>
|
|
||||||
/// <param name="length">The number of bytes the fixed-size block takes.</param>
|
|
||||||
public static void WriteString(this BinaryStream self, string value, int length)
|
|
||||||
{
|
|
||||||
// TODO: This may not work with multi-byte encodings.
|
|
||||||
byte[] bytes = new byte[length];
|
|
||||||
if (value != null)
|
|
||||||
self.Encoding.GetBytes(value, 0, value.Length, bytes, 0);
|
|
||||||
self.WriteBytes(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes the given strings into fixed-size blocks of <paramref name="length"/> bytes and 0-terminates them.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <param name="values">The strings to write.</param>
|
|
||||||
/// <param name="length">The number of bytes a fixed-size block takes.</param>
|
|
||||||
public static void WriteStrings(this BinaryStream self, IEnumerable<string> values, int length)
|
|
||||||
{
|
|
||||||
foreach (string value in values)
|
|
||||||
WriteString(self, value, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes the bytes of a structure into the current stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the structure to read.</typeparam>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <param name="value">The structure to write.</param>
|
|
||||||
public static unsafe void WriteStruct<T>(this BinaryStream self, T value) where T : unmanaged
|
|
||||||
{
|
|
||||||
ReadOnlySpan<byte> bytes = new ReadOnlySpan<byte>(Unsafe.AsPointer(ref value), Unsafe.SizeOf<T>());
|
|
||||||
#if NETSTANDARD2_0
|
|
||||||
self.Write(bytes.ToArray()); // cannot prevent copy in .NET Standard 2.0
|
|
||||||
#else
|
|
||||||
self.Write(bytes);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes the bytes of structures into the current stream.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the structure to read.</typeparam>
|
|
||||||
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
|
||||||
/// <param name="values">The structures to write.</param>
|
|
||||||
public static void WriteStructs<T>(this BinaryStream self, IEnumerable<T> values) where T : unmanaged
|
|
||||||
{
|
|
||||||
foreach (T value in values)
|
|
||||||
WriteStruct(self, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +1,18 @@
|
|||||||
using System.Drawing;
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
namespace Syroot.Worms.Core.Graphics
|
|
||||||
{
|
namespace Syroot.Worms.Graphics
|
||||||
/// <summary>
|
{
|
||||||
/// Represents an interface for any class storing indexed image palette colors.
|
/// <summary>
|
||||||
/// </summary>
|
/// Represents an interface for any class storing indexed image palette colors.
|
||||||
public interface IPalette
|
/// </summary>
|
||||||
{
|
public interface IPalette
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
{
|
||||||
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the <see cref="Color"/> values stored by this palette.
|
/// <summary>
|
||||||
/// </summary>
|
/// Gets or sets the <see cref="Color"/> values stored by this palette.
|
||||||
Color[] Colors { get; set; }
|
/// </summary>
|
||||||
}
|
IList<Color> Colors { get; set; }
|
||||||
}
|
}
|
||||||
|
}
|
@ -1,61 +1,85 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Drawing.Imaging;
|
using System.Drawing.Imaging;
|
||||||
|
|
||||||
namespace Syroot.Worms
|
namespace Syroot.Worms
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a pixel-based 2D image in different color formats.
|
/// Represents a pixel-based 2D image in different color formats.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RawBitmap
|
public class RawBitmap
|
||||||
{
|
{
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the number of bits required to describe a color per pixel.
|
/// Gets or sets the number of bits required to describe a color per pixel.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte BitsPerPixel { get; set; }
|
public byte BitsPerPixel { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the colors in the palette of the bitmap, if it has one.
|
/// Gets or sets the colors in the palette of the bitmap, if it has one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<Color> Palette { get; set; }
|
public IList<Color>? Palette { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the size of the image in pixels.
|
/// Gets or sets the size of the image in pixels.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Size Size { get; set; }
|
public Size Size { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the data of the image pixels.
|
/// Gets or sets the data of the image pixels.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte[] Data { get; set; }
|
public byte[] Data { get; set; } = Array.Empty<byte>();
|
||||||
|
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="Bitmap"/> from the raw data.
|
/// Creates a <see cref="Bitmap"/> from the raw data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The <see cref="Bitmap"/> created from the raw data.</returns>
|
/// <returns>The <see cref="Bitmap"/> created from the raw data.</returns>
|
||||||
public unsafe Bitmap ToBitmap()
|
public unsafe Bitmap ToBitmap()
|
||||||
{
|
{
|
||||||
// Transfer the pixel data, respecting power-of-2 strides.
|
// Create bitmap with appropriate pixel format.
|
||||||
Bitmap bitmap = new Bitmap(Size.Width, Size.Height, PixelFormat.Format8bppIndexed);
|
PixelFormat pixelFormat = BitsPerPixel switch
|
||||||
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, Size.Width, Size.Height),
|
{
|
||||||
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
|
1 => PixelFormat.Format1bppIndexed,
|
||||||
Span<byte> bitmapSpan = new Span<byte>(bitmapData.Scan0.ToPointer(), bitmapData.Stride * bitmapData.Height);
|
8 => PixelFormat.Format8bppIndexed,
|
||||||
|
32 => PixelFormat.Format32bppRgb,
|
||||||
|
_ => throw new NotSupportedException($"Cannot convert to {BitsPerPixel}bpp bitmap.")
|
||||||
|
};
|
||||||
|
Bitmap bitmap = new Bitmap(Size.Width, Size.Height, pixelFormat);
|
||||||
|
|
||||||
|
// Transfer the pixel data, respecting power-of-2 strides.
|
||||||
|
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, Size.Width, Size.Height),
|
||||||
|
ImageLockMode.WriteOnly, pixelFormat);
|
||||||
|
float bytesPerPixel = BitsPerPixel / 8f;
|
||||||
|
Span<byte> bitmapSpan = new Span<byte>(bitmapData.Scan0.ToPointer(), bitmapData.Stride * bitmapData.Height);
|
||||||
for (int y = 0; y < Size.Height; y++)
|
for (int y = 0; y < Size.Height; y++)
|
||||||
Data.AsSpan(y * Size.Width, Size.Width).CopyTo(bitmapSpan.Slice(y * bitmapData.Stride));
|
{
|
||||||
bitmap.UnlockBits(bitmapData);
|
Data.AsSpan((int)(y * Size.Width * bytesPerPixel), (int)(Size.Width * bytesPerPixel))
|
||||||
|
.CopyTo(bitmapSpan.Slice((int)(y * bitmapData.Stride * bytesPerPixel)));
|
||||||
// Transfer the palette.
|
}
|
||||||
ColorPalette bitmapPalette = bitmap.Palette;
|
bitmap.UnlockBits(bitmapData);
|
||||||
for (int i = 0; i < Palette.Count; i++)
|
|
||||||
bitmapPalette.Entries[i] = Palette[i];
|
// Transfer any palette.
|
||||||
bitmap.Palette = bitmapPalette;
|
switch (pixelFormat)
|
||||||
|
{
|
||||||
return bitmap;
|
case PixelFormat.Format1bppIndexed:
|
||||||
}
|
ColorPalette palette1bpp = bitmap.Palette;
|
||||||
}
|
palette1bpp.Entries[0] = Color.Black;
|
||||||
}
|
palette1bpp.Entries[1] = Color.White;
|
||||||
|
bitmap.Palette = palette1bpp;
|
||||||
|
break;
|
||||||
|
case PixelFormat.Format8bppIndexed:
|
||||||
|
ColorPalette palette8bpp = bitmap.Palette;
|
||||||
|
for (int i = 0; i < Palette!.Count; i++)
|
||||||
|
palette8bpp.Entries[i] = Palette[i];
|
||||||
|
bitmap.Palette = palette8bpp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,80 +1,115 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Core
|
namespace Syroot.Worms.Graphics
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents methods to decompress data which is compressed using Team17's internal compression algorithm for
|
/// Represents methods to decompress data which is compressed using Team17's internal compression algorithm for
|
||||||
/// graphic file formats.
|
/// graphic file formats.
|
||||||
/// S. http://worms2d.info/Team17_compression.
|
/// S. http://worms2d.info/Team17_compression.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class Team17Compression
|
internal static class Team17Compression
|
||||||
{
|
{
|
||||||
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
// ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the data available in <paramref name="bytes"/> in compressed format.
|
/// Returns the data available in <paramref name="bytes"/> in compressed format.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bytes">The data to compress.</param>
|
/// <param name="bytes">The data to compress.</param>
|
||||||
/// <returns>The compressed data.</returns>
|
/// <returns>The compressed data.</returns>
|
||||||
internal static byte[] Compress(byte[] bytes)
|
internal static byte[] Compress(ReadOnlySpan<byte> bytes)
|
||||||
=> throw new NotImplementedException("Compressing data has not been implemented yet.");
|
=> throw new NotImplementedException("Compressing data has not been implemented yet.");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Decompresses the data available in the given <paramref name="stream"/> into the provided
|
/// Decompresses the data available in the given <paramref name="stream"/> into the provided
|
||||||
/// <paramref name="buffer"/>.
|
/// <paramref name="buffer"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to read the data from.</param>
|
/// <param name="stream">The <see cref="Stream"/> to read the data from.</param>
|
||||||
/// <param name="buffer">The byte array buffer to write the decompressed data to.</param>
|
/// <param name="buffer">The byte buffer to write the decompressed data to.</param>
|
||||||
internal static void Decompress(Stream stream, byte[] buffer)
|
internal static void Decompress(Stream stream, Span<byte> buffer)
|
||||||
{
|
{
|
||||||
// TODO: This fails for compressed data in CD:\Data\Mission\Training0-9.img.
|
int output = 0; // Offset of next write.
|
||||||
int output = 0; // Offset of next write.
|
int cmd;
|
||||||
int cmd;
|
while ((cmd = stream.ReadByte()) != -1)
|
||||||
while ((cmd = stream.ReadByte()) != -1)
|
{
|
||||||
{
|
// Read a byte.
|
||||||
// Read a byte.
|
if ((cmd & 0x80) == 0)
|
||||||
if ((cmd & 0x80) == 0)
|
{
|
||||||
{
|
// Command: 1 byte (color)
|
||||||
// Command: 1 byte (color)
|
buffer[output++] = (byte)cmd;
|
||||||
buffer[output++] = (byte)cmd;
|
}
|
||||||
}
|
else
|
||||||
else
|
{
|
||||||
{
|
int arg1 = cmd >> 3 & 0b1111; // bits 2-5
|
||||||
// Arg1 = bits 2-5
|
int arg2 = stream.ReadByte();
|
||||||
int arg1 = (cmd >> 3) & 0xF;
|
if (arg2 == -1)
|
||||||
int arg2 = stream.ReadByte();
|
return;
|
||||||
if (arg2 == -1)
|
// Arg2 = bits 6-16
|
||||||
return;
|
arg2 = ((cmd << 8) | arg2) & 0x7FF;
|
||||||
// Arg2 = bits 6-16
|
if (arg1 == 0)
|
||||||
arg2 = ((cmd << 8) | arg2) & 0x7FF;
|
{
|
||||||
if (arg1 == 0)
|
// Command: 0x80 0x00
|
||||||
{
|
if (arg2 == 0)
|
||||||
// Command: 0x80 0x00
|
return;
|
||||||
if (arg2 == 0)
|
int arg3 = stream.ReadByte();
|
||||||
return;
|
if (arg3 == -1)
|
||||||
int arg3 = stream.ReadByte();
|
return;
|
||||||
if (arg3 == -1)
|
// Command: 3 bytes
|
||||||
return;
|
output = CopyData(output, arg2, arg3 + 18, buffer);
|
||||||
// Command: 3 bytes
|
}
|
||||||
output = CopyData(output, arg2, arg3 + 18, buffer);
|
else
|
||||||
}
|
{
|
||||||
else
|
// Command: 2 bytes
|
||||||
{
|
output = CopyData(output, arg2 + 1, arg1 + 2, buffer);
|
||||||
// Command: 2 bytes
|
}
|
||||||
output = CopyData(output, arg2 + 1, arg1 + 2, buffer);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
// Own Span reimplementation, has the same issues with some out-of-bounds-access maps. Supports color remap.
|
||||||
|
//internal static int Decompress(ReadOnlySpan<byte> src, Span<byte> dst, ReadOnlySpan<byte> colorMap)
|
||||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
//{
|
||||||
|
// int srcPos = 0;
|
||||||
private static int CopyData(int offset, int compressedOffset, int count, byte[] buffer)
|
// int dstPos = 0;
|
||||||
{
|
// int count, seek;
|
||||||
for (; count > 0; count--)
|
//
|
||||||
buffer[offset] = buffer[offset++ - compressedOffset];
|
// while (true)
|
||||||
return offset;
|
// {
|
||||||
}
|
// while (true)
|
||||||
}
|
// {
|
||||||
}
|
// while ((src[srcPos] & 0b1000_0000) == 0)
|
||||||
|
// dst[dstPos++] = colorMap[src[srcPos++]];
|
||||||
|
//
|
||||||
|
// if ((src[srcPos] & 0b0111_1000) == 0)
|
||||||
|
// break;
|
||||||
|
//
|
||||||
|
// count = (src[srcPos] >> 3 & 0b0000_1111) + 2;
|
||||||
|
// seek = (src[srcPos + 1] | ((src[srcPos] & 0b0000_0111) << 8)) + 1;
|
||||||
|
// while (count-- > 0)
|
||||||
|
// dst[dstPos] = dst[dstPos++ - seek];
|
||||||
|
// srcPos += 2;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// seek = src[srcPos + 1] | (src[srcPos] & 0b0000_0111) << 8;
|
||||||
|
// if (seek == 0)
|
||||||
|
// break;
|
||||||
|
// count = src[srcPos + 2] + 18;
|
||||||
|
// while (count-- > 0)
|
||||||
|
// dst[dstPos] = dst[dstPos++ - seek];
|
||||||
|
//
|
||||||
|
// srcPos += 3;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return dstPos;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static int CopyData(int offset, int compressedOffset, int count, Span<byte> buffer)
|
||||||
|
{
|
||||||
|
for (; count > 0; count--)
|
||||||
|
buffer[offset] = buffer[offset++ - compressedOffset];
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
140
src/library/Syroot.Worms/IO/BinaryStreamExtensions.cs
Normal file
140
src/library/Syroot.Worms/IO/BinaryStreamExtensions.cs
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using Syroot.BinaryData;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.IO
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents extension methods for <see cref="BinaryStream"/> instances.
|
||||||
|
/// </summary>
|
||||||
|
public static partial class BinaryStreamExtensions
|
||||||
|
{
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ---- Reading ----
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads an <see cref="ILoadable"/> instance from the current stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the <see cref="ILoadable"/> class to instantiate.</typeparam>
|
||||||
|
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
||||||
|
/// <returns>The <see cref="ILoadable"/> instance.</returns>
|
||||||
|
public static T Load<T>(this BinaryStream self) where T : ILoadable, new()
|
||||||
|
{
|
||||||
|
T instance = new T();
|
||||||
|
instance.Load(self.BaseStream);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads an RGBA 32-bit color.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
||||||
|
/// <returns>The read color.</returns>
|
||||||
|
public static Color ReadColor(this BinaryStream self) => Color.FromArgb(self.ReadInt32());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads <paramref name="count"/> RGBA 32-bit colors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
||||||
|
/// <param name="count">The number of values to read.</param>
|
||||||
|
/// <returns>The read colors.</returns>
|
||||||
|
public static Color[] ReadColors(this BinaryStream self, int count)
|
||||||
|
{
|
||||||
|
Color[] values = new Color[count];
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
values[i] = Color.FromArgb(self.ReadInt32());
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads a 0-terminated string which is stored in a fixed-size block of <paramref name="length"/> bytes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
||||||
|
/// <param name="length">The number of bytes the fixed-size blocks takes.</param>
|
||||||
|
/// <returns>The read string.</returns>
|
||||||
|
public static string ReadFixedString(this BinaryStream self, int length)
|
||||||
|
{
|
||||||
|
// TODO: This may not work with multi-byte encodings.
|
||||||
|
// Ensure to not try to decode any bytes after the 0 termination.
|
||||||
|
byte[] bytes = self.ReadBytes(length);
|
||||||
|
for (length = 0; length < bytes.Length && bytes[length] != 0; length++) ;
|
||||||
|
return self.Encoding.GetString(bytes, 0, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the current position of the stream at which a 4-byte placeholder has been written which can be
|
||||||
|
/// filled later with <see cref="SatisfyOffset"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
||||||
|
/// <returns>The position at which a 4-byte placeholder has been written to.</returns>
|
||||||
|
public static uint ReserveOffset(this BinaryStream self)
|
||||||
|
{
|
||||||
|
uint offset = (uint)self.Position;
|
||||||
|
self.WriteUInt32(0);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Writing ----
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the given <see cref="ISaveable"/> instance into the current stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the <see cref="ISaveable"/> class to write.</typeparam>
|
||||||
|
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
||||||
|
/// <param name="value">The instance to write into the current stream.</param>
|
||||||
|
public static void Save<T>(this BinaryStream self, T value) where T : ISaveable
|
||||||
|
{
|
||||||
|
value.Save(self.BaseStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the given <paramref name="value"/> to the given <paramref name="offset"/>. This is meant to be used
|
||||||
|
/// in combination with <see cref="ReserveOffset"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
||||||
|
/// <param name="offset">The position at which to write the value.</param>
|
||||||
|
/// <param name="value">The value to write.</param>
|
||||||
|
public static void SatisfyOffset(this BinaryStream self, uint offset, uint value)
|
||||||
|
{
|
||||||
|
using var _ = self.TemporarySeek(offset, SeekOrigin.Begin);
|
||||||
|
self.WriteUInt32(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the given color.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
||||||
|
/// <param name="value">The color to write.</param>
|
||||||
|
public static void WriteColor(this BinaryStream self, Color color)
|
||||||
|
=> self.Write(color.A << 24 | color.R << 16 | color.G << 8 | color.B);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the given colors.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
||||||
|
/// <param name="value">The colors to write.</param>
|
||||||
|
public static void WriteColors(this BinaryStream self, IEnumerable<Color> colors)
|
||||||
|
{
|
||||||
|
foreach (Color color in colors)
|
||||||
|
WriteColor(self, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the given string into a fixed-size block of <paramref name="length"/> bytes and 0-terminates it.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="self">The extended <see cref="BinaryStream"/> instance.</param>
|
||||||
|
/// <param name="value">The string to write.</param>
|
||||||
|
/// <param name="length">The number of bytes the fixed-size block takes.</param>
|
||||||
|
public static void WriteFixedString(this BinaryStream self, string value, int length)
|
||||||
|
{
|
||||||
|
// TODO: This may not work with multi-byte encodings.
|
||||||
|
byte[] bytes = new byte[length];
|
||||||
|
if (value != null)
|
||||||
|
self.Encoding.GetBytes(value, 0, value.Length, bytes, 0);
|
||||||
|
self.WriteBytes(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Core.IO
|
namespace Syroot.Worms.IO
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents data which can be loaded by providing a <see cref="Stream"/> to read from.
|
/// Represents data which can be loaded by providing a <see cref="Stream"/> to read from.
|
@ -1,4 +1,4 @@
|
|||||||
namespace Syroot.Worms.Core.IO
|
namespace Syroot.Worms.IO
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a file which can be loaded by providing a file name.
|
/// Represents a file which can be loaded by providing a file name.
|
@ -1,6 +1,6 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Core.IO
|
namespace Syroot.Worms.IO
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents data which can be saved by providing a <see cref="Stream "/>to write to.
|
/// Represents data which can be saved by providing a <see cref="Stream "/>to write to.
|
@ -1,4 +1,4 @@
|
|||||||
namespace Syroot.Worms.Core.IO
|
namespace Syroot.Worms.IO
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a file which can be saved by providing a file name.
|
/// Represents a file which can be saved by providing a file name.
|
@ -4,7 +4,7 @@ using System.IO;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Syroot.BinaryData;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.IO;
|
||||||
|
|
||||||
namespace Syroot.Worms.Core.Riff
|
namespace Syroot.Worms.Core.Riff
|
||||||
{
|
{
|
||||||
@ -82,10 +82,10 @@ namespace Syroot.Worms.Core.Riff
|
|||||||
|
|
||||||
chunkSaver.Value.Invoke(this, new object[] { writer });
|
chunkSaver.Value.Invoke(this, new object[] { writer });
|
||||||
|
|
||||||
writer.SatisfyOffset(chunkSizeOffset, (int)(writer.Position - chunkSizeOffset - 4));
|
writer.SatisfyOffset(chunkSizeOffset, (uint)(writer.Position - chunkSizeOffset - 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.SatisfyOffset(fileSizeOffset, (int)(writer.Position - 8));
|
writer.SatisfyOffset(fileSizeOffset, (uint)(writer.Position - 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
@ -102,8 +102,6 @@ namespace Syroot.Worms.Core.Riff
|
|||||||
typeData.FileIdentifier = typeInfo.GetCustomAttribute<RiffFileAttribute>().Identifier;
|
typeData.FileIdentifier = typeInfo.GetCustomAttribute<RiffFileAttribute>().Identifier;
|
||||||
|
|
||||||
// Get the chunk loading and saving handlers.
|
// Get the chunk loading and saving handlers.
|
||||||
typeData.ChunkLoaders = new Dictionary<string, MethodInfo>();
|
|
||||||
typeData.ChunkSavers = new Dictionary<string, MethodInfo>();
|
|
||||||
foreach (MethodInfo method in typeInfo.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance))
|
foreach (MethodInfo method in typeInfo.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance))
|
||||||
{
|
{
|
||||||
RiffChunkLoadAttribute loadAttribute = method.GetCustomAttribute<RiffChunkLoadAttribute>();
|
RiffChunkLoadAttribute loadAttribute = method.GetCustomAttribute<RiffChunkLoadAttribute>();
|
||||||
@ -137,9 +135,9 @@ namespace Syroot.Worms.Core.Riff
|
|||||||
|
|
||||||
private class TypeData
|
private class TypeData
|
||||||
{
|
{
|
||||||
internal string FileIdentifier;
|
internal string FileIdentifier = String.Empty;
|
||||||
internal Dictionary<string, MethodInfo> ChunkLoaders;
|
internal IDictionary<string, MethodInfo> ChunkLoaders = new Dictionary<string, MethodInfo>();
|
||||||
internal Dictionary<string, MethodInfo> ChunkSavers;
|
internal IDictionary<string, MethodInfo> ChunkSavers = new Dictionary<string, MethodInfo>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
101
src/library/Syroot.Worms/IO/StreamExtensions.cs
Normal file
101
src/library/Syroot.Worms/IO/StreamExtensions.cs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Syroot.Worms.IO
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents extension methods for <see cref="Stream"/> instances.
|
||||||
|
/// </summary>
|
||||||
|
public static class StreamExtensions
|
||||||
|
{
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the unmanaged representation of a struct of type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the struct to read.</typeparam>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> instance to read with.</param>
|
||||||
|
/// <returns>The read value.</returns>
|
||||||
|
public static T ReadStruct<T>(this Stream stream) where T : unmanaged
|
||||||
|
{
|
||||||
|
Span<T> span = stackalloc T[1];
|
||||||
|
stream.Read(MemoryMarshal.Cast<T, byte>(span));
|
||||||
|
return span[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the unmanaged representations of structs of type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the structs to read.</typeparam>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> instance to read with.</param>
|
||||||
|
/// <param name="span">A span receiving the read instances.</param>
|
||||||
|
public static void ReadStructs<T>(this Stream stream, Span<T> span) where T : unmanaged
|
||||||
|
=> stream.Read(MemoryMarshal.Cast<T, byte>(span));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the unmanaged representation of a struct of type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the struct to write.</typeparam>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> instance to write with.</param>
|
||||||
|
/// <param name="value">The value to write.</param>
|
||||||
|
public static unsafe void WriteStruct<T>(this Stream stream, T value) where T : unmanaged
|
||||||
|
=> stream.Write(new ReadOnlySpan<byte>(Unsafe.AsPointer(ref value), Unsafe.SizeOf<T>()));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the unmanaged representation of a struct of type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the struct to write.</typeparam>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> instance to write with.</param>
|
||||||
|
/// <param name="value">The value to write.</param>
|
||||||
|
public static unsafe void WriteStruct<T>(this Stream stream, ref T value) where T : unmanaged
|
||||||
|
=> stream.Write(new ReadOnlySpan<byte>(Unsafe.AsPointer(ref value), Unsafe.SizeOf<T>()));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the unmanaged representations of structs of type <typeparamref name="T"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the struct to write.</typeparam>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> instance to write with.</param>
|
||||||
|
/// <param name="span">The values to write.</param>
|
||||||
|
public static unsafe void WriteStructs<T>(this Stream stream, ReadOnlySpan<T> span) where T : unmanaged
|
||||||
|
=> stream.Write(MemoryMarshal.Cast<T, byte>(span));
|
||||||
|
|
||||||
|
// ---- Backports ----
|
||||||
|
#if NETSTANDARD2_0
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the
|
||||||
|
/// position within the stream by the number of bytes read.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> instance to write with.</param>
|
||||||
|
/// <param name="buffer">A region of memory. When this method returns, the contents of this region are replaced
|
||||||
|
/// by the bytes read from the current source.</param>
|
||||||
|
/// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes allocated
|
||||||
|
/// in the buffer if that many bytes are not currently available, or zero (0) if the end of the stream has been
|
||||||
|
/// reached.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This .NET Standard 2.0 backport requires a temporary copy.
|
||||||
|
/// </remarks>
|
||||||
|
public static int Read(this Stream stream, Span<byte> buffer)
|
||||||
|
{
|
||||||
|
byte[] bytes = new byte[buffer.Length];
|
||||||
|
int bytesRead = stream.Read(bytes);
|
||||||
|
bytes.AsSpan(0, bytesRead).CopyTo(buffer);
|
||||||
|
return bytesRead;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the
|
||||||
|
/// current position within this stream by the number of bytes written.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> instance.</param>
|
||||||
|
/// <param name="value">A region of memory. This method copies the contents of this region to the current
|
||||||
|
/// stream.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// This .NET Standard 2.0 backport requires a temporary copy.
|
||||||
|
/// </remarks>
|
||||||
|
public static void Write(this Stream stream, ReadOnlySpan<byte> value) => stream.Write(value.ToArray());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
@ -1,252 +1,254 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.Drawing;
|
||||||
using System.Text;
|
using System.IO;
|
||||||
using Syroot.BinaryData;
|
using System.Text;
|
||||||
using Syroot.Worms.Core;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.Worms.Graphics;
|
||||||
|
using Syroot.Worms.IO;
|
||||||
namespace Syroot.Worms
|
|
||||||
{
|
namespace Syroot.Worms
|
||||||
/// <summary>
|
{
|
||||||
/// Represents a (palettized) graphical image stored in an IMG file, possibly compressed.
|
/// <summary>
|
||||||
/// Used by W2, WA and WWP. S. https://worms2d.info/Image_file.
|
/// Represents a (palletized) graphical image stored in an IMG file, possibly compressed.
|
||||||
/// </summary>
|
/// Used by W2, WA and WWP. S. https://worms2d.info/Image_file.
|
||||||
public class Img : RawBitmap, ILoadableFile, ISaveableFile
|
/// </summary>
|
||||||
{
|
public class Img : RawBitmap, ILoadableFile, ISaveableFile
|
||||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
{
|
||||||
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
private const int _signature = 0x1A474D49; // "IMG", 0x1A
|
|
||||||
|
/// <summary>Magic value identifying the start of IMG data.</summary>
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
public const uint Signature = 0x1A474D49; // "IMG\x1A"
|
||||||
|
|
||||||
/// <summary>
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
/// Initializes a new instance of the <see cref="Img"/> class.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public Img() { }
|
/// Initializes a new instance of the <see cref="Img"/> class.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public Img() { }
|
||||||
/// Initializes a new instance of the <see cref="Img"/> class, loading the data from the given
|
|
||||||
/// <see cref="Stream"/>.
|
/// <summary>
|
||||||
/// </summary>
|
/// Initializes a new instance of the <see cref="Img"/> class, loading the data from the given
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// <see cref="Stream"/>.
|
||||||
public Img(Stream stream) => Load(stream);
|
/// </summary>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
/// <summary>
|
public Img(Stream stream) => Load(stream);
|
||||||
/// Initializes a new instance of the <see cref="Img"/> class, loading the data from the given file.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// Initializes a new instance of the <see cref="Img"/> class, loading the data from the given file.
|
||||||
public Img(string fileName) => Load(fileName);
|
/// </summary>
|
||||||
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
/// <summary>
|
public Img(string fileName) => Load(fileName);
|
||||||
/// Initializes a new instance of the <see cref="Img"/> class, loading the data from the given
|
|
||||||
/// <see cref="Stream"/>. The data block can be aligned to a 4-bte boundary.
|
/// <summary>
|
||||||
/// </summary>
|
/// Initializes a new instance of the <see cref="Img"/> class, loading the data from the given
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// <see cref="Stream"/>. The data block can be aligned to a 4-bte boundary.
|
||||||
/// <param name="alignData"><c>true</c> to align the data array by 4 bytes.</param>
|
/// </summary>
|
||||||
public Img(Stream stream, bool alignData) => Load(stream, alignData);
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
|
/// <param name="alignData"><see langword="true"/> to align the data array by 4 bytes.</param>
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
public Img(Stream stream, bool alignData) => Load(stream, alignData);
|
||||||
|
|
||||||
/// <summary>
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
/// Gets an optional description of the image contents.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public string Description { get; private set; }
|
/// Gets or sets a value indicating data was compressed or should be compressed when saving.
|
||||||
|
/// </summary>
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
public bool Compressed { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads the data from the given <see cref="Stream"/>.
|
/// Gets an optional description of the image contents.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
public string? Description { get; private set; }
|
||||||
public void Load(Stream stream) => Load(stream, false);
|
|
||||||
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
/// <summary>
|
|
||||||
/// Loads the data from the given file.
|
/// <summary>
|
||||||
/// </summary>
|
/// Loads the data from the given <see cref="Stream"/>.
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// </summary>
|
||||||
public void Load(string fileName)
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
{
|
/// <exception cref="IndexOutOfRangeException">Compressed images required more bytes than usually to decompress.
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
/// This error can be ignored, the image has been completely read, and is only caused by a few files.</exception>
|
||||||
Load(stream);
|
public void Load(Stream stream) => Load(stream, false);
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
/// <summary>
|
/// Loads the data from the given file.
|
||||||
/// Loads the data from the given <see cref="Stream"/>. The data block can be aligned to a 4-bte boundary.
|
/// </summary>
|
||||||
/// </summary>
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// <exception cref="IndexOutOfRangeException">Compressed images required more bytes than usually to decompress.
|
||||||
/// <param name="alignData"><c>true</c> to align the data array by 4 bytes.</param>
|
/// This error can be ignored, the image has been completely read, and is only caused by a few files.</exception>
|
||||||
public void Load(Stream stream, bool alignData)
|
public void Load(string fileName)
|
||||||
{
|
{
|
||||||
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
Load(stream);
|
||||||
// Read the header.
|
}
|
||||||
if (reader.ReadInt32() != _signature)
|
|
||||||
throw new InvalidDataException("Invalid IMG file signature.");
|
/// <summary>
|
||||||
int fileSize = reader.ReadInt32();
|
/// Loads the data from the given <see cref="Stream"/>. The data block can be aligned to a 4-bte boundary.
|
||||||
|
/// </summary>
|
||||||
// Read an optional string describing the image contents and the bits per pixel.
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
BitsPerPixel = reader.Read1Byte();
|
/// <param name="alignData"><see langword="true"/> to align the data array by 4 bytes.</param>
|
||||||
if (BitsPerPixel == 0)
|
/// <exception cref="IndexOutOfRangeException">Compressed images required more bytes than usually to decompress.
|
||||||
{
|
/// This error can be ignored, the image has been completely read, and is only caused by a few files.</exception>
|
||||||
Description = String.Empty;
|
public void Load(Stream stream, bool alignData)
|
||||||
BitsPerPixel = reader.Read1Byte();
|
{
|
||||||
}
|
using BinaryStream reader = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
else if (BitsPerPixel > 32)
|
|
||||||
{
|
// Read the header.
|
||||||
Description = (char)BitsPerPixel + reader.ReadString(StringCoding.ZeroTerminated);
|
if (reader.ReadUInt32() != Signature)
|
||||||
BitsPerPixel = reader.Read1Byte();
|
throw new InvalidDataException("Invalid IMG file signature.");
|
||||||
}
|
int fileSize = reader.ReadInt32();
|
||||||
else
|
|
||||||
{
|
// Read an optional string describing the image contents and the bits per pixel.
|
||||||
Description = null;
|
BitsPerPixel = reader.Read1Byte();
|
||||||
}
|
if (BitsPerPixel == 0)
|
||||||
|
{
|
||||||
// Read image flags describing the format and availability of the following contents.
|
Description = String.Empty;
|
||||||
Flags flags = (Flags)reader.ReadByte();
|
BitsPerPixel = reader.Read1Byte();
|
||||||
|
}
|
||||||
// Read the image palette if available. The first color of the palette is implicitly black.
|
else if (BitsPerPixel > 32)
|
||||||
if (flags.HasFlag(Flags.Palettized))
|
{
|
||||||
{
|
Description = (char)BitsPerPixel + reader.ReadString(StringCoding.ZeroTerminated);
|
||||||
int colorCount = reader.ReadInt16();
|
BitsPerPixel = reader.Read1Byte();
|
||||||
Palette = new Color[colorCount + 1];
|
}
|
||||||
Palette[0] = Color.Black;
|
else
|
||||||
for (int i = 1; i <= colorCount; i++)
|
{
|
||||||
Palette[i] = Color.FromArgb(reader.Read1Byte(), reader.Read1Byte(), reader.Read1Byte());
|
Description = null;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
// Read image flags describing the format and availability of the following contents.
|
||||||
Palette = null;
|
Flags flags = (Flags)reader.ReadByte();
|
||||||
}
|
|
||||||
|
// Read the image palette if available. The first color of the palette is implicitly black.
|
||||||
// Read the image size.
|
if (flags.HasFlag(Flags.Palettized))
|
||||||
Size = new Size(reader.ReadInt16(), reader.ReadInt16());
|
{
|
||||||
|
int colorCount = reader.ReadInt16();
|
||||||
// Read the data byte array, which might be compressed or aligned.
|
Palette = new List<Color>(colorCount + 1);
|
||||||
if (alignData)
|
Palette.Add(Color.Black);
|
||||||
reader.Align(4);
|
while (colorCount-- > 0)
|
||||||
byte[] data = new byte[Size.Width * Size.Height * BitsPerPixel / 8];
|
Palette.Add(Color.FromArgb(reader.Read1Byte(), reader.Read1Byte(), reader.Read1Byte()));
|
||||||
if (flags.HasFlag(Flags.Compressed))
|
}
|
||||||
Team17Compression.Decompress(reader.BaseStream, data);
|
else
|
||||||
else
|
{
|
||||||
data = reader.ReadBytes(data.Length);
|
Palette = null;
|
||||||
|
}
|
||||||
Data = data;
|
|
||||||
}
|
// Read the image size.
|
||||||
|
Size = new Size(reader.ReadInt16(), reader.ReadInt16());
|
||||||
/// <summary>
|
|
||||||
/// Loads the data from the given file. The data block can be aligned to a 4-bte boundary.
|
// Read the data byte array, which might be compressed or aligned.
|
||||||
/// </summary>
|
if (alignData)
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
reader.Align(4);
|
||||||
/// <param name="alignData"><c>true</c> to align the data array by 4 bytes.</param>
|
int dataSize = (int)(BitsPerPixel / 8f * Size.Width * Size.Height);
|
||||||
public void Load(string fileName, bool alignData)
|
if (flags.HasFlag(Flags.Compressed))
|
||||||
{
|
{
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
// Some shipped images require up to 3 bytes to not fail when decompressing with out-of-bounds access.
|
||||||
Load(stream, alignData);
|
Data = new byte[dataSize];
|
||||||
}
|
Team17Compression.Decompress(reader.BaseStream, Data);
|
||||||
|
}
|
||||||
/// <summary>
|
else
|
||||||
/// Saves the data into the given <paramref name="stream"/>.
|
{
|
||||||
/// </summary>
|
Data = reader.ReadBytes(dataSize);
|
||||||
/// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
|
}
|
||||||
public void Save(Stream stream) => Save(stream, false, false);
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the optionally compressed data into the given <paramref name="stream"/>.
|
/// Loads the data from the given file. The data block can be aligned to a 4-bte boundary.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
/// <param name="compress"><c>true</c> to compress image data.</param>
|
/// <param name="alignData"><see langword="true"/> to align the data array by 4 bytes.</param>
|
||||||
public void Save(Stream stream, bool compress) => Save(stream, compress, false);
|
/// <exception cref="IndexOutOfRangeException">Compressed images required more bytes than usually to decompress.
|
||||||
|
/// This error can be ignored, the image has been completely read, and is only caused by a few files.</exception>
|
||||||
/// <summary>
|
public void Load(string fileName, bool alignData)
|
||||||
/// Saves the data in the given file.
|
{
|
||||||
/// </summary>
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
/// <param name="fileName">The name of the file to save the data in.</param>
|
Load(stream, alignData);
|
||||||
public void Save(string fileName) => Save(fileName, false, false);
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves the optionally compressed data in the given file.
|
/// Saves the data into the given <paramref name="stream"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fileName">The name of the file to save the data in.</param>
|
/// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
|
||||||
/// <param name="compress"><c>true</c> to compress image data.</param>
|
public void Save(Stream stream) => Save(stream, false);
|
||||||
public void Save(string fileName, bool compress) => Save(fileName, compress, false);
|
|
||||||
|
/// <summary>
|
||||||
/// <summary>
|
/// Saves the data in the given file.
|
||||||
/// Saves the optionally compressed data into the given <paramref name="stream"/>. The data block can be aligned
|
/// </summary>
|
||||||
/// to a 4-bte boundary.
|
/// <param name="fileName">The name of the file to save the data in.</param>
|
||||||
/// </summary>
|
public void Save(string fileName) => Save(fileName, false);
|
||||||
/// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
|
|
||||||
/// <param name="compress"><c>true</c> to compress image data.</param>
|
/// <summary>
|
||||||
/// <param name="alignData"><c>true</c> to align the data array by 4 bytes.</param>
|
/// Saves the data into the given <paramref name="stream"/>. The data block can be aligned to a 4-byte boundary.
|
||||||
public void Save(Stream stream, bool compress, bool alignData)
|
/// </summary>
|
||||||
{
|
/// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
|
||||||
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
/// <param name="alignData"><see langword="true"/> to align the data array by 4 bytes.</param>
|
||||||
|
public void Save(Stream stream, bool alignData)
|
||||||
// Write the header.
|
{
|
||||||
writer.Write(_signature);
|
using BinaryStream writer = new BinaryStream(stream, encoding: Encoding.ASCII, leaveOpen: true);
|
||||||
uint fileSizeOffset = writer.ReserveOffset();
|
|
||||||
|
// Write the header.
|
||||||
// Write an optional string describing the image contents and the bits per pixel.
|
writer.Write(Signature);
|
||||||
if (Description != null)
|
uint fileSizeOffset = writer.ReserveOffset();
|
||||||
writer.Write(Description, StringCoding.ZeroTerminated);
|
|
||||||
writer.Write(BitsPerPixel);
|
// Write an optional string describing the image contents and the bits per pixel.
|
||||||
|
if (Description != null)
|
||||||
// Write image flags describing the format and availability of the following contents.
|
writer.Write(Description, StringCoding.ZeroTerminated);
|
||||||
Flags flags = Flags.None;
|
writer.Write(BitsPerPixel);
|
||||||
if (Palette != null)
|
|
||||||
flags |= Flags.Palettized;
|
// Write image flags describing the format and availability of the following contents.
|
||||||
if (compress)
|
Flags flags = Flags.None;
|
||||||
flags |= Flags.Compressed;
|
if (Palette != null)
|
||||||
writer.WriteEnum(flags, true);
|
flags |= Flags.Palettized;
|
||||||
|
if (Compressed)
|
||||||
// Write the image palette if available. The first color of the palette is implicitly black.
|
flags |= Flags.Compressed;
|
||||||
if (Palette != null)
|
writer.WriteEnum(flags, true);
|
||||||
{
|
|
||||||
writer.Write((short)(Palette.Count - 1));
|
// Write the image palette if available. The first color of the palette is implicitly black.
|
||||||
for (int i = 1; i < Palette.Count; i++)
|
if (Palette != null)
|
||||||
{
|
{
|
||||||
Color color = Palette[i];
|
writer.Write((short)(Palette.Count - 1));
|
||||||
writer.Write(color.R);
|
for (int i = 1; i < Palette.Count; i++)
|
||||||
writer.Write(color.G);
|
{
|
||||||
writer.Write(color.B);
|
Color color = Palette[i];
|
||||||
}
|
writer.Write(color.R);
|
||||||
}
|
writer.Write(color.G);
|
||||||
|
writer.Write(color.B);
|
||||||
// Write the image size.
|
}
|
||||||
writer.Write((short)Size.Width);
|
}
|
||||||
writer.Write((short)Size.Height);
|
|
||||||
|
// Write the image size.
|
||||||
// Write the data byte array, which might be compressed or aligned.
|
writer.Write((short)Size.Width);
|
||||||
if (alignData)
|
writer.Write((short)Size.Height);
|
||||||
writer.Align(4);
|
|
||||||
byte[] data = Data;
|
// Write the data byte array, which might be compressed or aligned.
|
||||||
if (compress)
|
if (alignData)
|
||||||
data = Team17Compression.Compress(data);
|
writer.Align(4);
|
||||||
writer.Write(data);
|
byte[] data = Data;
|
||||||
|
if (Compressed)
|
||||||
writer.SatisfyOffset(fileSizeOffset, (int)writer.Position);
|
data = Team17Compression.Compress(data);
|
||||||
}
|
writer.Write(data);
|
||||||
|
|
||||||
/// <summary>
|
writer.SatisfyOffset(fileSizeOffset, (uint)writer.Position);
|
||||||
/// Saves the optionally compressed data in the given file. The data block can be aligned to a 4-byte boundary.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <param name="fileName">The name of the file to save the data in.</param>
|
/// <summary>
|
||||||
/// <param name="compress"><c>true</c> to compress image data.</param>
|
/// Saves the data in the given file. The data block can be aligned to a 4-byte boundary.
|
||||||
/// <param name="alignData"><c>true</c> to align the data array by 4 bytes.</param>
|
/// </summary>
|
||||||
public void Save(string fileName, bool compress, bool alignData)
|
/// <param name="fileName">The name of the file to save the data in.</param>
|
||||||
{
|
/// <param name="alignData"><see langword="true"/> to align the data array by 4 bytes.</param>
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
public void Save(string fileName, bool alignData)
|
||||||
Save(stream, compress, alignData);
|
{
|
||||||
}
|
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
|
Save(stream, alignData);
|
||||||
// ---- ENUMERATIONS -------------------------------------------------------------------------------------------
|
}
|
||||||
|
|
||||||
[Flags]
|
// ---- ENUMERATIONS -------------------------------------------------------------------------------------------
|
||||||
private enum Flags : byte
|
|
||||||
{
|
[Flags]
|
||||||
None = 0,
|
private enum Flags : byte
|
||||||
Compressed = 1 << 6,
|
{
|
||||||
Palettized = 1 << 7
|
None,
|
||||||
}
|
Compressed = 1 << 6,
|
||||||
}
|
Palettized = 1 << 7
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
namespace Syroot.Worms
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents the map terrain configuration found in LEV and BIT files.
|
|
||||||
/// </summary>
|
|
||||||
public class LevelInfo
|
|
||||||
{
|
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
public uint LandSeed { get; set; }
|
|
||||||
|
|
||||||
public uint ObjectSeed { get; set; }
|
|
||||||
|
|
||||||
public bool Cavern { get; set; }
|
|
||||||
|
|
||||||
public LandStyle Style { get; set; }
|
|
||||||
|
|
||||||
public bool NoBorder { get; set; }
|
|
||||||
|
|
||||||
public int ObjectPercent { get; set; }
|
|
||||||
|
|
||||||
public int BridgePercent { get; set; }
|
|
||||||
|
|
||||||
public int WaterLevel { get; set; }
|
|
||||||
|
|
||||||
public int SoilIndex { get; set; }
|
|
||||||
|
|
||||||
public int WaterColor { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum LandStyle : int
|
|
||||||
{
|
|
||||||
SingleIsland,
|
|
||||||
DoubleIsland,
|
|
||||||
SingleSmallIsland,
|
|
||||||
DoubleSmallIsland,
|
|
||||||
Cavern,
|
|
||||||
SingleTunnel,
|
|
||||||
CavernWater,
|
|
||||||
DoubleTunnel
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,159 +1,161 @@
|
|||||||
using System.Drawing;
|
using System;
|
||||||
using System.IO;
|
using System.Collections.Generic;
|
||||||
using Syroot.BinaryData;
|
using System.Drawing;
|
||||||
using Syroot.Worms.Core.Graphics;
|
using System.IO;
|
||||||
using Syroot.Worms.Core.IO;
|
using Syroot.BinaryData;
|
||||||
using Syroot.Worms.Core.Riff;
|
using Syroot.Worms.Core.Riff;
|
||||||
|
using Syroot.Worms.Graphics;
|
||||||
namespace Syroot.Worms
|
using Syroot.Worms.IO;
|
||||||
{
|
|
||||||
/// <summary>
|
namespace Syroot.Worms
|
||||||
/// Represents a color palette stored in PAL files, following the RIFF format. It is used to index colors in images.
|
{
|
||||||
/// Used by WA and WWP. S. http://worms2d.info/Palette_file.
|
/// <summary>
|
||||||
/// </summary>
|
/// Represents a color palette stored in PAL files, following the RIFF format. It is used to index colors in images.
|
||||||
[RiffFile("PAL ")]
|
/// Used by WA and WWP. S. http://worms2d.info/Palette_file.
|
||||||
public class RiffPalette : RiffFile, ILoadableFile, ISaveableFile, IPalette
|
/// </summary>
|
||||||
{
|
[RiffFile("PAL ")]
|
||||||
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
public class RiffPalette : RiffFile, ILoadableFile, ISaveableFile, IPalette
|
||||||
|
{
|
||||||
private const short _version = 0x0300;
|
// ---- CONSTANTS ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
private const short _version = 0x0300;
|
||||||
|
|
||||||
/// <summary>
|
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
|
||||||
/// Initializes a new instance of the <see cref="RiffPalette"/> class.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public RiffPalette() => Version = _version;
|
/// Initializes a new instance of the <see cref="RiffPalette"/> class.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public RiffPalette() { }
|
||||||
/// Initializes a new instance of the <see cref="RiffPalette"/> class, loading the data from the given
|
|
||||||
/// <see cref="Stream"/>.
|
/// <summary>
|
||||||
/// </summary>
|
/// Initializes a new instance of the <see cref="RiffPalette"/> class, loading the data from the given
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// <see cref="Stream"/>.
|
||||||
public RiffPalette(Stream stream) => Load(stream);
|
/// </summary>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
/// <summary>
|
public RiffPalette(Stream stream) => Load(stream);
|
||||||
/// Initializes a new instance of the <see cref="RiffPalette"/> class, loading the data from the given file.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// Initializes a new instance of the <see cref="RiffPalette"/> class, loading the data from the given file.
|
||||||
public RiffPalette(string fileName) => Load(fileName);
|
/// </summary>
|
||||||
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
public RiffPalette(string fileName) => Load(fileName);
|
||||||
|
|
||||||
/// <summary>
|
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
|
||||||
/// Gets the version of the palette data.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public int Version { get; set; }
|
/// Gets the version of the palette data.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public int Version { get; set; } = _version;
|
||||||
/// Gets the <see cref="Color"/> instances stored in this palette.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public Color[] Colors { get; set; }
|
/// Gets or sets the <see cref="Color"/> instances stored in this palette.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public IList<Color> Colors { get; set; } = new List<Color>();
|
||||||
/// Gets the unknown data in the offl chunk.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public byte[] OfflData { get; set; }
|
/// Gets the unknown data in the offl chunk.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public byte[] OfflData { get; set; } = Array.Empty<byte>();
|
||||||
/// Gets the unknown data in the tran chunk.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public byte[] TranData { get; set; }
|
/// Gets the unknown data in the tran chunk.
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public byte[] TranData { get; set; } = Array.Empty<byte>();
|
||||||
/// Gets the unknown data in the unde chunk.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public byte[] UndeData { get; set; }
|
/// Gets the unknown data in the unde chunk.
|
||||||
|
/// </summary>
|
||||||
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
public byte[] UndeData { get; set; } = Array.Empty<byte>();
|
||||||
|
|
||||||
/// <summary>
|
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
|
||||||
/// Loads the data from the given <see cref="Stream"/>.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
/// Loads the data from the given <see cref="Stream"/>.
|
||||||
public void Load(Stream stream) => LoadRiff(stream);
|
/// </summary>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> to load the data from.</param>
|
||||||
/// <summary>
|
public void Load(Stream stream) => LoadRiff(stream);
|
||||||
/// Loads the data from the given file.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
/// <param name="fileName">The name of the file to load the data from.</param>
|
/// Loads the data from the given file.
|
||||||
public void Load(string fileName)
|
/// </summary>
|
||||||
{
|
/// <param name="fileName">The name of the file to load the data from.</param>
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
public void Load(string fileName)
|
||||||
Load(stream);
|
{
|
||||||
}
|
using FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
|
Load(stream);
|
||||||
/// <summary>
|
}
|
||||||
/// Saves the data into the given <paramref name="stream"/>.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
/// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
|
/// Saves the data into the given <paramref name="stream"/>.
|
||||||
public void Save(Stream stream) => SaveRiff(stream);
|
/// </summary>
|
||||||
|
/// <param name="stream">The <see cref="Stream"/> to save the data to.</param>
|
||||||
/// <summary>
|
public void Save(Stream stream) => SaveRiff(stream);
|
||||||
/// Saves the data in the given file.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
/// <param name="fileName">The name of the file to save the data in.</param>
|
/// Saves the data in the given file.
|
||||||
public void Save(string fileName)
|
/// </summary>
|
||||||
{
|
/// <param name="fileName">The name of the file to save the data in.</param>
|
||||||
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
public void Save(string fileName)
|
||||||
Save(stream);
|
{
|
||||||
}
|
using FileStream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
|
Save(stream);
|
||||||
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
}
|
||||||
|
|
||||||
|
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
#pragma warning disable IDE0051 // Remove unused private members
|
#pragma warning disable IDE0051 // Remove unused private members
|
||||||
[RiffChunkLoad("data")]
|
[RiffChunkLoad("data")]
|
||||||
private void LoadDataChunk(BinaryStream reader, int length)
|
private void LoadDataChunk(BinaryStream reader, int _)
|
||||||
{
|
{
|
||||||
// Read the PAL version.
|
// Read the PAL version.
|
||||||
Version = reader.ReadInt16();
|
Version = reader.ReadInt16();
|
||||||
if (Version != _version)
|
if (Version != _version)
|
||||||
throw new InvalidDataException("Unknown PAL version.");
|
throw new InvalidDataException("Unknown PAL version.");
|
||||||
|
|
||||||
// Read the colors.
|
// Read the colors.
|
||||||
Colors = new Color[reader.ReadInt16()];
|
short colorCount = reader.ReadInt16();
|
||||||
for (int i = 0; i < Colors.Length; i++)
|
Colors = new List<Color>();
|
||||||
{
|
while (colorCount-- > 0)
|
||||||
Colors[i] = Color.FromArgb(reader.Read1Byte(), reader.Read1Byte(), reader.Read1Byte());
|
{
|
||||||
_ = reader.ReadByte(); // Dismiss alpha, as it is not used in WA.
|
Colors.Add(Color.FromArgb(reader.Read1Byte(), reader.Read1Byte(), reader.Read1Byte()));
|
||||||
}
|
_ = reader.ReadByte(); // Dismiss alpha, as it is not used in WA.
|
||||||
}
|
}
|
||||||
|
}
|
||||||
[RiffChunkLoad("offl")]
|
|
||||||
private void LoadOfflChunk(BinaryStream reader, int length) => OfflData = reader.ReadBytes(length);
|
[RiffChunkLoad("offl")]
|
||||||
|
private void LoadOfflChunk(BinaryStream reader, int length) => OfflData = reader.ReadBytes(length);
|
||||||
[RiffChunkLoad("tran")]
|
|
||||||
private void LoadTranChunk(BinaryStream reader, int length) => TranData = reader.ReadBytes(length);
|
[RiffChunkLoad("tran")]
|
||||||
|
private void LoadTranChunk(BinaryStream reader, int length) => TranData = reader.ReadBytes(length);
|
||||||
[RiffChunkLoad("unde")]
|
|
||||||
private void LoadUndeChunk(BinaryStream reader, int length) => UndeData = reader.ReadBytes(length);
|
[RiffChunkLoad("unde")]
|
||||||
|
private void LoadUndeChunk(BinaryStream reader, int length) => UndeData = reader.ReadBytes(length);
|
||||||
[RiffChunkSave("data")]
|
|
||||||
private void SaveDataChunk(BinaryStream writer)
|
[RiffChunkSave("data")]
|
||||||
{
|
private void SaveDataChunk(BinaryStream writer)
|
||||||
// Write the PAL version.
|
{
|
||||||
writer.Write(_version);
|
// Write the PAL version.
|
||||||
|
writer.Write(_version);
|
||||||
// Write the colors.
|
|
||||||
writer.Write((short)Colors.Length);
|
// Write the colors.
|
||||||
for (int i = 0; i < Colors.Length; i++)
|
writer.Write((short)Colors.Count);
|
||||||
{
|
foreach (Color color in Colors)
|
||||||
Color color = Colors[i];
|
{
|
||||||
writer.Write(color.R);
|
writer.Write(color.R);
|
||||||
writer.Write(color.G);
|
writer.Write(color.G);
|
||||||
writer.Write(color.B);
|
writer.Write(color.B);
|
||||||
writer.Write((byte)0); // Dismiss alpha, as it is not used in WA.
|
writer.Write((byte)0); // Dismiss alpha, as it is not used in WA.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[RiffChunkSave("offl")]
|
[RiffChunkSave("offl")]
|
||||||
private void SaveOfflChunk(BinaryStream writer) => writer.Write(OfflData);
|
private void SaveOfflChunk(BinaryStream writer) => writer.Write(OfflData);
|
||||||
|
|
||||||
[RiffChunkSave("tran")]
|
[RiffChunkSave("tran")]
|
||||||
private void SaveTranChunk(BinaryStream writer) => writer.Write(TranData);
|
private void SaveTranChunk(BinaryStream writer) => writer.Write(TranData);
|
||||||
|
|
||||||
[RiffChunkSave("unde")]
|
[RiffChunkSave("unde")]
|
||||||
private void SaveUndeChunk(BinaryStream writer) => writer.Write(UndeData);
|
private void SaveUndeChunk(BinaryStream writer) => writer.Write(UndeData);
|
||||||
#pragma warning restore IDE0051
|
#pragma warning restore IDE0051
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<AssemblyName>Syroot.Worms</AssemblyName>
|
<AssemblyName>Syroot.Worms</AssemblyName>
|
||||||
<Description>.NET library for loading and modifying files of Team17 Worms games.</Description>
|
<Description>.NET library for loading and modifying files of Team17 Worms games.</Description>
|
||||||
<PackageReleaseNotes>Fix issues when loading and saving some formats.</PackageReleaseNotes>
|
<PackageReleaseNotes>Overhaul implementation and documentation.</PackageReleaseNotes>
|
||||||
<Version>3.2.0</Version>
|
<Version>4.0.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Syroot.BinaryData.Serialization" Version="5.2.0" />
|
<PackageReference Include="Syroot.BinaryData.Serialization" Version="5.2.0" />
|
||||||
|
26
src/test.xml
Normal file
26
src/test.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<!-- Compilation -->
|
||||||
|
<PropertyGroup>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<TargetFramework>netcoreapp3</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- References -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||||
|
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
|
||||||
|
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Files Linking -->
|
||||||
|
<Target Name="FilesRemove" AfterTargets="Build;Clean">
|
||||||
|
<Message Text="Removing Files" Importance="high" />
|
||||||
|
<Exec Command="RD "$(OutputPath)Files" /S/Q" />
|
||||||
|
</Target>
|
||||||
|
<Target Name="FilesCreate" AfterTargets="Build">
|
||||||
|
<Message Text="Linking Files" Importance="high" />
|
||||||
|
<Exec Command="MKLINK /D "$(OutputPath)Files" "$(ProjectDir)Files"" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/(_twicken_weapons_).pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/(_twicken_weapons_).pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/0h so high 2.0.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/0h so high 2.0.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_102917.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_102917.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1180343.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1180343.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1281323.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1281323.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1341540.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1341540.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1360588.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1360588.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1423955.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1423955.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1432545.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1432545.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1497131.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1497131.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1518228.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1518228.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1608557.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1608557.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1675572.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1675572.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1696867.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1696867.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1764764.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1764764.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_194940.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_194940.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1967721.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1967721.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1974050.pxl
(Stored with Git LFS)
Normal file
BIN
src/test/Syroot.Worms.Armageddon.ProjectX.Test/Files/Libraries/Cache/cached_1974050.pxl
(Stored with Git LFS)
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user