Skip to content

Commit

Permalink
Add CVX reading
Browse files Browse the repository at this point in the history
  • Loading branch information
IntelOrca committed Dec 26, 2023
1 parent 3efeff8 commit d758ee9
Show file tree
Hide file tree
Showing 9 changed files with 393 additions and 7 deletions.
75 changes: 75 additions & 0 deletions src/IntelOrca.Biohazard/AfsFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System;

namespace IntelOrca.Biohazard
{
public sealed class AfsFile
{
private AFSLib.AfsArchive _afsArchive;

public ReadOnlyMemory<byte> Data { get; }

public AfsFile(ReadOnlyMemory<byte> data)
{
Data = data;
if (AFSLib.AfsArchive.TryFromFile(data.ToArray(), out var archive))
{
_afsArchive = archive;
}
else
{
throw new ArgumentException("Invalid AFS data", nameof(data));
}
}

public ReadOnlyMemory<byte> GetFileData(int index)
{
return _afsArchive.Files[index].Data;
}

public ReadOnlyMemory<byte> GetFileData(string path)
{
foreach (var file in _afsArchive.Files)
{
if (file.Name == path)
{
return file.Data;
}
}
throw new ArgumentException("File not found", nameof(path));
}

public Builder ToBuilder()
{
return new Builder(Data);
}

public class Builder
{
private AFSLib.AfsArchive _afsArchive;

public Builder(ReadOnlyMemory<byte> data)
{
if (AFSLib.AfsArchive.TryFromFile(data.ToArray(), out var archive))
{
_afsArchive = archive;
}
else
{
throw new ArgumentException("Invalid AFS data", nameof(data));
}
}

public void Replace(int index, ReadOnlyMemory<byte> data) => Replace(index, data.ToArray());

public void Replace(int index, byte[] data)
{
_afsArchive.Files[index].Data = data;
}

public AfsFile ToAfsFile()
{
return new AfsFile(_afsArchive.ToBytes());
}
}
}
}
1 change: 1 addition & 0 deletions src/IntelOrca.Biohazard/BioVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ public enum BioVersion
Biohazard1_5,
Biohazard2,
Biohazard3,
BiohazardCv,
}
}
10 changes: 9 additions & 1 deletion src/IntelOrca.Biohazard/IntelOrca.Biohazard.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AFSLib" Version="1.1.1" />
<PackageReference Include="csharp-prs" Version="2.1.3" />
<PackageReference Include="NVorbis" Version="0.10.5" />
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.Text.Json" Version="6.0.6" />
<PackageReference Include="NVorbis" Version="0.10.5" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resources.Designer.cs">
Expand All @@ -22,4 +24,10 @@
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<NativeLibs Remove="PrsFile.cs" />
</ItemGroup>
<ItemGroup>
<NativeLibs Remove="AfsFile.cs" />
</ItemGroup>
</Project>
48 changes: 48 additions & 0 deletions src/IntelOrca.Biohazard/PrsFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;

namespace IntelOrca.Biohazard
{
public sealed class PrsFile
{
private readonly ReadOnlyMemory<byte> _compressed;
private ReadOnlyMemory<byte>? _uncompressed;
private object _sync = new object();

public ReadOnlyMemory<byte> Data => _compressed;

public static PrsFile Compress(ReadOnlyMemory<byte> uncompressed)
{
// var bufferSize = 0x1FFF;
var bufferSize = 0xFF;
var compressed = csharp_prs.Prs.Compress(uncompressed.ToArray(), bufferSize);
return new PrsFile(compressed);
}

public PrsFile(ReadOnlyMemory<byte> compressed)
{
_compressed = compressed;
}

public unsafe ReadOnlyMemory<byte> Uncompressed
{
get
{
if (_uncompressed == null)
{
lock (_sync)
{
if (_uncompressed == null)
{
var span = _compressed.Span;
fixed (byte* src = span)
{
_uncompressed = csharp_prs.Prs.Decompress(src, span.Length);
}
}
}
}
return _uncompressed.Value;
}
}
}
}
3 changes: 0 additions & 3 deletions src/IntelOrca.Biohazard/Room/IRdt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@ public interface IRdt
{
BioVersion Version { get; }
ReadOnlyMemory<byte> Data { get; }
EmbeddedEffectList EmbeddedEffects { get; }
IRdtBuilder ToBuilder();
}

