mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/PCK-Studio.git
synced 2026-07-13 17:08:11 +00:00
Move GRF processing to OMI lib
This commit is contained in:
@@ -1,283 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace PckStudio.Classes.FileTypes
|
||||
{
|
||||
public class GRFFile
|
||||
{
|
||||
public static readonly string[] ValidGameRules = new string[]
|
||||
{
|
||||
"MapOptions",
|
||||
"ApplySchematic",
|
||||
"GenerateStructure",
|
||||
"GenerateBox",
|
||||
"PlaceBlock",
|
||||
"PlaceContainer",
|
||||
"PlaceSpawner",
|
||||
"BiomeOverride",
|
||||
"StartFeature",
|
||||
"AddItem",
|
||||
"AddEnchantment",
|
||||
"WeighedTreasureItem",
|
||||
"RandomItemSet",
|
||||
"DistributeItems",
|
||||
"WorldPosition",
|
||||
"LevelRules",
|
||||
"NamedArea",
|
||||
"ActiveChunkArea",
|
||||
"TargetArea",
|
||||
"ScoreRing",
|
||||
"ThermalArea",
|
||||
"PlayerBoundsVolume",
|
||||
"Killbox",
|
||||
"BlockLayer",
|
||||
"UseBlock",
|
||||
"CollectItem",
|
||||
"CompleteAll",
|
||||
"UpdatePlayer",
|
||||
"OnGameStartSpawnPositions",
|
||||
"OnInitialiseWorld",
|
||||
"SpawnPositionSet",
|
||||
"PopulateContainer",
|
||||
"DegradationSequence",
|
||||
"RandomDissolveDegrade",
|
||||
"DirectionalDegrade",
|
||||
"GrantPermissions",
|
||||
"AllowIn",
|
||||
"LayerGeneration",
|
||||
"LayerAsset",
|
||||
"AnyCombinationOf",
|
||||
"CombinationDefinition",
|
||||
"Variations",
|
||||
"BlockDef",
|
||||
"LayerSize",
|
||||
"UniformSize",
|
||||
"RandomizeSize",
|
||||
"LinearBlendSize",
|
||||
"LayerShape",
|
||||
"BasicShape",
|
||||
"StarShape",
|
||||
"PatchyShape",
|
||||
"RingShape",
|
||||
"SpiralShape",
|
||||
"LayerFill",
|
||||
"BasicLayerFill",
|
||||
"CurvedLayerFill",
|
||||
"WarpedLayerFill",
|
||||
"LayerTheme",
|
||||
"NullTheme",
|
||||
"FilterTheme",
|
||||
"ShaftsTheme",
|
||||
"BasicPatchesTheme",
|
||||
"BlockStackTheme",
|
||||
"RainbowTheme",
|
||||
"TerracottaTheme",
|
||||
"FunctionPatchesTheme",
|
||||
"SimplePatchesTheme",
|
||||
"CarpetTrapTheme",
|
||||
"MushroomBlockTheme",
|
||||
"TextureTheme",
|
||||
"SchematicTheme",
|
||||
"BlockCollisionException",
|
||||
"Powerup",
|
||||
"Checkpoint",
|
||||
"CustomBeacon",
|
||||
"ActiveViewArea",
|
||||
};
|
||||
|
||||
public readonly GameRule Root = null;
|
||||
|
||||
public int Crc => _crc;
|
||||
public bool IsWorld => _isWorld;
|
||||
public CompressionType CompressionLevel => _compressionLevel;
|
||||
|
||||
private int _crc = 0;
|
||||
private bool _isWorld = false;
|
||||
private CompressionType _compressionLevel = CompressionType.None;
|
||||
|
||||
public enum CompressionType : byte
|
||||
{
|
||||
None = 0,
|
||||
Compressed = 1,
|
||||
CompressedRle = 2,
|
||||
CompressedRleCrc = 3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new GRFFile as a non-world grf file
|
||||
/// </summary>
|
||||
public GRFFile() : this(-1, false)
|
||||
{}
|
||||
|
||||
public GRFFile(int crc, bool isWolrd) : this(crc, isWolrd, CompressionType.None)
|
||||
{}
|
||||
public GRFFile(int crc, bool isWolrd, CompressionType compressionLevel)
|
||||
{
|
||||
Root = new GameRule("__ROOT__", null);
|
||||
_compressionLevel = compressionLevel;
|
||||
_crc = crc;
|
||||
_isWorld = isWolrd;
|
||||
}
|
||||
|
||||
public class GameRule
|
||||
{
|
||||
/// <summary> Contains all valid Parameter names </summary>
|
||||
public static readonly string[] ValidParameters = new string[]
|
||||
{
|
||||
"plus_x",
|
||||
"minus_x",
|
||||
"plus_z",
|
||||
"minus_z",
|
||||
"omni_plus_x",
|
||||
"omni_minus_x",
|
||||
"omni_plus_z",
|
||||
"omni_minus_z",
|
||||
"none",
|
||||
"plus_y",
|
||||
"minus_y",
|
||||
"plus_x",
|
||||
"minus_x",
|
||||
"plus_z",
|
||||
"minus_z",
|
||||
"descriptionName",
|
||||
"promptName",
|
||||
"dataTag",
|
||||
"enchantmentId",
|
||||
"enchantmentLevel",
|
||||
"itemId",
|
||||
"quantity",
|
||||
"auxValue",
|
||||
"slot",
|
||||
"name",
|
||||
"food",
|
||||
"health",
|
||||
"blockId",
|
||||
"useCoords",
|
||||
"seed",
|
||||
"flatworld",
|
||||
"filename",
|
||||
"rot",
|
||||
"data",
|
||||
"block",
|
||||
"entity",
|
||||
"facing",
|
||||
"edgeBlock",
|
||||
"fillBlock",
|
||||
"skipAir",
|
||||
"x",
|
||||
"x0",
|
||||
"x1",
|
||||
"y",
|
||||
"y0",
|
||||
"y1",
|
||||
"z",
|
||||
"z0",
|
||||
"z1",
|
||||
"chunkX",
|
||||
"chunkZ",
|
||||
"yRot",
|
||||
"xRot",
|
||||
"spawnX",
|
||||
"spawnY",
|
||||
"spawnZ",
|
||||
"orientation",
|
||||
"dimension",
|
||||
"topblockId",
|
||||
"biomeId",
|
||||
"feature",
|
||||
"minCount",
|
||||
"maxCount",
|
||||
"weight",
|
||||
"id",
|
||||
"probability",
|
||||
"method",
|
||||
"hasBeenInCreative",
|
||||
"cloudHeight",
|
||||
"fogDistance",
|
||||
"dayTime",
|
||||
"target",
|
||||
"speed",
|
||||
"dir",
|
||||
"type",
|
||||
"pass",
|
||||
"for",
|
||||
"random",
|
||||
"blockAux",
|
||||
"size",
|
||||
"scale",
|
||||
"freq",
|
||||
"func",
|
||||
"upper",
|
||||
"lower",
|
||||
"dY",
|
||||
"thickness",
|
||||
"points",
|
||||
"holeSize",
|
||||
"variant",
|
||||
"startHeight",
|
||||
"pattern",
|
||||
"colour",
|
||||
"primary",
|
||||
"laps",
|
||||
"liftForceModifier",
|
||||
"staticLift",
|
||||
"targetHeight",
|
||||
"speedBoost",
|
||||
"boostDirection",
|
||||
"condition_type",
|
||||
"condition_value_0",
|
||||
"condition_value_1",
|
||||
"beam_length",
|
||||
};
|
||||
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
public GameRule Parent { get; } = null;
|
||||
public Dictionary<string, string> Parameters { get; } = new Dictionary<string, string>();
|
||||
public List<GameRule> ChildRules { get; } = new List<GameRule>();
|
||||
|
||||
public GameRule(string name, GameRule parent)
|
||||
{
|
||||
Name = name;
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
public GameRule AddRule(string gameRuleName) => AddRule(gameRuleName, false);
|
||||
|
||||
/// <summary>Adds a new gamerule</summary>
|
||||
/// <param name="gameRuleName">Game rule to add</param>
|
||||
/// <param name="validate">Wether to check the given game rule</param>
|
||||
/// <returns>The Added GRFTag</returns>
|
||||
public GameRule AddRule(string gameRuleName, bool validate)
|
||||
{
|
||||
if (validate && !ValidGameRules.Contains(gameRuleName)) return null;
|
||||
var tag = new GameRule(gameRuleName, this);
|
||||
ChildRules.Add(tag);
|
||||
return tag;
|
||||
}
|
||||
|
||||
public GameRule AddRule(string gameRuleName, params KeyValuePair<string,string>[] parameters)
|
||||
{
|
||||
var tag = AddRule(gameRuleName); // should never return null unless its called with the validate bool set to true
|
||||
foreach(var param in parameters)
|
||||
{
|
||||
tag.Parameters[param.Key] = param.Value;
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddGameRules(IEnumerable<GameRule> gameRules) => Root.ChildRules.AddRange(gameRules);
|
||||
|
||||
public GameRule AddRule(string gameRuleName)
|
||||
=> AddRule(gameRuleName, false);
|
||||
|
||||
public GameRule AddRule(string gameRuleName, bool validate)
|
||||
=> Root.AddRule(gameRuleName, validate);
|
||||
|
||||
public GameRule AddRule(string gameRuleName, params KeyValuePair<string, string>[] parameters)
|
||||
=> Root.AddRule(gameRuleName, parameters);
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
using PckStudio.Classes.FileTypes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
||||
using PckStudio.Classes.Utils;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace PckStudio.Classes.IO.GRF
|
||||
{
|
||||
internal class GRFFileReader : StreamDataReader<GRFFile>
|
||||
{
|
||||
private IList<string> StringLookUpTable;
|
||||
private GRFFile _file;
|
||||
|
||||
public static GRFFile Read(Stream stream)
|
||||
{
|
||||
return new GRFFileReader().ReadFromStream(stream);
|
||||
}
|
||||
|
||||
private GRFFileReader() : base(false)
|
||||
{ }
|
||||
|
||||
protected override GRFFile ReadFromStream(Stream stream)
|
||||
{
|
||||
stream = ReadHeader(stream);
|
||||
ReadBody(stream);
|
||||
return _file;
|
||||
}
|
||||
|
||||
private Stream ReadHeader(Stream stream)
|
||||
{
|
||||
int x = ReadShort(stream);
|
||||
if (((x >> 31) | x) == 0)
|
||||
{
|
||||
// 14 bools ?...
|
||||
ReadBytes(stream, 14);
|
||||
return stream;
|
||||
}
|
||||
|
||||
GRFFile.CompressionType compression_type = (GRFFile.CompressionType)stream.ReadByte();
|
||||
int crc = ReadInt(stream);
|
||||
int byte1 = stream.ReadByte();
|
||||
int byte2 = stream.ReadByte();
|
||||
int byte3 = stream.ReadByte();
|
||||
int byte4 = stream.ReadByte();
|
||||
if (byte4 > 0)
|
||||
{
|
||||
compression_type = (GRFFile.CompressionType)byte4;
|
||||
}
|
||||
_file = new GRFFile(crc, byte4 > 0, compression_type);
|
||||
|
||||
if (compression_type == GRFFile.CompressionType.None && byte4 == 0)
|
||||
return stream;
|
||||
|
||||
int buf_size = ReadInt(stream);
|
||||
var new_stream = stream;
|
||||
if (byte4 != 0)
|
||||
{
|
||||
new_stream = new MemoryStream(ReadBytes(stream, buf_size));
|
||||
buf_size = ReadInt(new_stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadInt(stream); // ignored cuz rest of data is compressed
|
||||
}
|
||||
var decompressed_stream = DecompressZLX(new_stream);
|
||||
new_stream.Dispose();
|
||||
if (compression_type > GRFFile.CompressionType.Compressed)
|
||||
{
|
||||
byte[] data = ReadBytes(decompressed_stream, buf_size);
|
||||
byte[] decoded_data = RLE<byte>.Decode(data).ToArray();
|
||||
decompressed_stream.Dispose();
|
||||
decompressed_stream = new MemoryStream(decoded_data);
|
||||
}
|
||||
|
||||
if (byte4 != 0)
|
||||
ReadBytes(decompressed_stream, 23);
|
||||
|
||||
return decompressed_stream;
|
||||
}
|
||||
|
||||
private void ReadBody(Stream stream)
|
||||
{
|
||||
ReadStringLookUpTable(stream);
|
||||
string Name = GetString(stream);
|
||||
Debug.WriteLine(string.Format("Root Name: {0}", Name), category: nameof(GRFFile));
|
||||
ReadGameRuleHierarchy(stream, _file.Root);
|
||||
}
|
||||
|
||||
private Stream DecompressZLX(Stream compressedStream)
|
||||
{
|
||||
Stream outputstream = new MemoryStream();
|
||||
using (var inputStream = new InflaterInputStream(compressedStream))
|
||||
{
|
||||
inputStream.IsStreamOwner = false;
|
||||
inputStream.CopyTo(outputstream);
|
||||
outputstream.Position = 0;
|
||||
};
|
||||
return outputstream;
|
||||
}
|
||||
|
||||
private void ReadStringLookUpTable(Stream stream)
|
||||
{
|
||||
int tableSize = ReadInt(stream);
|
||||
StringLookUpTable = new List<string>(tableSize);
|
||||
for (int i = 0; i < tableSize; i++)
|
||||
{
|
||||
string s = ReadString(stream);
|
||||
StringLookUpTable.Add(s);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadGameRuleHierarchy(Stream stream, GRFFile.GameRule parentRule)
|
||||
{
|
||||
_ = parentRule ?? throw new ArgumentNullException(nameof(parentRule));
|
||||
int count = ReadInt(stream);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
(string Name, int Count) parameter = (GetString(stream), ReadInt(stream));
|
||||
var rule = parentRule.AddRule(parameter.Name);
|
||||
for (int j = 0; j < parameter.Count; j++)
|
||||
{
|
||||
rule.Parameters.Add(GetString(stream), ReadString(stream));
|
||||
}
|
||||
ReadGameRuleHierarchy(stream, rule);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetString(Stream stream) => StringLookUpTable[ReadInt(stream)];
|
||||
|
||||
private string ReadString(Stream stream)
|
||||
{
|
||||
short stringLength = ReadShort(stream);
|
||||
return ReadString(stream, stringLength, Encoding.ASCII);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
using PckStudio.Classes.FileTypes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
||||
using PckStudio.Classes.Utils.grf;
|
||||
using PckStudio.Classes.Utils;
|
||||
|
||||
namespace PckStudio.Classes.IO.GRF
|
||||
{
|
||||
internal class GRFFileWriter : StreamDataWriter
|
||||
{
|
||||
private readonly GRFFile _grfFile;
|
||||
private List<string> StringLookUpTable;
|
||||
|
||||
private GRFFile.CompressionType _compressionType;
|
||||
|
||||
public static void Write(in Stream stream, GRFFile grfFile, GRFFile.CompressionType compressionType)
|
||||
{
|
||||
new GRFFileWriter(grfFile, compressionType).WriteToStream(stream);
|
||||
}
|
||||
|
||||
private GRFFileWriter(GRFFile grfFile, GRFFile.CompressionType compressionType) : base(false)
|
||||
{
|
||||
_compressionType = compressionType;
|
||||
if (grfFile.IsWorld)
|
||||
throw new NotImplementedException("World grf saving is currently unsupported");
|
||||
_grfFile = grfFile;
|
||||
StringLookUpTable = new List<string>();
|
||||
PrepareLookUpTable(_grfFile.Root, StringLookUpTable);
|
||||
}
|
||||
|
||||
private void PrepareLookUpTable(GRFFile.GameRule rule, List<string> LUT)
|
||||
{
|
||||
if (!LUT.Contains(rule.Name)) LUT.Add(rule.Name);
|
||||
rule.ChildRules.ForEach(subRule => PrepareLookUpTable(subRule, LUT));
|
||||
foreach (var param in rule.Parameters)
|
||||
if (!LUT.Contains(param.Key)) LUT.Add(param.Key);
|
||||
}
|
||||
|
||||
protected override void WriteToStream(Stream stream)
|
||||
{
|
||||
WriteHeader(stream);
|
||||
using (var uncompressed_stream = new MemoryStream())
|
||||
{
|
||||
WriteBody(uncompressed_stream);
|
||||
HandleCompression(stream, uncompressed_stream);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCompression(Stream destinationStream, MemoryStream sourceStream)
|
||||
{
|
||||
byte[] _buffer = sourceStream.ToArray();
|
||||
int _original_length = _buffer.Length;
|
||||
|
||||
if (_compressionType >= GRFFile.CompressionType.CompressedRle)
|
||||
_buffer = CompressRle(_buffer);
|
||||
if (_compressionType >= GRFFile.CompressionType.Compressed)
|
||||
{
|
||||
_buffer = CompressZib(_buffer);
|
||||
WriteInt(destinationStream, _original_length);
|
||||
WriteInt(destinationStream, _buffer.Length);
|
||||
}
|
||||
if (_compressionType >= GRFFile.CompressionType.CompressedRleCrc)
|
||||
MakeAndWriteCrc(destinationStream, _buffer);
|
||||
WriteBytes(destinationStream, _buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
private byte[] CompressZib(byte[] buffer)
|
||||
{
|
||||
byte[] result;
|
||||
var outputStream = new MemoryStream(); // Stream gets Disposed in DeflaterOutputStream
|
||||
using (var deflateStream = new DeflaterOutputStream(outputStream))
|
||||
{
|
||||
WriteBytes(deflateStream, buffer);
|
||||
deflateStream.Flush();
|
||||
deflateStream.Finish();
|
||||
outputStream.Position = 0;
|
||||
result = outputStream.ToArray();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte[] CompressRle(byte[] buffer) => RLE<byte>.Encode(buffer).ToArray();
|
||||
|
||||
private void MakeAndWriteCrc(Stream stream, byte[] data)
|
||||
{
|
||||
uint crc = CRC32.CRC(data);
|
||||
if (crc != _grfFile.Crc) // no writting needed if there is no change
|
||||
{
|
||||
stream.Position = 3;
|
||||
WriteUInt(stream, crc);
|
||||
stream.Seek(0, SeekOrigin.End); // reset to the end of the stream
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteHeader(Stream stream)
|
||||
{
|
||||
WriteShort(stream, 1);
|
||||
if (_compressionType < GRFFile.CompressionType.None ||
|
||||
_compressionType > GRFFile.CompressionType.CompressedRleCrc)
|
||||
throw new ArgumentException(nameof(_compressionType));
|
||||
stream.WriteByte((byte)_compressionType);
|
||||
WriteInt(stream, _grfFile.Crc);
|
||||
stream.WriteByte(0);
|
||||
stream.WriteByte(0);
|
||||
stream.WriteByte(0);
|
||||
stream.WriteByte(0); // <- used in world grf
|
||||
}
|
||||
|
||||
private void WriteBody(Stream stream)
|
||||
{
|
||||
WriteTagLookUpTable(stream);
|
||||
SetString(stream, _grfFile.Root.Name);
|
||||
WriteInt(stream, _grfFile.Root.ChildRules.Count);
|
||||
WriteGameRuleHierarchy(stream, _grfFile.Root);
|
||||
}
|
||||
|
||||
private void WriteTagLookUpTable(Stream stream)
|
||||
{
|
||||
WriteInt(stream, StringLookUpTable.Count);
|
||||
StringLookUpTable.ForEach( s => WriteString(stream, s) );
|
||||
}
|
||||
|
||||
private void WriteGameRuleHierarchy(Stream stream, GRFFile.GameRule rule)
|
||||
{
|
||||
foreach (var subRule in rule.ChildRules)
|
||||
{
|
||||
SetString(stream, subRule.Name);
|
||||
WriteInt(stream, subRule.Parameters.Count);
|
||||
foreach (var param in subRule.Parameters) WriteParameter(stream, param);
|
||||
WriteInt(stream, subRule.ChildRules.Count);
|
||||
WriteGameRuleHierarchy(stream, subRule);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteParameter(Stream stream, KeyValuePair<string, string> param)
|
||||
{
|
||||
SetString(stream, param.Key);
|
||||
WriteString(stream, param.Value);
|
||||
}
|
||||
|
||||
private void SetString(Stream stream, string s)
|
||||
{
|
||||
int i = StringLookUpTable.IndexOf(s);
|
||||
if (i == -1) throw new Exception(nameof(s));
|
||||
WriteInt(stream, i);
|
||||
}
|
||||
|
||||
private void WriteString(Stream stream, string s)
|
||||
{
|
||||
WriteShort(stream, (short)s.Length);
|
||||
WriteString(stream, s, Encoding.ASCII);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
namespace PckStudio.Classes.Utils;
|
||||
|
||||
///! Credits
|
||||
///! <see cref="https://nicoschertler.wordpress.com/2014/06/04/generic-run-length-encoding-rle-for-c/"/>
|
||||
|
||||
/// <summary>
|
||||
/// Provides the RLE codec for any integer data type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The data's type. Must be an integer type or an ArgumentException will be thrown</typeparam>
|
||||
static class RLE<T> where T : struct, IConvertible
|
||||
{
|
||||
/// <summary>
|
||||
/// This is the marker that identifies a compressed run
|
||||
/// </summary>
|
||||
private static T rleMarker;
|
||||
|
||||
/// <summary>
|
||||
/// A run can be at most as long as the marker - 1
|
||||
/// </summary>
|
||||
private static ulong maxLength;
|
||||
|
||||
static RLE()
|
||||
{
|
||||
GetMaxValues();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RLE-Encodes a data set.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to encode</param>
|
||||
/// <returns>Encoded data</returns>
|
||||
public static IEnumerable<T> Encode(IEnumerable<T> data)
|
||||
{
|
||||
var enumerator = data.GetEnumerator();
|
||||
|
||||
if (!enumerator.MoveNext())
|
||||
yield break;
|
||||
|
||||
var firstRunValue = enumerator.Current;
|
||||
ulong runLength = 1;
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var currentValue = enumerator.Current;
|
||||
// if the current value is the value of the current run, don't yield anything,
|
||||
// just extend the run
|
||||
if (currentValue.Equals(firstRunValue))
|
||||
runLength++;
|
||||
else
|
||||
{
|
||||
// the current value is different from the current run
|
||||
// yield what we have so far
|
||||
foreach (var item in MakeRun(firstRunValue, runLength))
|
||||
yield return item;
|
||||
|
||||
// and reset the run
|
||||
firstRunValue = currentValue;
|
||||
runLength = 1;
|
||||
}
|
||||
// if there are very many identical values, don't exceed the max length
|
||||
if (runLength > maxLength)
|
||||
{
|
||||
foreach (var item in MakeRun(firstRunValue, maxLength))
|
||||
yield return item;
|
||||
runLength -= maxLength;
|
||||
}
|
||||
}
|
||||
//yield everything that has been buffered
|
||||
foreach (var item in MakeRun(firstRunValue, runLength))
|
||||
yield return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes RLE-encoded data
|
||||
/// </summary>
|
||||
/// <param name="data">RLE-encoded data</param>
|
||||
/// <returns>The original data</returns>
|
||||
public static IEnumerable<T> Decode(IEnumerable<T> data)
|
||||
{
|
||||
var enumerator = data.GetEnumerator();
|
||||
if (!enumerator.MoveNext())
|
||||
yield break;
|
||||
|
||||
do
|
||||
{
|
||||
var value = enumerator.Current;
|
||||
if (!value.Equals(rleMarker))
|
||||
{
|
||||
//an ordinary value
|
||||
yield return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
//might be flag or escape
|
||||
//examine the next value
|
||||
if (!enumerator.MoveNext())
|
||||
throw new ArgumentException("The provided data is not properly encoded.");
|
||||
if (enumerator.Current.Equals(rleMarker))
|
||||
{
|
||||
//escaped value
|
||||
yield return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
//rle marker
|
||||
var length = enumerator.Current.ToInt64(CultureInfo.InvariantCulture);
|
||||
if (!enumerator.MoveNext())
|
||||
throw new ArgumentException("The provided data is not properly encoded.");
|
||||
var val = enumerator.Current;
|
||||
for (var j = 0; j < length+1; ++j)
|
||||
yield return val;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (enumerator.MoveNext());
|
||||
}
|
||||
|
||||
private static IEnumerable<T> MakeRun(T value, ulong length)
|
||||
{
|
||||
if ((length <= 3 && !value.Equals(rleMarker)) || length <= 1)
|
||||
{
|
||||
//don't compress this run, it is just too small
|
||||
for (ulong i = 0; i < length; i++)
|
||||
{
|
||||
yield return value.Equals(rleMarker) ? rleMarker : value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//compressed run
|
||||
yield return rleMarker;
|
||||
yield return (T)(dynamic)(length-1);
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void GetMaxValues()
|
||||
{
|
||||
TypeCode typeCode = Type.GetTypeCode(typeof(T));
|
||||
switch (typeCode)
|
||||
{
|
||||
case TypeCode.Byte:
|
||||
{
|
||||
var limit = byte.MaxValue;
|
||||
rleMarker = __refvalue(__makeref(limit), T);
|
||||
maxLength = (ulong)(limit - 1);
|
||||
break;
|
||||
}
|
||||
case TypeCode.Char:
|
||||
{
|
||||
var limit = char.MaxValue;
|
||||
rleMarker = __refvalue(__makeref(limit), T);
|
||||
maxLength = (ulong)(limit - 1);
|
||||
break;
|
||||
}
|
||||
case TypeCode.Int16:
|
||||
{
|
||||
var limit = short.MaxValue;
|
||||
rleMarker = __refvalue(__makeref(limit), T);
|
||||
maxLength = (ulong)(limit - 1);
|
||||
break;
|
||||
}
|
||||
case TypeCode.Int32:
|
||||
{
|
||||
var limit = int.MaxValue;
|
||||
rleMarker = __refvalue(__makeref(limit), T);
|
||||
maxLength = (ulong)(limit - 1);
|
||||
break;
|
||||
}
|
||||
case TypeCode.Int64:
|
||||
{
|
||||
var limit = long.MaxValue;
|
||||
rleMarker = __refvalue(__makeref(limit), T);
|
||||
maxLength = (ulong)(limit - 1);
|
||||
break;
|
||||
}
|
||||
case TypeCode.SByte:
|
||||
{
|
||||
var limit = sbyte.MaxValue;
|
||||
rleMarker = __refvalue(__makeref(limit), T);
|
||||
maxLength = (ulong)(limit - 1);
|
||||
break;
|
||||
}
|
||||
case TypeCode.UInt16:
|
||||
{
|
||||
var limit = ushort.MaxValue;
|
||||
rleMarker = __refvalue(__makeref(limit), T);
|
||||
maxLength = (ulong)(limit - 1);
|
||||
break;
|
||||
}
|
||||
case TypeCode.UInt32:
|
||||
{
|
||||
var limit = uint.MaxValue;
|
||||
rleMarker = __refvalue(__makeref(limit), T);
|
||||
maxLength = (ulong)(limit - 1);
|
||||
break;
|
||||
}
|
||||
case TypeCode.UInt64:
|
||||
{
|
||||
var limit = ulong.MaxValue;
|
||||
rleMarker = __refvalue(__makeref(limit), T);
|
||||
maxLength = (ulong)(limit - 1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ArgumentException("The provided type parameter is not an integer type");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
namespace PckStudio.Classes.Utils.grf
|
||||
{
|
||||
public class CRC32
|
||||
{
|
||||
static readonly private byte[] offsets =
|
||||
{
|
||||
0x05, 0x06, 0x07, 0x03,
|
||||
0x04, 0x05, 0x06, 0x07,
|
||||
0x01, 0x02, 0x06, 0x01,
|
||||
0x01, 0x02, 0x03, 0x04,
|
||||
0x05, 0x05, 0x06, 0x07,
|
||||
0x03, 0x04, 0x05, 0x06,
|
||||
0x07, 0x01, 0x02, 0x06,
|
||||
0x01, 0x01, 0x02, 0x03,
|
||||
0x04, 0x05, 0x05, 0x06,
|
||||
0x07, 0x03, 0x04, 0x05,
|
||||
0x06, 0x07, 0x07, 0x05,
|
||||
0x04, 0x07, 0x05, 0x04,
|
||||
0x07, 0x05, 0x04, 0x07,
|
||||
0x06, 0x05, 0x04, 0x03,
|
||||
};
|
||||
|
||||
static private uint[] CRCTable;
|
||||
static private bool hasCRCTable = false;
|
||||
|
||||
static void MakeCRCTable()
|
||||
{
|
||||
const uint val = 0xedb88320;
|
||||
CRCTable = new uint[256];
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
uint temp = (uint)i;
|
||||
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
if ((temp & 1) == 1)
|
||||
{
|
||||
temp >>= 1;
|
||||
temp ^= val;
|
||||
}
|
||||
else { temp >>= 1; }
|
||||
}
|
||||
CRCTable[i] = temp;
|
||||
}
|
||||
hasCRCTable = true;
|
||||
}
|
||||
public static uint UpdateCRC(uint _crc, byte[] data)
|
||||
{
|
||||
uint crc = _crc;
|
||||
if (!hasCRCTable)
|
||||
MakeCRCTable();
|
||||
long pos = 0;
|
||||
long offset = 0;
|
||||
do
|
||||
{
|
||||
var value = data[pos];
|
||||
pos += offsets[offset];
|
||||
crc = CRCTable[(crc ^ value) & 0xff] ^ crc >> 8;
|
||||
offset = offset + 1U + ((offset + 1U >> 3) / 7) * -0x38;
|
||||
} while (pos < data.Length);
|
||||
return crc;
|
||||
}
|
||||
|
||||
public static uint CRC(byte[] data)
|
||||
{
|
||||
return ~UpdateCRC(0xffffffff, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,785 +0,0 @@
|
||||
#region HEADER
|
||||
/* This file was derived from libmspack
|
||||
* (C) 2003-2004 Stuart Caie.
|
||||
* (C) 2011 Ali Scissons.
|
||||
*
|
||||
* The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted
|
||||
* by Microsoft Corporation.
|
||||
*
|
||||
* This source file is Dual licensed; meaning the end-user of this source file
|
||||
* may redistribute/modify it under the LGPL 2.1 or MS-PL licenses.
|
||||
*/
|
||||
#region LGPL License
|
||||
/* GNU LESSER GENERAL PUBLIC LICENSE version 2.1
|
||||
* LzxDecoder is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License (LGPL) version 2.1
|
||||
*/
|
||||
#endregion
|
||||
#region MS-PL License
|
||||
/*
|
||||
* MICROSOFT PUBLIC LICENSE
|
||||
* This source code is subject to the terms of the Microsoft Public License (Ms-PL).
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* is permitted provided that redistributions of the source code retain the above
|
||||
* copyright notices and this file header.
|
||||
*
|
||||
* Additional copyright notices should be appended to the list above.
|
||||
*
|
||||
* For details, see <http://www.opensource.org/licenses/ms-pl.html>.
|
||||
*/
|
||||
#endregion
|
||||
/*
|
||||
* This derived work is recognized by Stuart Caie and is authorized to adapt
|
||||
* any changes made to lzxd.c in his libmspack library and will still retain
|
||||
* this dual licensing scheme. Big thanks to Stuart Caie!
|
||||
*
|
||||
* DETAILS
|
||||
* This file is a pure C# port of the lzxd.c file from libmspack, with minor
|
||||
* changes towards the decompression of XNB files. The original decompression
|
||||
* software of LZX encoded data was written by Suart Caie in his
|
||||
* libmspack/cabextract projects, which can be located at
|
||||
* http://http://www.cabextract.org.uk/
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.Xna.Framework.Content
|
||||
{
|
||||
using System.IO;
|
||||
|
||||
class LzxDecoder
|
||||
{
|
||||
public static uint[] position_base = null;
|
||||
public static byte[] extra_bits = null;
|
||||
|
||||
private LzxState m_state;
|
||||
|
||||
public LzxDecoder(int window)
|
||||
{
|
||||
uint wndsize = (uint)(1 << window);
|
||||
int posn_slots;
|
||||
|
||||
// setup proper exception
|
||||
if (window < 15 || window > 21) throw new UnsupportedWindowSizeRange();
|
||||
|
||||
// let's initialise our state
|
||||
m_state = new LzxState();
|
||||
m_state.actual_size = 0;
|
||||
m_state.window = new byte[wndsize];
|
||||
for (int i = 0; i < wndsize; i++) m_state.window[i] = 0xDC;
|
||||
m_state.actual_size = wndsize;
|
||||
m_state.window_size = wndsize;
|
||||
m_state.window_posn = 0;
|
||||
|
||||
/* initialize static tables */
|
||||
if (extra_bits == null)
|
||||
{
|
||||
extra_bits = new byte[52];
|
||||
for (int i = 0, j = 0; i <= 50; i += 2)
|
||||
{
|
||||
extra_bits[i] = extra_bits[i + 1] = (byte)j;
|
||||
if ((i != 0) && (j < 17)) j++;
|
||||
}
|
||||
}
|
||||
if (position_base == null)
|
||||
{
|
||||
position_base = new uint[51];
|
||||
for (int i = 0, j = 0; i <= 50; i++)
|
||||
{
|
||||
position_base[i] = (uint)j;
|
||||
j += 1 << extra_bits[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* calculate required position slots */
|
||||
if (window == 20) posn_slots = 42;
|
||||
else if (window == 21) posn_slots = 50;
|
||||
else posn_slots = window << 1;
|
||||
|
||||
m_state.R0 = m_state.R1 = m_state.R2 = 1;
|
||||
m_state.main_elements = (ushort)(LzxConstants.NUM_CHARS + (posn_slots << 3));
|
||||
m_state.header_read = 0;
|
||||
m_state.frames_read = 0;
|
||||
m_state.block_remaining = 0;
|
||||
m_state.block_type = LzxConstants.BLOCKTYPE.INVALID;
|
||||
m_state.intel_curpos = 0;
|
||||
m_state.intel_started = 0;
|
||||
|
||||
// yo dawg i herd u liek arrays so we put arrays in ur arrays so u can array while u array
|
||||
m_state.PRETREE_table = new ushort[(1 << LzxConstants.PRETREE_TABLEBITS) + (LzxConstants.PRETREE_MAXSYMBOLS << 1)];
|
||||
m_state.PRETREE_len = new byte[LzxConstants.PRETREE_MAXSYMBOLS + LzxConstants.LENTABLE_SAFETY];
|
||||
m_state.MAINTREE_table = new ushort[(1 << LzxConstants.MAINTREE_TABLEBITS) + (LzxConstants.MAINTREE_MAXSYMBOLS << 1)];
|
||||
m_state.MAINTREE_len = new byte[LzxConstants.MAINTREE_MAXSYMBOLS + LzxConstants.LENTABLE_SAFETY];
|
||||
m_state.LENGTH_table = new ushort[(1 << LzxConstants.LENGTH_TABLEBITS) + (LzxConstants.LENGTH_MAXSYMBOLS << 1)];
|
||||
m_state.LENGTH_len = new byte[LzxConstants.LENGTH_MAXSYMBOLS + LzxConstants.LENTABLE_SAFETY];
|
||||
m_state.ALIGNED_table = new ushort[(1 << LzxConstants.ALIGNED_TABLEBITS) + (LzxConstants.ALIGNED_MAXSYMBOLS << 1)];
|
||||
m_state.ALIGNED_len = new byte[LzxConstants.ALIGNED_MAXSYMBOLS + LzxConstants.LENTABLE_SAFETY];
|
||||
/* initialise tables to 0 (because deltas will be applied to them) */
|
||||
for (int i = 0; i < LzxConstants.MAINTREE_MAXSYMBOLS; i++) m_state.MAINTREE_len[i] = 0;
|
||||
for (int i = 0; i < LzxConstants.LENGTH_MAXSYMBOLS; i++) m_state.LENGTH_len[i] = 0;
|
||||
}
|
||||
|
||||
public int Decompress(Stream inData, int inLen, Stream outData, int outLen)
|
||||
{
|
||||
BitBuffer bitbuf = new BitBuffer(inData);
|
||||
long startpos = inData.Position;
|
||||
long endpos = inData.Position + inLen;
|
||||
|
||||
byte[] window = m_state.window;
|
||||
|
||||
uint window_posn = m_state.window_posn;
|
||||
uint window_size = m_state.window_size;
|
||||
uint R0 = m_state.R0;
|
||||
uint R1 = m_state.R1;
|
||||
uint R2 = m_state.R2;
|
||||
uint i, j;
|
||||
|
||||
int togo = outLen, this_run, main_element, match_length, match_offset, length_footer, extra, verbatim_bits;
|
||||
int rundest, runsrc, copy_length, aligned_bits;
|
||||
|
||||
bitbuf.InitBitStream();
|
||||
|
||||
/* read header if necessary */
|
||||
if (m_state.header_read == 0)
|
||||
{
|
||||
uint intel = bitbuf.ReadBits(1);
|
||||
if (intel != 0)
|
||||
{
|
||||
// read the filesize
|
||||
i = bitbuf.ReadBits(16); j = bitbuf.ReadBits(16);
|
||||
m_state.intel_filesize = (int)((i << 16) | j);
|
||||
}
|
||||
m_state.header_read = 1;
|
||||
}
|
||||
|
||||
/* main decoding loop */
|
||||
while (togo > 0)
|
||||
{
|
||||
/* last block finished, new block expected */
|
||||
if (m_state.block_remaining == 0)
|
||||
{
|
||||
// TODO may screw something up here
|
||||
if (m_state.block_type == LzxConstants.BLOCKTYPE.UNCOMPRESSED)
|
||||
{
|
||||
if ((m_state.block_length & 1) == 1) inData.ReadByte(); /* realign bitstream to word */
|
||||
bitbuf.InitBitStream();
|
||||
}
|
||||
|
||||
m_state.block_type = (LzxConstants.BLOCKTYPE)bitbuf.ReadBits(3); ;
|
||||
i = bitbuf.ReadBits(16);
|
||||
j = bitbuf.ReadBits(8);
|
||||
m_state.block_remaining = m_state.block_length = (uint)((i << 8) | j);
|
||||
|
||||
switch (m_state.block_type)
|
||||
{
|
||||
case LzxConstants.BLOCKTYPE.ALIGNED:
|
||||
for (i = 0, j = 0; i < 8; i++) { j = bitbuf.ReadBits(3); m_state.ALIGNED_len[i] = (byte)j; }
|
||||
MakeDecodeTable(LzxConstants.ALIGNED_MAXSYMBOLS, LzxConstants.ALIGNED_TABLEBITS,
|
||||
m_state.ALIGNED_len, m_state.ALIGNED_table);
|
||||
/* rest of aligned header is same as verbatim */
|
||||
goto case LzxConstants.BLOCKTYPE.VERBATIM;
|
||||
|
||||
case LzxConstants.BLOCKTYPE.VERBATIM:
|
||||
ReadLengths(m_state.MAINTREE_len, 0, 256, bitbuf);
|
||||
ReadLengths(m_state.MAINTREE_len, 256, m_state.main_elements, bitbuf);
|
||||
MakeDecodeTable(LzxConstants.MAINTREE_MAXSYMBOLS, LzxConstants.MAINTREE_TABLEBITS,
|
||||
m_state.MAINTREE_len, m_state.MAINTREE_table);
|
||||
if (m_state.MAINTREE_len[0xE8] != 0) m_state.intel_started = 1;
|
||||
|
||||
ReadLengths(m_state.LENGTH_len, 0, LzxConstants.NUM_SECONDARY_LENGTHS, bitbuf);
|
||||
MakeDecodeTable(LzxConstants.LENGTH_MAXSYMBOLS, LzxConstants.LENGTH_TABLEBITS,
|
||||
m_state.LENGTH_len, m_state.LENGTH_table);
|
||||
break;
|
||||
|
||||
case LzxConstants.BLOCKTYPE.UNCOMPRESSED:
|
||||
m_state.intel_started = 1; /* because we can't assume otherwise */
|
||||
bitbuf.EnsureBits(16); /* get up to 16 pad bits into the buffer */
|
||||
if (bitbuf.GetBitsLeft() > 16) inData.Seek(-2, SeekOrigin.Current); /* and align the bitstream! */
|
||||
byte hi, mh, ml, lo;
|
||||
lo = (byte)inData.ReadByte(); ml = (byte)inData.ReadByte(); mh = (byte)inData.ReadByte(); hi = (byte)inData.ReadByte();
|
||||
R0 = (uint)(lo | ml << 8 | mh << 16 | hi << 24);
|
||||
lo = (byte)inData.ReadByte(); ml = (byte)inData.ReadByte(); mh = (byte)inData.ReadByte(); hi = (byte)inData.ReadByte();
|
||||
R1 = (uint)(lo | ml << 8 | mh << 16 | hi << 24);
|
||||
lo = (byte)inData.ReadByte(); ml = (byte)inData.ReadByte(); mh = (byte)inData.ReadByte(); hi = (byte)inData.ReadByte();
|
||||
R2 = (uint)(lo | ml << 8 | mh << 16 | hi << 24);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1; // TODO throw proper exception
|
||||
}
|
||||
}
|
||||
|
||||
/* buffer exhaustion check */
|
||||
if (inData.Position > (startpos + inLen))
|
||||
{
|
||||
/* it's possible to have a file where the next run is less than
|
||||
* 16 bits in size. In this case, the READ_HUFFSYM() macro used
|
||||
* in building the tables will exhaust the buffer, so we should
|
||||
* allow for this, but not allow those accidentally read bits to
|
||||
* be used (so we check that there are at least 16 bits
|
||||
* remaining - in this boundary case they aren't really part of
|
||||
* the compressed data)
|
||||
*/
|
||||
//Debug.WriteLine("WTF");
|
||||
|
||||
if (inData.Position > (startpos + inLen + 2) || bitbuf.GetBitsLeft() < 16) return -1; //TODO throw proper exception
|
||||
}
|
||||
|
||||
while ((this_run = (int)m_state.block_remaining) > 0 && togo > 0)
|
||||
{
|
||||
if (this_run > togo) this_run = togo;
|
||||
togo -= this_run;
|
||||
m_state.block_remaining -= (uint)this_run;
|
||||
|
||||
/* apply 2^x-1 mask */
|
||||
window_posn &= window_size - 1;
|
||||
/* runs can't straddle the window wraparound */
|
||||
if ((window_posn + this_run) > window_size)
|
||||
return -1; //TODO throw proper exception
|
||||
|
||||
switch (m_state.block_type)
|
||||
{
|
||||
case LzxConstants.BLOCKTYPE.VERBATIM:
|
||||
while (this_run > 0)
|
||||
{
|
||||
main_element = (int)ReadHuffSym(m_state.MAINTREE_table, m_state.MAINTREE_len,
|
||||
LzxConstants.MAINTREE_MAXSYMBOLS, LzxConstants.MAINTREE_TABLEBITS,
|
||||
bitbuf);
|
||||
if (main_element < LzxConstants.NUM_CHARS)
|
||||
{
|
||||
/* literal: 0 to NUM_CHARS-1 */
|
||||
window[window_posn++] = (byte)main_element;
|
||||
this_run--;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* match: NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
|
||||
main_element -= LzxConstants.NUM_CHARS;
|
||||
|
||||
match_length = main_element & LzxConstants.NUM_PRIMARY_LENGTHS;
|
||||
if (match_length == LzxConstants.NUM_PRIMARY_LENGTHS)
|
||||
{
|
||||
length_footer = (int)ReadHuffSym(m_state.LENGTH_table, m_state.LENGTH_len,
|
||||
LzxConstants.LENGTH_MAXSYMBOLS, LzxConstants.LENGTH_TABLEBITS,
|
||||
bitbuf);
|
||||
match_length += length_footer;
|
||||
}
|
||||
match_length += LzxConstants.MIN_MATCH;
|
||||
|
||||
match_offset = main_element >> 3;
|
||||
|
||||
if (match_offset > 2)
|
||||
{
|
||||
/* not repeated offset */
|
||||
if (match_offset != 3)
|
||||
{
|
||||
extra = extra_bits[match_offset];
|
||||
verbatim_bits = (int)bitbuf.ReadBits((byte)extra);
|
||||
match_offset = (int)position_base[match_offset] - 2 + verbatim_bits;
|
||||
}
|
||||
else
|
||||
{
|
||||
match_offset = 1;
|
||||
}
|
||||
|
||||
/* update repeated offset LRU queue */
|
||||
R2 = R1; R1 = R0; R0 = (uint)match_offset;
|
||||
}
|
||||
else if (match_offset == 0)
|
||||
{
|
||||
match_offset = (int)R0;
|
||||
}
|
||||
else if (match_offset == 1)
|
||||
{
|
||||
match_offset = (int)R1;
|
||||
R1 = R0; R0 = (uint)match_offset;
|
||||
}
|
||||
else /* match_offset == 2 */
|
||||
{
|
||||
match_offset = (int)R2;
|
||||
R2 = R0; R0 = (uint)match_offset;
|
||||
}
|
||||
|
||||
rundest = (int)window_posn;
|
||||
this_run -= match_length;
|
||||
|
||||
/* copy any wrapped around source data */
|
||||
if (window_posn >= match_offset)
|
||||
{
|
||||
/* no wrap */
|
||||
runsrc = rundest - match_offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
runsrc = rundest + ((int)window_size - match_offset);
|
||||
copy_length = match_offset - (int)window_posn;
|
||||
if (copy_length < match_length)
|
||||
{
|
||||
match_length -= copy_length;
|
||||
window_posn += (uint)copy_length;
|
||||
while (copy_length-- > 0) window[rundest++] = window[runsrc++];
|
||||
runsrc = 0;
|
||||
}
|
||||
}
|
||||
window_posn += (uint)match_length;
|
||||
|
||||
/* copy match data - no worries about destination wraps */
|
||||
while (match_length-- > 0) window[rundest++] = window[runsrc++];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LzxConstants.BLOCKTYPE.ALIGNED:
|
||||
while (this_run > 0)
|
||||
{
|
||||
main_element = (int)ReadHuffSym(m_state.MAINTREE_table, m_state.MAINTREE_len,
|
||||
LzxConstants.MAINTREE_MAXSYMBOLS, LzxConstants.MAINTREE_TABLEBITS,
|
||||
bitbuf);
|
||||
|
||||
if (main_element < LzxConstants.NUM_CHARS)
|
||||
{
|
||||
/* literal 0 to NUM_CHARS-1 */
|
||||
window[window_posn++] = (byte)main_element;
|
||||
this_run--;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* match: NUM_CHARS + ((slot<<3) | length_header (3 bits)) */
|
||||
main_element -= LzxConstants.NUM_CHARS;
|
||||
|
||||
match_length = main_element & LzxConstants.NUM_PRIMARY_LENGTHS;
|
||||
if (match_length == LzxConstants.NUM_PRIMARY_LENGTHS)
|
||||
{
|
||||
length_footer = (int)ReadHuffSym(m_state.LENGTH_table, m_state.LENGTH_len,
|
||||
LzxConstants.LENGTH_MAXSYMBOLS, LzxConstants.LENGTH_TABLEBITS,
|
||||
bitbuf);
|
||||
match_length += length_footer;
|
||||
}
|
||||
match_length += LzxConstants.MIN_MATCH;
|
||||
|
||||
match_offset = main_element >> 3;
|
||||
|
||||
if (match_offset > 2)
|
||||
{
|
||||
/* not repeated offset */
|
||||
extra = extra_bits[match_offset];
|
||||
match_offset = (int)position_base[match_offset] - 2;
|
||||
if (extra > 3)
|
||||
{
|
||||
/* verbatim and aligned bits */
|
||||
extra -= 3;
|
||||
verbatim_bits = (int)bitbuf.ReadBits((byte)extra);
|
||||
match_offset += (verbatim_bits << 3);
|
||||
aligned_bits = (int)ReadHuffSym(m_state.ALIGNED_table, m_state.ALIGNED_len,
|
||||
LzxConstants.ALIGNED_MAXSYMBOLS, LzxConstants.ALIGNED_TABLEBITS,
|
||||
bitbuf);
|
||||
match_offset += aligned_bits;
|
||||
}
|
||||
else if (extra == 3)
|
||||
{
|
||||
/* aligned bits only */
|
||||
aligned_bits = (int)ReadHuffSym(m_state.ALIGNED_table, m_state.ALIGNED_len,
|
||||
LzxConstants.ALIGNED_MAXSYMBOLS, LzxConstants.ALIGNED_TABLEBITS,
|
||||
bitbuf);
|
||||
match_offset += aligned_bits;
|
||||
}
|
||||
else if (extra > 0) /* extra==1, extra==2 */
|
||||
{
|
||||
/* verbatim bits only */
|
||||
verbatim_bits = (int)bitbuf.ReadBits((byte)extra);
|
||||
match_offset += verbatim_bits;
|
||||
}
|
||||
else /* extra == 0 */
|
||||
{
|
||||
/* ??? */
|
||||
match_offset = 1;
|
||||
}
|
||||
|
||||
/* update repeated offset LRU queue */
|
||||
R2 = R1; R1 = R0; R0 = (uint)match_offset;
|
||||
}
|
||||
else if (match_offset == 0)
|
||||
{
|
||||
match_offset = (int)R0;
|
||||
}
|
||||
else if (match_offset == 1)
|
||||
{
|
||||
match_offset = (int)R1;
|
||||
R1 = R0; R0 = (uint)match_offset;
|
||||
}
|
||||
else /* match_offset == 2 */
|
||||
{
|
||||
match_offset = (int)R2;
|
||||
R2 = R0; R0 = (uint)match_offset;
|
||||
}
|
||||
|
||||
rundest = (int)window_posn;
|
||||
this_run -= match_length;
|
||||
|
||||
/* copy any wrapped around source data */
|
||||
if (window_posn >= match_offset)
|
||||
{
|
||||
/* no wrap */
|
||||
runsrc = rundest - match_offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
runsrc = rundest + ((int)window_size - match_offset);
|
||||
copy_length = match_offset - (int)window_posn;
|
||||
if (copy_length < match_length)
|
||||
{
|
||||
match_length -= copy_length;
|
||||
window_posn += (uint)copy_length;
|
||||
while (copy_length-- > 0) window[rundest++] = window[runsrc++];
|
||||
runsrc = 0;
|
||||
}
|
||||
}
|
||||
window_posn += (uint)match_length;
|
||||
|
||||
/* copy match data - no worries about destination wraps */
|
||||
while (match_length-- > 0) window[rundest++] = window[runsrc++];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LzxConstants.BLOCKTYPE.UNCOMPRESSED:
|
||||
if ((inData.Position + this_run) > endpos) return -1; //TODO throw proper exception
|
||||
byte[] temp_buffer = new byte[this_run];
|
||||
inData.Read(temp_buffer, 0, this_run);
|
||||
temp_buffer.CopyTo(window, (int)window_posn);
|
||||
window_posn += (uint)this_run;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1; //TODO throw proper exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (togo != 0) return -1; //TODO throw proper exception
|
||||
int start_window_pos = (int)window_posn;
|
||||
if (start_window_pos == 0) start_window_pos = (int)window_size;
|
||||
start_window_pos -= outLen;
|
||||
outData.Write(window, start_window_pos, outLen);
|
||||
|
||||
m_state.window_posn = window_posn;
|
||||
m_state.R0 = R0;
|
||||
m_state.R1 = R1;
|
||||
m_state.R2 = R2;
|
||||
|
||||
// TODO finish intel E8 decoding
|
||||
/* intel E8 decoding */
|
||||
if ((m_state.frames_read++ < 32768) && m_state.intel_filesize != 0)
|
||||
{
|
||||
if (outLen <= 6 || m_state.intel_started == 0)
|
||||
{
|
||||
m_state.intel_curpos += outLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
int dataend = outLen - 10;
|
||||
uint curpos = (uint)m_state.intel_curpos;
|
||||
|
||||
m_state.intel_curpos = (int)curpos + outLen;
|
||||
|
||||
while (outData.Position < dataend)
|
||||
{
|
||||
if (outData.ReadByte() != 0xE8) { curpos++; continue; }
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// READ_LENGTHS(table, first, last)
|
||||
// if(lzx_read_lens(LENTABLE(table), first, last, bitsleft))
|
||||
// return ERROR (ILLEGAL_DATA)
|
||||
//
|
||||
|
||||
// TODO make returns throw exceptions
|
||||
private int MakeDecodeTable(uint nsyms, uint nbits, byte[] length, ushort[] table)
|
||||
{
|
||||
ushort sym;
|
||||
uint leaf;
|
||||
byte bit_num = 1;
|
||||
uint fill;
|
||||
uint pos = 0; /* the current position in the decode table */
|
||||
uint table_mask = (uint)(1 << (int)nbits);
|
||||
uint bit_mask = table_mask >> 1; /* don't do 0 length codes */
|
||||
uint next_symbol = bit_mask; /* base of allocation for long codes */
|
||||
|
||||
/* fill entries for codes short enough for a direct mapping */
|
||||
while (bit_num <= nbits)
|
||||
{
|
||||
for (sym = 0; sym < nsyms; sym++)
|
||||
{
|
||||
if (length[sym] == bit_num)
|
||||
{
|
||||
leaf = pos;
|
||||
|
||||
if ((pos += bit_mask) > table_mask) return 1; /* table overrun */
|
||||
|
||||
/* fill all possible lookups of this symbol with the symbol itself */
|
||||
fill = bit_mask;
|
||||
while (fill-- > 0) table[leaf++] = sym;
|
||||
}
|
||||
}
|
||||
bit_mask >>= 1;
|
||||
bit_num++;
|
||||
}
|
||||
|
||||
/* if there are any codes longer than nbits */
|
||||
if (pos != table_mask)
|
||||
{
|
||||
/* clear the remainder of the table */
|
||||
for (sym = (ushort)pos; sym < table_mask; sym++) table[sym] = 0;
|
||||
|
||||
/* give ourselves room for codes to grow by up to 16 more bits */
|
||||
pos <<= 16;
|
||||
table_mask <<= 16;
|
||||
bit_mask = 1 << 15;
|
||||
|
||||
while (bit_num <= 16)
|
||||
{
|
||||
for (sym = 0; sym < nsyms; sym++)
|
||||
{
|
||||
if (length[sym] == bit_num)
|
||||
{
|
||||
leaf = pos >> 16;
|
||||
for (fill = 0; fill < bit_num - nbits; fill++)
|
||||
{
|
||||
/* if this path hasn't been taken yet, 'allocate' two entries */
|
||||
if (table[leaf] == 0)
|
||||
{
|
||||
table[(next_symbol << 1)] = 0;
|
||||
table[(next_symbol << 1) + 1] = 0;
|
||||
table[leaf] = (ushort)(next_symbol++);
|
||||
}
|
||||
/* follow the path and select either left or right for next bit */
|
||||
leaf = (uint)(table[leaf] << 1);
|
||||
if (((pos >> (int)(15 - fill)) & 1) == 1) leaf++;
|
||||
}
|
||||
table[leaf] = sym;
|
||||
|
||||
if ((pos += bit_mask) > table_mask) return 1;
|
||||
}
|
||||
}
|
||||
bit_mask >>= 1;
|
||||
bit_num++;
|
||||
}
|
||||
}
|
||||
|
||||
/* full talbe? */
|
||||
if (pos == table_mask) return 0;
|
||||
|
||||
/* either erroneous table, or all elements are 0 - let's find out. */
|
||||
for (sym = 0; sym < nsyms; sym++) if (length[sym] != 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO throw exceptions instead of returns
|
||||
private void ReadLengths(byte[] lens, uint first, uint last, BitBuffer bitbuf)
|
||||
{
|
||||
uint x, y;
|
||||
int z;
|
||||
|
||||
// hufftbl pointer here?
|
||||
|
||||
for (x = 0; x < 20; x++)
|
||||
{
|
||||
y = bitbuf.ReadBits(4);
|
||||
m_state.PRETREE_len[x] = (byte)y;
|
||||
}
|
||||
MakeDecodeTable(LzxConstants.PRETREE_MAXSYMBOLS, LzxConstants.PRETREE_TABLEBITS,
|
||||
m_state.PRETREE_len, m_state.PRETREE_table);
|
||||
|
||||
for (x = first; x < last;)
|
||||
{
|
||||
z = (int)ReadHuffSym(m_state.PRETREE_table, m_state.PRETREE_len,
|
||||
LzxConstants.PRETREE_MAXSYMBOLS, LzxConstants.PRETREE_TABLEBITS, bitbuf);
|
||||
if (z == 17)
|
||||
{
|
||||
y = bitbuf.ReadBits(4); y += 4;
|
||||
while (y-- != 0) lens[x++] = 0;
|
||||
}
|
||||
else if (z == 18)
|
||||
{
|
||||
y = bitbuf.ReadBits(5); y += 20;
|
||||
while (y-- != 0) lens[x++] = 0;
|
||||
}
|
||||
else if (z == 19)
|
||||
{
|
||||
y = bitbuf.ReadBits(1); y += 4;
|
||||
z = (int)ReadHuffSym(m_state.PRETREE_table, m_state.PRETREE_len,
|
||||
LzxConstants.PRETREE_MAXSYMBOLS, LzxConstants.PRETREE_TABLEBITS, bitbuf);
|
||||
z = lens[x] - z; if (z < 0) z += 17;
|
||||
while (y-- != 0) lens[x++] = (byte)z;
|
||||
}
|
||||
else
|
||||
{
|
||||
z = lens[x] - z; if (z < 0) z += 17;
|
||||
lens[x++] = (byte)z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private uint ReadHuffSym(ushort[] table, byte[] lengths, uint nsyms, uint nbits, BitBuffer bitbuf)
|
||||
{
|
||||
uint i, j;
|
||||
bitbuf.EnsureBits(16);
|
||||
if ((i = table[bitbuf.PeekBits((byte)nbits)]) >= nsyms)
|
||||
{
|
||||
j = (uint)(1 << (int)((sizeof(uint) * 8) - nbits));
|
||||
do
|
||||
{
|
||||
j >>= 1; i <<= 1; i |= (bitbuf.GetBuffer() & j) != 0 ? (uint)1 : 0;
|
||||
if (j == 0) return 0; // TODO throw proper exception
|
||||
} while ((i = table[i]) >= nsyms);
|
||||
}
|
||||
j = lengths[i];
|
||||
bitbuf.RemoveBits((byte)j);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
#region Our BitBuffer Class
|
||||
private class BitBuffer
|
||||
{
|
||||
uint buffer;
|
||||
byte bitsleft;
|
||||
Stream byteStream;
|
||||
|
||||
public BitBuffer(Stream stream)
|
||||
{
|
||||
byteStream = stream;
|
||||
InitBitStream();
|
||||
}
|
||||
|
||||
public void InitBitStream()
|
||||
{
|
||||
buffer = 0;
|
||||
bitsleft = 0;
|
||||
}
|
||||
|
||||
public void EnsureBits(byte bits)
|
||||
{
|
||||
while (bitsleft < bits)
|
||||
{
|
||||
int lo = (byte)byteStream.ReadByte();
|
||||
int hi = (byte)byteStream.ReadByte();
|
||||
//int amount2shift = sizeof(uint)*8 - 16 - bitsleft;
|
||||
buffer |= (uint)(((hi << 8) | lo) << (sizeof(uint) * 8 - 16 - bitsleft));
|
||||
bitsleft += 16;
|
||||
}
|
||||
}
|
||||
|
||||
public uint PeekBits(byte bits)
|
||||
{
|
||||
return (buffer >> ((sizeof(uint) * 8) - bits));
|
||||
}
|
||||
|
||||
public void RemoveBits(byte bits)
|
||||
{
|
||||
buffer <<= bits;
|
||||
bitsleft -= bits;
|
||||
}
|
||||
|
||||
public uint ReadBits(byte bits)
|
||||
{
|
||||
uint ret = 0;
|
||||
|
||||
if (bits > 0)
|
||||
{
|
||||
EnsureBits(bits);
|
||||
ret = PeekBits(bits);
|
||||
RemoveBits(bits);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public uint GetBuffer()
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public byte GetBitsLeft()
|
||||
{
|
||||
return bitsleft;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
struct LzxState
|
||||
{
|
||||
public uint R0, R1, R2; /* for the LRU offset system */
|
||||
public ushort main_elements; /* number of main tree elements */
|
||||
public int header_read; /* have we started decoding at all yet? */
|
||||
public LzxConstants.BLOCKTYPE block_type; /* type of this block */
|
||||
public uint block_length; /* uncompressed length of this block */
|
||||
public uint block_remaining; /* uncompressed bytes still left to decode */
|
||||
public uint frames_read; /* the number of CFDATA blocks processed */
|
||||
public int intel_filesize; /* magic header value used for transform */
|
||||
public int intel_curpos; /* current offset in transform space */
|
||||
public int intel_started; /* have we seen any translateable data yet? */
|
||||
|
||||
public ushort[] PRETREE_table;
|
||||
public byte[] PRETREE_len;
|
||||
public ushort[] MAINTREE_table;
|
||||
public byte[] MAINTREE_len;
|
||||
public ushort[] LENGTH_table;
|
||||
public byte[] LENGTH_len;
|
||||
public ushort[] ALIGNED_table;
|
||||
public byte[] ALIGNED_len;
|
||||
|
||||
// NEEDED MEMBERS
|
||||
// CAB actualsize
|
||||
// CAB window
|
||||
// CAB window_size
|
||||
// CAB window_posn
|
||||
public uint actual_size;
|
||||
public byte[] window;
|
||||
public uint window_size;
|
||||
public uint window_posn;
|
||||
}
|
||||
}
|
||||
|
||||
/* CONSTANTS */
|
||||
struct LzxConstants
|
||||
{
|
||||
public const ushort MIN_MATCH = 2;
|
||||
public const ushort MAX_MATCH = 257;
|
||||
public const ushort NUM_CHARS = 256;
|
||||
public enum BLOCKTYPE
|
||||
{
|
||||
INVALID = 0,
|
||||
VERBATIM = 1,
|
||||
ALIGNED = 2,
|
||||
UNCOMPRESSED = 3
|
||||
}
|
||||
public const ushort PRETREE_NUM_ELEMENTS = 20;
|
||||
public const ushort ALIGNED_NUM_ELEMENTS = 8;
|
||||
public const ushort NUM_PRIMARY_LENGTHS = 7;
|
||||
public const ushort NUM_SECONDARY_LENGTHS = 249;
|
||||
|
||||
public const ushort PRETREE_MAXSYMBOLS = PRETREE_NUM_ELEMENTS;
|
||||
public const ushort PRETREE_TABLEBITS = 6;
|
||||
public const ushort MAINTREE_MAXSYMBOLS = NUM_CHARS + 50 * 8;
|
||||
public const ushort MAINTREE_TABLEBITS = 12;
|
||||
public const ushort LENGTH_MAXSYMBOLS = NUM_SECONDARY_LENGTHS + 1;
|
||||
public const ushort LENGTH_TABLEBITS = 12;
|
||||
public const ushort ALIGNED_MAXSYMBOLS = ALIGNED_NUM_ELEMENTS;
|
||||
public const ushort ALIGNED_TABLEBITS = 7;
|
||||
|
||||
public const ushort LENTABLE_SAFETY = 64;
|
||||
}
|
||||
|
||||
/* EXCEPTIONS */
|
||||
class UnsupportedWindowSizeRange : Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
// MonoGame - Copyright (C) The MonoGame Team
|
||||
// This file is subject to the terms and conditions defined in
|
||||
// file 'LICENSE.txt', which is part of this source code package.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.Xna.Framework.Content;
|
||||
|
||||
namespace MonoGame.Framework.Utilities
|
||||
{
|
||||
internal class LzxDecoderStream : Stream
|
||||
{
|
||||
LzxDecoder dec;
|
||||
MemoryStream decompressedStream;
|
||||
|
||||
public LzxDecoderStream(Stream input, int decompressedSize, int compressedSize)
|
||||
{
|
||||
dec = new LzxDecoder(16);
|
||||
|
||||
// TODO: Rewrite using block decompression like Lz4DecoderStream
|
||||
Decompress(input, decompressedSize, compressedSize);
|
||||
}
|
||||
|
||||
// Decompress into MemoryStream
|
||||
private void Decompress(Stream stream, int decompressedSize, int compressedSize)
|
||||
{
|
||||
//thanks to ShinAli (https://bitbucket.org/alisci01/xnbdecompressor)
|
||||
// default window size for XNB encoded files is 64Kb (need 16 bits to represent it)
|
||||
decompressedStream = new MemoryStream(decompressedSize);
|
||||
long startPos = stream.Position;
|
||||
long pos = startPos;
|
||||
|
||||
while (pos - startPos < compressedSize)
|
||||
{
|
||||
// the compressed stream is seperated into blocks that will decompress
|
||||
// into 32Kb or some other size if specified.
|
||||
// normal, 32Kb output blocks will have a short indicating the size
|
||||
// of the block before the block starts
|
||||
// blocks that have a defined output will be preceded by a byte of value
|
||||
// 0xFF (255), then a short indicating the output size and another
|
||||
// for the block size
|
||||
// all shorts for these cases are encoded in big endian order
|
||||
int hi = stream.ReadByte();
|
||||
int lo = stream.ReadByte();
|
||||
int block_size = (hi << 8) | lo;
|
||||
int frame_size = 0x8000; // frame size is 32Kb by default
|
||||
// does this block define a frame size?
|
||||
if (hi == 0xFF)
|
||||
{
|
||||
hi = lo;
|
||||
lo = (byte)stream.ReadByte();
|
||||
frame_size = (hi << 8) | lo;
|
||||
hi = (byte)stream.ReadByte();
|
||||
lo = (byte)stream.ReadByte();
|
||||
block_size = (hi << 8) | lo;
|
||||
pos += 5;
|
||||
}
|
||||
else
|
||||
pos += 2;
|
||||
|
||||
// either says there is nothing to decode
|
||||
if (block_size == 0 || frame_size == 0)
|
||||
break;
|
||||
|
||||
dec.Decompress(stream, block_size, decompressedStream, frame_size);
|
||||
pos += block_size;
|
||||
|
||||
// reset the position of the input just incase the bit buffer
|
||||
// read in some unused bytes
|
||||
stream.Seek(pos, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
Console.WriteLine(decompressedStream.Position);
|
||||
Console.WriteLine(decompressedSize);
|
||||
//if (decompressedStream.Position != decompressedSize)
|
||||
//{
|
||||
// throw new Exception("Decompression failed.");
|
||||
//}
|
||||
|
||||
decompressedStream.Seek(0, SeekOrigin.Begin);
|
||||
using (var fs = File.OpenWrite("xmem_test.grf"))
|
||||
{
|
||||
decompressedStream.CopyTo(fs);
|
||||
}
|
||||
decompressedStream.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
decompressedStream.Dispose();
|
||||
}
|
||||
dec = null;
|
||||
decompressedStream = null;
|
||||
}
|
||||
|
||||
#region Stream internals
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return decompressedStream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public override long Length
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace PckStudio.Forms.Editor
|
||||
{
|
||||
partial class GRFEditor
|
||||
partial class GameRuleFileEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
@@ -29,7 +29,7 @@
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GRFEditor));
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GameRuleFileEditor));
|
||||
this.GrfTreeView = new System.Windows.Forms.TreeView();
|
||||
this.MessageContextMenu = new MetroFramework.Controls.MetroContextMenu(this.components);
|
||||
this.addGameRuleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
@@ -44,9 +44,11 @@
|
||||
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.metroPanel1 = new MetroFramework.Controls.MetroPanel();
|
||||
this.compressionLvlToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.toolStripComboBox1 = new System.Windows.Forms.ToolStripComboBox();
|
||||
this.compressionTypeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.compressionTypeComboBox = new System.Windows.Forms.ToolStripComboBox();
|
||||
this.metroPanel1 = new MetroFramework.Controls.MetroPanel();
|
||||
this.MessageContextMenu.SuspendLayout();
|
||||
this.DetailContextMenu.SuspendLayout();
|
||||
this.menuStrip1.SuspendLayout();
|
||||
@@ -153,7 +155,8 @@
|
||||
this.menuStrip1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64)))));
|
||||
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.fileToolStripMenuItem,
|
||||
this.compressionLvlToolStripMenuItem});
|
||||
this.compressionLvlToolStripMenuItem,
|
||||
this.compressionTypeToolStripMenuItem});
|
||||
this.menuStrip1.Location = new System.Drawing.Point(25, 60);
|
||||
this.menuStrip1.Name = "menuStrip1";
|
||||
this.menuStrip1.Size = new System.Drawing.Size(450, 24);
|
||||
@@ -172,10 +175,10 @@
|
||||
//
|
||||
// openToolStripMenuItem
|
||||
//
|
||||
this.openToolStripMenuItem.Enabled = false;
|
||||
this.openToolStripMenuItem.Name = "openToolStripMenuItem";
|
||||
this.openToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
|
||||
this.openToolStripMenuItem.Text = "Open";
|
||||
this.openToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click);
|
||||
//
|
||||
// saveToolStripMenuItem
|
||||
//
|
||||
@@ -185,6 +188,43 @@
|
||||
this.saveToolStripMenuItem.Text = "Save";
|
||||
this.saveToolStripMenuItem.Click += new System.EventHandler(this.saveToolStripMenuItem_Click);
|
||||
//
|
||||
// compressionLvlToolStripMenuItem
|
||||
//
|
||||
this.compressionLvlToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.toolStripComboBox1});
|
||||
this.compressionLvlToolStripMenuItem.ForeColor = System.Drawing.SystemColors.Menu;
|
||||
this.compressionLvlToolStripMenuItem.Name = "compressionLvlToolStripMenuItem";
|
||||
this.compressionLvlToolStripMenuItem.Size = new System.Drawing.Size(106, 20);
|
||||
this.compressionLvlToolStripMenuItem.Text = "Compression Lvl";
|
||||
//
|
||||
// toolStripComboBox1
|
||||
//
|
||||
this.toolStripComboBox1.Items.AddRange(new object[] {
|
||||
"None",
|
||||
"Compressed",
|
||||
"Compressed + RLE",
|
||||
"Compressed + RLE + CRC"});
|
||||
this.toolStripComboBox1.Name = "toolStripComboBox1";
|
||||
this.toolStripComboBox1.Size = new System.Drawing.Size(121, 23);
|
||||
this.toolStripComboBox1.Text = "None";
|
||||
//
|
||||
// compressionTypeToolStripMenuItem
|
||||
//
|
||||
this.compressionTypeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.compressionTypeComboBox});
|
||||
this.compressionTypeToolStripMenuItem.Name = "compressionTypeToolStripMenuItem";
|
||||
this.compressionTypeToolStripMenuItem.Size = new System.Drawing.Size(116, 20);
|
||||
this.compressionTypeToolStripMenuItem.Text = "Compression Type";
|
||||
//
|
||||
// compressionTypeComboBox
|
||||
//
|
||||
this.compressionTypeComboBox.Items.AddRange(new object[] {
|
||||
"Zlib",
|
||||
"Deflate",
|
||||
"XMem"});
|
||||
this.compressionTypeComboBox.Name = "compressionTypeComboBox";
|
||||
this.compressionTypeComboBox.Size = new System.Drawing.Size(121, 23);
|
||||
//
|
||||
// metroPanel1
|
||||
//
|
||||
this.metroPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
@@ -205,26 +245,6 @@
|
||||
this.metroPanel1.VerticalScrollbarSize = 10;
|
||||
this.metroPanel1.Resize += new System.EventHandler(this.metroPanel1_Resize);
|
||||
//
|
||||
// compressionLvlToolStripMenuItem
|
||||
//
|
||||
this.compressionLvlToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.toolStripComboBox1});
|
||||
this.compressionLvlToolStripMenuItem.ForeColor = System.Drawing.SystemColors.Menu;
|
||||
this.compressionLvlToolStripMenuItem.Name = "compressionLvlToolStripMenuItem";
|
||||
this.compressionLvlToolStripMenuItem.Size = new System.Drawing.Size(106, 20);
|
||||
this.compressionLvlToolStripMenuItem.Text = "Compression Lvl";
|
||||
//
|
||||
// toolStripComboBox1
|
||||
//
|
||||
this.toolStripComboBox1.Items.AddRange(new object[] {
|
||||
"None",
|
||||
"Compressed",
|
||||
"Compressed + RLE",
|
||||
"Compressed + RLE + CRC"});
|
||||
this.toolStripComboBox1.Name = "toolStripComboBox1";
|
||||
this.toolStripComboBox1.Size = new System.Drawing.Size(121, 23);
|
||||
this.toolStripComboBox1.Text = "Zlib";
|
||||
//
|
||||
// GRFEditor
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
@@ -274,5 +294,7 @@
|
||||
private MetroFramework.Controls.MetroPanel metroPanel1;
|
||||
private System.Windows.Forms.ToolStripMenuItem compressionLvlToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripComboBox toolStripComboBox1;
|
||||
private System.Windows.Forms.ToolStripMenuItem compressionTypeToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripComboBox compressionTypeComboBox;
|
||||
}
|
||||
}
|
||||
@@ -5,81 +5,101 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using PckStudio.Classes.FileTypes;
|
||||
using PckStudio.Classes.IO.GRF;
|
||||
using PckStudio.Forms.Additional_Popups.Grf;
|
||||
using PckStudio.Classes.Misc;
|
||||
using OMI.Formats.GameRule;
|
||||
using OMI.Workers.GameRule;
|
||||
using System.Diagnostics;
|
||||
using PckStudio.Forms.Additional_Popups.Audio;
|
||||
|
||||
namespace PckStudio.Forms.Editor
|
||||
{
|
||||
public partial class GRFEditor : MetroFramework.Forms.MetroForm
|
||||
public partial class GameRuleFileEditor : MetroFramework.Forms.MetroForm
|
||||
{
|
||||
private PCKFile.FileData _pckfile;
|
||||
private GRFFile _file;
|
||||
private GameRuleFile _file;
|
||||
|
||||
|
||||
private GRFEditor()
|
||||
public GameRuleFileEditor()
|
||||
{
|
||||
InitializeComponent();
|
||||
PromptForCompressionType();
|
||||
}
|
||||
|
||||
public GRFEditor(PCKFile.FileData file) : this()
|
||||
private void PromptForCompressionType()
|
||||
{
|
||||
addCategory dialog = new addCategory(compressionTypeComboBox.Items.Cast<string>().ToArray());
|
||||
dialog.label2.Text = "Type";
|
||||
dialog.button1.Text = "Ok";
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
compressionTypeComboBox.SelectedIndex = compressionTypeComboBox.Items.IndexOf(dialog.Category);
|
||||
}
|
||||
|
||||
public GameRuleFileEditor(PCKFile.FileData file) : this()
|
||||
{
|
||||
_pckfile = file;
|
||||
using(var stream = new MemoryStream(file.Data))
|
||||
using (var stream = new MemoryStream(file.Data))
|
||||
{
|
||||
try
|
||||
{
|
||||
_file = GRFFileReader.Read(stream);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
MessageBox.Show("Faild to open .grf/.grh file");
|
||||
}
|
||||
_file = OpenGameRuleFile(stream, (GameRuleFile.CompressionType)compressionTypeComboBox.SelectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public GRFEditor(Stream stream) : this()
|
||||
public GameRuleFileEditor(Stream stream) : this()
|
||||
{
|
||||
_file = OpenGameRuleFile(stream, (GameRuleFile.CompressionType)compressionTypeComboBox.SelectedIndex);
|
||||
}
|
||||
|
||||
private GameRuleFile OpenGameRuleFile(Stream stream, GameRuleFile.CompressionType compressionType)
|
||||
{
|
||||
try
|
||||
{
|
||||
_file = GRFFileReader.Read(stream);
|
||||
var reader = new GameRuleFileReader(compressionType);
|
||||
return reader.FromStream(stream);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
Debug.WriteLine(ex.Message);
|
||||
MessageBox.Show("Faild to open .grf/.grh file");
|
||||
}
|
||||
return default!;
|
||||
}
|
||||
|
||||
private void OnLoad(object sender, EventArgs e)
|
||||
{
|
||||
RPC.SetPresence("GRF Editor", "Editing a GRF File");
|
||||
toolStripComboBox1.SelectedIndex = (int)_file.CompressionLevel;
|
||||
loadGRFTreeView(GrfTreeView.Nodes, _file.Root);
|
||||
ReloadGameRuleTree();
|
||||
}
|
||||
|
||||
private void loadGRFTreeView(TreeNodeCollection root, GRFFile.GameRule parentRule)
|
||||
private void LoadGameRuleTree(TreeNodeCollection root, GameRuleFile.GameRule parentRule)
|
||||
{
|
||||
foreach (var rule in parentRule.ChildRules)
|
||||
{
|
||||
TreeNode node = new TreeNode(rule.Name);
|
||||
node.Tag = rule;
|
||||
root.Add(node);
|
||||
loadGRFTreeView(node.Nodes, rule);
|
||||
LoadGameRuleTree(node.Nodes, rule);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReloadGameRuleTree()
|
||||
{
|
||||
GrfTreeView.Nodes.Clear();
|
||||
if (_file is not null)
|
||||
{
|
||||
toolStripComboBox1.SelectedIndex = (int)_file.FileHeader.CompressionLevel;
|
||||
LoadGameRuleTree(GrfTreeView.Nodes, _file.Root);
|
||||
}
|
||||
}
|
||||
|
||||
private void GrfTreeView_AfterSelect(object sender, TreeViewEventArgs e)
|
||||
{
|
||||
if (e.Node is TreeNode t && t.Tag is GRFFile.GameRule)
|
||||
if (e.Node is TreeNode t && t.Tag is GameRuleFile.GameRule)
|
||||
ReloadParameterTreeView();
|
||||
}
|
||||
|
||||
private void ReloadParameterTreeView()
|
||||
{
|
||||
GrfParametersTreeView.Nodes.Clear();
|
||||
if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GRFFile.GameRule rule)
|
||||
if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GameRuleFile.GameRule rule)
|
||||
foreach (var param in rule.Parameters)
|
||||
{
|
||||
GrfParametersTreeView.Nodes.Add(new TreeNode($"{param.Key}: {param.Value}") { Tag = param});
|
||||
@@ -88,8 +108,8 @@ namespace PckStudio.Forms.Editor
|
||||
|
||||
private void addDetailContextMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (GrfTreeView.SelectedNode == null || !(GrfTreeView.SelectedNode.Tag is GRFFile.GameRule)) return;
|
||||
var grfTag = GrfTreeView.SelectedNode.Tag as GRFFile.GameRule;
|
||||
if (GrfTreeView.SelectedNode == null || !(GrfTreeView.SelectedNode.Tag is GameRuleFile.GameRule)) return;
|
||||
var grfTag = GrfTreeView.SelectedNode.Tag as GameRuleFile.GameRule;
|
||||
AddParameter prompt = new AddParameter();
|
||||
if (prompt.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
@@ -105,7 +125,7 @@ namespace PckStudio.Forms.Editor
|
||||
|
||||
private void removeToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GRFFile.GameRule rule &&
|
||||
if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GameRuleFile.GameRule rule &&
|
||||
GrfParametersTreeView.SelectedNode is TreeNode paramNode && paramNode.Tag is KeyValuePair<string, string> pair &&
|
||||
rule.Parameters.ContainsKey(pair.Key) && rule.Parameters.Remove(pair.Key))
|
||||
{
|
||||
@@ -123,7 +143,7 @@ namespace PckStudio.Forms.Editor
|
||||
|
||||
private void GrfDetailsTreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
|
||||
{
|
||||
if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GRFFile.GameRule rule &&
|
||||
if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GameRuleFile.GameRule rule &&
|
||||
GrfParametersTreeView.SelectedNode is TreeNode paramNode && paramNode.Tag is KeyValuePair<string, string> param)
|
||||
{
|
||||
AddParameter prompt = new AddParameter(param.Key, param.Value, false);
|
||||
@@ -137,9 +157,9 @@ namespace PckStudio.Forms.Editor
|
||||
|
||||
private void addGameRuleToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
bool isValidNode = GrfTreeView.SelectedNode is TreeNode t && t.Tag is GRFFile.GameRule;
|
||||
GRFFile.GameRule parentRule = isValidNode
|
||||
? GrfTreeView.SelectedNode.Tag as GRFFile.GameRule
|
||||
bool isValidNode = GrfTreeView.SelectedNode is TreeNode t && t.Tag is GameRuleFile.GameRule;
|
||||
var parentRule = isValidNode
|
||||
? GrfTreeView.SelectedNode.Tag as GameRuleFile.GameRule
|
||||
: _file.Root;
|
||||
|
||||
TreeNodeCollection root = isValidNode
|
||||
@@ -164,11 +184,11 @@ namespace PckStudio.Forms.Editor
|
||||
|
||||
private void removeGameRuleToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GRFFile.GameRule tag && removeTag(tag))
|
||||
if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GameRuleFile.GameRule tag && removeTag(tag))
|
||||
t.Remove();
|
||||
}
|
||||
|
||||
private bool removeTag(GRFFile.GameRule rule)
|
||||
private bool removeTag(GameRuleFile.GameRule rule)
|
||||
{
|
||||
_ = rule.Parent ?? throw new ArgumentNullException(nameof(rule.Parent));
|
||||
foreach (var subTag in rule.ChildRules.ToList())
|
||||
@@ -184,7 +204,7 @@ namespace PckStudio.Forms.Editor
|
||||
|
||||
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_file.IsWorld)
|
||||
if (_file.FileHeader.unknownData[3] != 0)
|
||||
{
|
||||
MessageBox.Show("World grf saving is currently unsupported");
|
||||
return;
|
||||
@@ -193,7 +213,11 @@ namespace PckStudio.Forms.Editor
|
||||
{
|
||||
try
|
||||
{
|
||||
GRFFileWriter.Write(stream, _file, (GRFFile.CompressionType)toolStripComboBox1.SelectedIndex/*GRFFile.eCompressionType.ZlibRleCrc*/);
|
||||
var writer = new GameRuleFileWriter(
|
||||
_file,
|
||||
(GameRuleFile.CompressionLevel)toolStripComboBox1.SelectedIndex,
|
||||
(GameRuleFile.CompressionType)compressionTypeComboBox.SelectedIndex);
|
||||
writer.WriteToStream(stream);
|
||||
_pckfile?.SetData(stream.ToArray());
|
||||
MessageBox.Show("Saved!");
|
||||
}
|
||||
@@ -213,5 +237,20 @@ namespace PckStudio.Forms.Editor
|
||||
// good enough
|
||||
metroLabel2.Location = new Point(metroPanel1.Size.Width / 2 + 25, metroLabel2.Location.Y);
|
||||
}
|
||||
|
||||
private void openToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
OpenFileDialog dialog = new OpenFileDialog();
|
||||
dialog.Filter = "Game Rule File|*.grf";
|
||||
PromptForCompressionType();
|
||||
if (dialog.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
using (var fs = File.OpenRead(dialog.FileName))
|
||||
{
|
||||
_file = OpenGameRuleFile(fs, (GameRuleFile.CompressionType)compressionTypeComboBox.SelectedIndex);
|
||||
ReloadGameRuleTree();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,12 @@ using System.Drawing.Drawing2D;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
using OMI.Formats.GameRule;
|
||||
using OMI.Workers.GameRule;
|
||||
using PckStudio.Properties;
|
||||
using PckStudio.Classes.FileTypes;
|
||||
using PckStudio.Classes.IO.LOC;
|
||||
using PckStudio.Classes.IO.PCK;
|
||||
using PckStudio.Classes.IO.GRF;
|
||||
using PckStudio.Classes.Utils;
|
||||
using PckStudio.Classes.Utils.ARC;
|
||||
using PckStudio.Classes._3ds.Utils;
|
||||
@@ -372,7 +373,7 @@ namespace PckStudio
|
||||
|
||||
private void HandleGameRuleFile(PCKFile.FileData file)
|
||||
{
|
||||
using GRFEditor grfEditor = new GRFEditor(file);
|
||||
using GameRuleFileEditor grfEditor = new GameRuleFileEditor(file);
|
||||
wasModified = grfEditor.ShowDialog(this) == DialogResult.OK;
|
||||
UpdateRPC();
|
||||
}
|
||||
@@ -1256,7 +1257,7 @@ namespace PckStudio
|
||||
{
|
||||
var newPck = InitializeTexturePack(packId, packVersion, packName, res, true);
|
||||
var gameRuleFile = newPck.CreateNew("GameRules.grf", PCKFile.FileData.FileType.GameRulesFile);
|
||||
var grfFile = new GRFFile();
|
||||
var grfFile = new GameRuleFile();
|
||||
grfFile.AddRule("MapOptions",
|
||||
new KeyValuePair<string, string>("seed", "0"),
|
||||
new KeyValuePair<string, string>("baseSaveName", string.Empty),
|
||||
@@ -1272,8 +1273,9 @@ namespace PckStudio
|
||||
new KeyValuePair<string, string>("spawnZ", "0")
|
||||
);
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
GRFFileWriter.Write(stream, grfFile, GRFFile.CompressionType.None);
|
||||
{
|
||||
var writer = new GameRuleFileWriter(grfFile);
|
||||
writer.WriteToStream(stream);
|
||||
gameRuleFile.SetData(stream.ToArray());
|
||||
}
|
||||
return newPck;
|
||||
|
||||
@@ -176,7 +176,6 @@
|
||||
<Compile Include="Classes\FileTypes\PCKAudioFile.cs" />
|
||||
<Compile Include="Classes\FileTypes\Binka.cs" />
|
||||
<Compile Include="Classes\FileTypes\CSM.cs" />
|
||||
<Compile Include="Classes\FileTypes\GRFFile.cs" />
|
||||
<Compile Include="Classes\IO\CSMB\CSMBFileReader.cs" />
|
||||
<Compile Include="Classes\IO\CSMB\CSMBFileWriter.cs" />
|
||||
<Compile Include="Classes\IO\Materials\MaterialsReader.cs" />
|
||||
@@ -195,8 +194,6 @@
|
||||
<Compile Include="Classes\Utils\SkinANIM.cs" />
|
||||
<Compile Include="Classes\FileTypes\PCKProperties.cs" />
|
||||
<Compile Include="Classes\FileTypes\PCKFile.cs" />
|
||||
<Compile Include="Classes\IO\GRF\GRFFileReader.cs" />
|
||||
<Compile Include="Classes\IO\GRF\GRFFileWriter.cs" />
|
||||
<Compile Include="Classes\IO\LOC\LOCFileReader.cs" />
|
||||
<Compile Include="Classes\IO\LOC\LOCFileWriter.cs" />
|
||||
<Compile Include="Classes\IO\PCK\PCKFileReader.cs" />
|
||||
@@ -232,8 +229,6 @@
|
||||
<Compile Include="Classes\Networking\Network.cs" />
|
||||
<Compile Include="Classes\Misc\RichPresenceClient.cs" />
|
||||
<Compile Include="Classes\Networking\Update.cs" />
|
||||
<Compile Include="Classes\Utils\GRF\CRC32.cs" />
|
||||
<Compile Include="Classes\Utils\RLE.cs" />
|
||||
<Compile Include="Forms\Additional-Popups\EntityForms\AddEntry.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
@@ -342,11 +337,11 @@
|
||||
<Compile Include="Forms\Skins-And-Textures\AdvancedOptions.Designer.cs">
|
||||
<DependentUpon>AdvancedOptions.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Forms\Editor\GRFEditor.cs">
|
||||
<Compile Include="Forms\Editor\GameRuleFileEditor.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Forms\Editor\GRFEditor.Designer.cs">
|
||||
<DependentUpon>GRFEditor.cs</DependentUpon>
|
||||
<Compile Include="Forms\Editor\GameRuleFileEditor.Designer.cs">
|
||||
<DependentUpon>GameRuleFileEditor.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Forms\Additional-Popups\Grf\AddParameter.cs">
|
||||
<SubType>Form</SubType>
|
||||
@@ -561,8 +556,8 @@
|
||||
<DependentUpon>AdvancedOptions.cs</DependentUpon>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Forms\Editor\GRFEditor.resx">
|
||||
<DependentUpon>GRFEditor.cs</DependentUpon>
|
||||
<EmbeddedResource Include="Forms\Editor\GameRuleFileEditor.resx">
|
||||
<DependentUpon>GameRuleFileEditor.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Forms\Additional-Popups\Grf\AddParameter.resx">
|
||||
<DependentUpon>AddParameter.cs</DependentUpon>
|
||||
@@ -772,9 +767,6 @@
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>13.0.2</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SharpZipLib">
|
||||
<Version>1.4.1</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.ValueTuple">
|
||||
<Version>4.5.0</Version>
|
||||
</PackageReference>
|
||||
|
||||
2
Vendor/OMI-Lib
vendored
2
Vendor/OMI-Lib
vendored
Submodule Vendor/OMI-Lib updated: e2fa983abc...16280e2f0a
Reference in New Issue
Block a user