Skip to content

Commit

Permalink
Merge pull request #126 from Xeeynamo/feature/hdasset-detection
Browse files Browse the repository at this point in the history
HD Asset detection
  • Loading branch information
Rikux3 authored May 31, 2020
2 parents fdfff67 + 4da0952 commit 0176727
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 8 deletions.
32 changes: 30 additions & 2 deletions OpenKh.Common/Archives/HdAsset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,9 @@ public List<Entry> Entries
set => entries = value ?? throw new ArgumentNullException(nameof(Entries));
}

private HdAsset()
public HdAsset()
{
_header = new Header();
Stream = new MemoryStream();
Entries = new List<Entry>();
}
Expand Down Expand Up @@ -129,7 +130,34 @@ private Stream GetSubStreamCopy(Stream stream, int offset, int length)
return outStream;
}

public static HdAsset New() => new HdAsset();
public static HdAsset Read(Stream stream) => new HdAsset(stream);

public static bool IsValid(Stream stream)
{
const int MinimumPossibleSizeForHeader = 0x10;
const int EstimatedMaximumPossibleSizeForOriginalAsset = 32 * 1024 * 1024;
const int EstimatedMaximumPossibleRemasteredAssetCount = 1024;

if (stream.Length < MinimumPossibleSizeForHeader)
return false;

var originalAssetLength = stream.ReadInt32();
if (originalAssetLength > EstimatedMaximumPossibleSizeForOriginalAsset)
return false;

var assetCount = stream.ReadInt32();
if (assetCount >= EstimatedMaximumPossibleRemasteredAssetCount)
return false;

if (stream.ReadInt32() != 0)
return false;
if (stream.ReadInt32() != 0)
return false;

if (originalAssetLength + MinimumPossibleSizeForHeader > stream.Length)
return false;

return true;
}
}
}
27 changes: 27 additions & 0 deletions OpenKh.Game/DataContent/HdAssetContent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using OpenKh.Common.Archives;
using OpenKh.Game.Infrastructure;
using System.IO;

namespace OpenKh.Game.DataContent
{
public class HdAssetContent : IDataContent
{
private readonly IDataContent _innerDataContext;

public HdAssetContent(IDataContent innerDataContext)
{
_innerDataContext = innerDataContext;
}

public bool FileExists(string fileName) => _innerDataContext.FileExists(fileName);

public Stream FileOpen(string fileName)
{
var stream = _innerDataContext.FileOpen(fileName);
if (stream == null)
return null;

return HdAsset.Read(stream).Stream;
}
}
}
16 changes: 14 additions & 2 deletions OpenKh.Game/Infrastructure/Kernel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using OpenKh.Common;
using OpenKh.Common.Archives;
using OpenKh.Engine;
using OpenKh.Engine.Extensions;
using OpenKh.Engine.Renders;
Expand Down Expand Up @@ -40,7 +41,7 @@ public Kernel(IDataContent dataContent)
FontContext = new FontContext();
MessageProvider = new Kh2MessageProvider();
RegionId = DetectRegion(dataContent);
IsReMix = DetectReMix(dataContent, Region);
IsReMix = IsReMixFileExists(dataContent, Region);

// Load files in the same order as KH2 does
ObjEntries = LoadFile("00objentry.bin", stream => Objentry.Read(stream));
Expand Down Expand Up @@ -124,10 +125,21 @@ private static int DetectRegion(IDataContent dataContent)
throw new Exception("Unable to detect any region for the game. Some files are potentially missing.");
}

private static bool DetectReMix(IDataContent dataContent, string region)
public static bool IsReMixFileExists(IDataContent dataContent, string region)
{
var testFileName = $"menu/{region}/titlejf.2ld";
return dataContent.FileExists(testFileName);
}

public static bool IsReMixFileHasHdAssetHeader(IDataContent dataContent, string region)
{
var testFileName = $"menu/{region}/titlejf.2ld";
var stream = dataContent.FileOpen(testFileName);
if (stream == null)
return false;

using (stream)
return HdAsset.IsValid(stream);
}
}
}
6 changes: 5 additions & 1 deletion OpenKh.Game/OpenKhGame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ public int State

public OpenKhGame()
{
_dataContent = new SafeDataContent(CreateDataContent(".", "KH2.IDX", "KH2.IMG"));
_dataContent = CreateDataContent(".", "KH2.IDX", "KH2.IMG");
if (Kernel.IsReMixFileHasHdAssetHeader(_dataContent, "fm"))
_dataContent = new HdAssetContent(_dataContent);
_dataContent = new SafeDataContent(_dataContent);

_kernel = new Kernel(_dataContent);

var resolutionWidth = _kernel.IsReMix ?
Expand Down
68 changes: 65 additions & 3 deletions OpenKh.Tests/Archives/HdAssetTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using OpenKh.Common.Archives;
using OpenKh.Common;
using OpenKh.Common.Archives;
using System;
using System.IO;
using System.Linq;
using Xunit;

namespace OpenKh.Tests.Archives
Expand All @@ -24,13 +26,13 @@ public void WriteBackTheSameReadStream() => Helpers.UseAsset("ps4archive.bin", s
[Fact]
public void CannotAssignNullMainStream()
{
Assert.Throws<ArgumentNullException>(() => HdAsset.New().Stream = null);
Assert.Throws<ArgumentNullException>(() => new HdAsset().Stream = null);
}

[Fact]
public void CannotAssignNullEntriesListStream()
{
Assert.Throws<ArgumentNullException>(() => HdAsset.New().Entries = null);
Assert.Throws<ArgumentNullException>(() => new HdAsset().Entries = null);
}

[Fact]
Expand All @@ -41,5 +43,65 @@ public void CannotAssignNullEntryStreamListStream()
Stream = null
});
}

[Fact]
public void IsValid() => Helpers.UseAsset("ps4archive.bin", stream =>
{
Assert.True(HdAsset.IsValid(stream));
});

[Fact]
public void IsValidWhenTheMinimumNecessaryInformationAreThere()
{
var hdasset = new HdAsset();

using var stream = new MemoryStream();
hdasset.Write(stream);

Assert.True(HdAsset.IsValid(stream.SetPosition(0)));
}

[Fact]
public void IsNotValidWhenTheHeaderSizeIsTooSmall()
{
var stream = new MemoryStream(new byte[15]);
Assert.False(HdAsset.IsValid(stream));
}

[Fact]
public void IsNotValidWhenTheInnerStreamLengthIsTooBig()
{
var hdasset = new HdAsset();
hdasset.Stream = new MemoryStream(new byte[] { 2, 3, 4, 5, 6, 7, 8 });

using var stream = new MemoryStream();
hdasset.Write(stream);

stream.SetLength(stream.Length - 1);

Assert.False(HdAsset.IsValid(stream.SetPosition(0)));
}

[Fact]
public void IsNotValidIfThereAreTooManyHdAssets()
{
var hdasset = new HdAsset();
hdasset.Stream = new MemoryStream(new byte[] { 2, 3, 4, 5, 6, 7, 8 });
hdasset.Entries = Enumerable
.Range(0, 1024)
.Select(x => new HdAsset.Entry()
{
Name = "Test",
Stream = new MemoryStream()
})
.ToList();

using var stream = new MemoryStream();
hdasset.Write(stream);

stream.SetLength(stream.Length - 1);

Assert.False(HdAsset.IsValid(stream.SetPosition(0)));
}
}
}

0 comments on commit 0176727

Please sign in to comment.