public interface IRdtBuilder
{
EmbeddedEffectList EmbeddedEffects { get; set; }

IRdt ToRdt();
}
}
70 changes: 70 additions & 0 deletions src/IntelOrca.Biohazard/Room/RdtCv.Builder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.IO;

namespace IntelOrca.Biohazard.Room
{
public partial class RdtCv
{
public class Builder : IRdtBuilder
{
private readonly MemoryStream _ms;

public List<Item> Items { get; } = new List<Item>();
public List<Door> Doors { get; } = new List<Door>();

public Builder(byte[] data)
{
_ms = new MemoryStream(data);
}

public RdtCv ToRdt()
{
var br = new BinaryReader(_ms);
var bw = new BinaryWriter(_ms);
_ms.Position = 16;
_ms.Position = br.ReadInt32();
br.ReadInt32();
br.ReadInt32();
br.ReadInt32();
br.ReadInt32();
var items = br.ReadInt32();
br.ReadInt32();
br.ReadInt32();
var doors = br.ReadInt32();

_ms.Position = 256;
br.ReadInt32();
br.ReadInt32();
br.ReadInt32();
br.ReadInt32();
var itemCount = br.ReadInt32();
br.ReadInt32();
br.ReadInt32();
var doorCount = br.ReadInt32();

if (itemCount != Items.Count)
throw new Exception("Changing number of items is not supported");

if (doorCount != Doors.Count)
throw new Exception("Changing number of doors is not supported");

_ms.Position = items;
foreach (var item in Items)
{
bw.Write(item);
}

_ms.Position = doors;
foreach (var door in Doors)
{
bw.Write(door);
}

return new RdtCv(_ms.ToArray());
}

IRdt IRdtBuilder.ToRdt() => ToRdt();
}
}
}
90 changes: 90 additions & 0 deletions src/IntelOrca.Biohazard/Room/RdtCv.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using IntelOrca.Biohazard.Extensions;

namespace IntelOrca.Biohazard.Room
{
public partial class RdtCv : IRdt
{
public BioVersion Version => BioVersion.BiohazardCv;

public ReadOnlyMemory<byte> Data { get; }

public RdtCv(string path)
: this(File.ReadAllBytes(path))
{
}

public RdtCv(ReadOnlyMemory<byte> data)
{
Data = data;
}

public Builder ToBuilder()
{
var builder = new Builder(Data.ToArray());
builder.Doors.AddRange(Doors.ToArray());
builder.Items.AddRange(Items.ToArray());
return builder;
}

private int ScriptOffsetListOffset => Data.GetSafeSpan<int>(16, 1)[0];
private ReadOnlySpan<int> ScriptOffsets => Data.GetSafeSpan<int>(ScriptOffsetListOffset, 9);
private ReadOnlySpan<int> ScriptCounts => Data.GetSafeSpan<int>(256, 9);

public ReadOnlySpan<Item> Items => GetTable<Item>(4);
public ReadOnlySpan<Door> Doors => GetTable<Door>(7);

private ReadOnlySpan<T> GetTable<T>(int index) where T : struct
{
var offset = ScriptOffsets[index];
var count = ScriptCounts[index];
return Data.GetSafeSpan<T>(offset, count);
}

IRdtBuilder IRdt.ToBuilder() => ToBuilder();

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Item
{
public byte Unk00;
public byte Unk01;
public byte Unk02;
public byte Unk03;
public int Type;
public int Unk08;
public int X;
public int Y;
public int Z;
public short XRot;
public short YRot;
public short ZRot;
public short Unk1E;
public byte Unk20;
public byte Unk21;
public byte Unk22;
public byte Unk23;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Door
{
public byte Unk00;
public byte Unk01;
public byte Unk02;
public byte Unk03;
public int Unk04;
public int Unk08;
public int Unk0C;
public int Unk10;
public int Unk14;
public int Unk18;
public int Unk1C;
public byte Stage;
public byte Room;
public byte ExitId;
public byte Transition;
}
}
}
Loading

0 comments on commit d758ee9

Please sign in to comment.