Add mesh reading and conversion support
This commit is contained in:
parent
65dc96c4d6
commit
aa1277a4e1
25 changed files with 2212 additions and 9 deletions
332
LibDgf/Ps2/Vif/BinaryReaderVifExtensions.cs
Normal file
332
LibDgf/Ps2/Vif/BinaryReaderVifExtensions.cs
Normal file
|
@ -0,0 +1,332 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Vif
|
||||
{
|
||||
public static class BinaryReaderVifExtensions
|
||||
{
|
||||
public static VuFloat ReadPs2Float(this BinaryReader br)
|
||||
{
|
||||
return new VuFloat { Packed = br.ReadUInt32() };
|
||||
}
|
||||
|
||||
public static VuVector[] ReadBoundingBox(this BinaryReader br)
|
||||
{
|
||||
var box = new VuVector[8];
|
||||
for (int i = 0; i < box.Length; ++i)
|
||||
{
|
||||
box[i] = br.ReadV4_32();
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
public static VuVector ReadOneVifCodeUnpack(this BinaryReader br, VifCodeUnpack u)
|
||||
{
|
||||
switch (u.Vn)
|
||||
{
|
||||
case VifUnpackVnType.S:
|
||||
switch (u.Vl)
|
||||
{
|
||||
case VifUnpackVlType.L_32:
|
||||
return br.ReadS_32();
|
||||
case VifUnpackVlType.L_16:
|
||||
return u.Unsigned ? br.ReadS_16U() : br.ReadS_16S();
|
||||
case VifUnpackVlType.L_8:
|
||||
return u.Unsigned ? br.ReadS_8U() : br.ReadS_8S();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VifUnpackVnType.V2:
|
||||
switch (u.Vl)
|
||||
{
|
||||
case VifUnpackVlType.L_32:
|
||||
return br.ReadV2_32();
|
||||
case VifUnpackVlType.L_16:
|
||||
return u.Unsigned ? br.ReadV2_16U() : br.ReadV2_16S();
|
||||
case VifUnpackVlType.L_8:
|
||||
return u.Unsigned ? br.ReadV2_8U() : br.ReadV2_8S();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VifUnpackVnType.V3:
|
||||
switch (u.Vl)
|
||||
{
|
||||
case VifUnpackVlType.L_32:
|
||||
return br.ReadV3_32();
|
||||
case VifUnpackVlType.L_16:
|
||||
return u.Unsigned ? br.ReadV3_16U() : br.ReadV3_16S();
|
||||
case VifUnpackVlType.L_8:
|
||||
return u.Unsigned ? br.ReadV3_8U() : br.ReadV3_8S();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case VifUnpackVnType.V4:
|
||||
switch (u.Vl)
|
||||
{
|
||||
case VifUnpackVlType.L_32:
|
||||
return br.ReadV4_32();
|
||||
case VifUnpackVlType.L_16:
|
||||
return u.Unsigned ? br.ReadV4_16U() : br.ReadV4_16S();
|
||||
case VifUnpackVlType.L_8:
|
||||
return u.Unsigned ? br.ReadV4_8U() : br.ReadV4_8S();
|
||||
case VifUnpackVlType.L_5:
|
||||
return br.ReadV4_5();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
throw new ArgumentException("Invalid vn/vl combination", nameof(u));
|
||||
}
|
||||
|
||||
public static VuVector ReadV4_32(this BinaryReader br)
|
||||
{
|
||||
return new VuVector
|
||||
{
|
||||
X = new VuFloat { Packed = br.ReadUInt32() },
|
||||
Y = new VuFloat { Packed = br.ReadUInt32() },
|
||||
Z = new VuFloat { Packed = br.ReadUInt32() },
|
||||
W = new VuFloat { Packed = br.ReadUInt32() }
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV4_16U(this BinaryReader br)
|
||||
{
|
||||
return new VuVector
|
||||
{
|
||||
X = new VuFloat { Packed = br.ReadUInt16() },
|
||||
Y = new VuFloat { Packed = br.ReadUInt16() },
|
||||
Z = new VuFloat { Packed = br.ReadUInt16() },
|
||||
W = new VuFloat { Packed = br.ReadUInt16() },
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV4_16S(this BinaryReader br)
|
||||
{
|
||||
return new VuVector
|
||||
{
|
||||
X = new VuFloat { Packed = unchecked((uint)br.ReadInt16()) },
|
||||
Y = new VuFloat { Packed = unchecked((uint)br.ReadInt16()) },
|
||||
Z = new VuFloat { Packed = unchecked((uint)br.ReadInt16()) },
|
||||
W = new VuFloat { Packed = unchecked((uint)br.ReadInt16()) },
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV4_8U(this BinaryReader br)
|
||||
{
|
||||
return new VuVector
|
||||
{
|
||||
X = new VuFloat { Packed = br.ReadByte() },
|
||||
Y = new VuFloat { Packed = br.ReadByte() },
|
||||
Z = new VuFloat { Packed = br.ReadByte() },
|
||||
W = new VuFloat { Packed = br.ReadByte() },
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV4_8S(this BinaryReader br)
|
||||
{
|
||||
return new VuVector
|
||||
{
|
||||
X = new VuFloat { Packed = unchecked((uint)br.ReadSByte()) },
|
||||
Y = new VuFloat { Packed = unchecked((uint)br.ReadSByte()) },
|
||||
Z = new VuFloat { Packed = unchecked((uint)br.ReadSByte()) },
|
||||
W = new VuFloat { Packed = unchecked((uint)br.ReadSByte()) },
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV3_32(this BinaryReader br)
|
||||
{
|
||||
return new VuVector
|
||||
{
|
||||
X = new VuFloat { Packed = br.ReadUInt32() },
|
||||
Y = new VuFloat { Packed = br.ReadUInt32() },
|
||||
Z = new VuFloat { Packed = br.ReadUInt32() },
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV3_16U(this BinaryReader br)
|
||||
{
|
||||
return new VuVector
|
||||
{
|
||||
X = new VuFloat { Packed = br.ReadUInt16() },
|
||||
Y = new VuFloat { Packed = br.ReadUInt16() },
|
||||
Z = new VuFloat { Packed = br.ReadUInt16() },
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV3_16S(this BinaryReader br)
|
||||
{
|
||||
return new VuVector
|
||||
{
|
||||
X = new VuFloat { Packed = unchecked((uint)br.ReadInt16()) },
|
||||
Y = new VuFloat { Packed = unchecked((uint)br.ReadInt16()) },
|
||||
Z = new VuFloat { Packed = unchecked((uint)br.ReadInt16()) },
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV3_8U(this BinaryReader br)
|
||||
{
|
||||
return new VuVector
|
||||
{
|
||||
X = new VuFloat { Packed = br.ReadByte() },
|
||||
Y = new VuFloat { Packed = br.ReadByte() },
|
||||
Z = new VuFloat { Packed = br.ReadByte() },
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV3_8S(this BinaryReader br)
|
||||
{
|
||||
return new VuVector
|
||||
{
|
||||
X = new VuFloat { Packed = unchecked((uint)br.ReadSByte()) },
|
||||
Y = new VuFloat { Packed = unchecked((uint)br.ReadSByte()) },
|
||||
Z = new VuFloat { Packed = unchecked((uint)br.ReadSByte()) },
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV2_32(this BinaryReader br)
|
||||
{
|
||||
var x = new VuFloat { Packed = br.ReadUInt32() };
|
||||
var y = new VuFloat { Packed = br.ReadUInt32() };
|
||||
return new VuVector
|
||||
{
|
||||
X = x,
|
||||
Y = y,
|
||||
Z = x,
|
||||
W = y
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV2_16U(this BinaryReader br)
|
||||
{
|
||||
var x = new VuFloat { Packed = br.ReadUInt16() };
|
||||
var y = new VuFloat { Packed = br.ReadUInt16() };
|
||||
return new VuVector
|
||||
{
|
||||
X = x,
|
||||
Y = y,
|
||||
Z = x,
|
||||
W = y
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV2_16S(this BinaryReader br)
|
||||
{
|
||||
var x = new VuFloat { Packed = unchecked((uint)br.ReadInt16()) };
|
||||
var y = new VuFloat { Packed = unchecked((uint)br.ReadInt16()) };
|
||||
return new VuVector
|
||||
{
|
||||
X = x,
|
||||
Y = y,
|
||||
Z = x,
|
||||
W = y
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV2_8U(this BinaryReader br)
|
||||
{
|
||||
var x = new VuFloat { Packed = (uint)br.ReadByte() };
|
||||
var y = new VuFloat { Packed = (uint)br.ReadByte() };
|
||||
return new VuVector
|
||||
{
|
||||
X = x,
|
||||
Y = y,
|
||||
Z = x,
|
||||
W = y
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV2_8S(this BinaryReader br)
|
||||
{
|
||||
var x = new VuFloat { Packed = unchecked((uint)br.ReadSByte()) };
|
||||
var y = new VuFloat { Packed = unchecked((uint)br.ReadSByte()) };
|
||||
return new VuVector
|
||||
{
|
||||
X = x,
|
||||
Y = y,
|
||||
Z = x,
|
||||
W = y
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadS_32(this BinaryReader br)
|
||||
{
|
||||
var s = new VuFloat { Packed = br.ReadUInt32() };
|
||||
return new VuVector
|
||||
{
|
||||
X = s,
|
||||
Y = s,
|
||||
Z = s,
|
||||
W = s
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadS_16U(this BinaryReader br)
|
||||
{
|
||||
var s = new VuFloat { Packed = (uint)br.ReadUInt16() };
|
||||
return new VuVector
|
||||
{
|
||||
X = s,
|
||||
Y = s,
|
||||
Z = s,
|
||||
W = s
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadS_16S(this BinaryReader br)
|
||||
{
|
||||
var s = new VuFloat { Packed = unchecked((uint)br.ReadInt16()) };
|
||||
return new VuVector
|
||||
{
|
||||
X = s,
|
||||
Y = s,
|
||||
Z = s,
|
||||
W = s
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadS_8U(this BinaryReader br)
|
||||
{
|
||||
var s = new VuFloat { Packed = (uint)br.ReadByte() };
|
||||
return new VuVector
|
||||
{
|
||||
X = s,
|
||||
Y = s,
|
||||
Z = s,
|
||||
W = s
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadS_8S(this BinaryReader br)
|
||||
{
|
||||
var s = new VuFloat { Packed = unchecked((uint)br.ReadSByte()) };
|
||||
return new VuVector
|
||||
{
|
||||
X = s,
|
||||
Y = s,
|
||||
Z = s,
|
||||
W = s
|
||||
};
|
||||
}
|
||||
|
||||
public static VuVector ReadV4_5(this BinaryReader br)
|
||||
{
|
||||
uint rgba = br.ReadUInt16();
|
||||
return new VuVector
|
||||
{
|
||||
X = new VuFloat { Packed = (rgba << 3) & 0xf8 },
|
||||
Y = new VuFloat { Packed = (rgba >> 2) & 0xf8 },
|
||||
Z = new VuFloat { Packed = (rgba >> 7) & 0xf8 },
|
||||
W = new VuFloat { Packed = (rgba >> 8) & 0x80 },
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
53
LibDgf/Ps2/Vif/VifCode.cs
Normal file
53
LibDgf/Ps2/Vif/VifCode.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Vif
|
||||
{
|
||||
public struct VifCode
|
||||
{
|
||||
public uint Value;
|
||||
|
||||
public bool Interrupt
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Cmd & VifCodeCmd.Interrupt) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public VifCodeCmd Cmd
|
||||
{
|
||||
get
|
||||
{
|
||||
return (VifCodeCmd)(byte)(Value >> 24);
|
||||
}
|
||||
}
|
||||
|
||||
public VifCodeCmd CmdWithoutInterrupt
|
||||
{
|
||||
get
|
||||
{
|
||||
return Cmd & ~VifCodeCmd.Interrupt;
|
||||
}
|
||||
}
|
||||
|
||||
public byte Num
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)(Value >> 16);
|
||||
}
|
||||
}
|
||||
|
||||
public ushort Immediate
|
||||
{
|
||||
get
|
||||
{
|
||||
return (ushort)Value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsUnpack => (Cmd & VifCodeCmd.Unpack) == VifCodeCmd.Unpack;
|
||||
}
|
||||
}
|
32
LibDgf/Ps2/Vif/VifCodeCmd.cs
Normal file
32
LibDgf/Ps2/Vif/VifCodeCmd.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Vif
|
||||
{
|
||||
public enum VifCodeCmd : byte
|
||||
{
|
||||
Nop = 0b000_0000,
|
||||
StCycl = 0b000_0001,
|
||||
Offset = 0b000_0010,
|
||||
Base = 0b000_0011,
|
||||
Itop = 0b000_0100,
|
||||
StMod = 0b000_0101,
|
||||
MskPath3 = 0b000_0110,
|
||||
Mark = 0b000_0111,
|
||||
FlushE = 0b001_0000,
|
||||
Flush = 0b001_0001,
|
||||
FlushA = 0b001_0011,
|
||||
MsCal = 0b001_0100,
|
||||
MsCnt = 0b001_0111,
|
||||
MsCalF = 0b001_0101,
|
||||
StMask = 0b010_0000,
|
||||
StRow = 0b011_0000,
|
||||
StCol = 0b011_0001,
|
||||
Mpg = 0b100_1010,
|
||||
Direct = 0b101_0000,
|
||||
DirectHl = 0b101_0001,
|
||||
Unpack = 0b11_00000,
|
||||
Interrupt = 0b1000_0000
|
||||
}
|
||||
}
|
87
LibDgf/Ps2/Vif/VifCodeUnpack.cs
Normal file
87
LibDgf/Ps2/Vif/VifCodeUnpack.cs
Normal file
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Vif
|
||||
{
|
||||
public struct VifCodeUnpack
|
||||
{
|
||||
private uint Value;
|
||||
|
||||
public bool Interrupt
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Value & 0x80000000) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Mask
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Value & 0x10000000) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public VifUnpackVnType Vn
|
||||
{
|
||||
get
|
||||
{
|
||||
return (VifUnpackVnType)((Value >> 26) & 0x3);
|
||||
}
|
||||
}
|
||||
|
||||
public VifUnpackVlType Vl
|
||||
{
|
||||
get
|
||||
{
|
||||
return (VifUnpackVlType)((Value >> 24) & 0x3);
|
||||
}
|
||||
}
|
||||
|
||||
public byte Num
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)(Value >> 16);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Flag
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Value & 0x8000) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Unsigned
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Value & 0x4000) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public ushort Address
|
||||
{
|
||||
get
|
||||
{
|
||||
return (ushort)(Value & 0x3ff);
|
||||
}
|
||||
}
|
||||
|
||||
public static implicit operator VifCode(VifCodeUnpack v)
|
||||
{
|
||||
return new VifCode { Value = v.Value | ((uint)VifCodeCmd.Unpack << 24) };
|
||||
}
|
||||
|
||||
public static explicit operator VifCodeUnpack(VifCode v)
|
||||
{
|
||||
if ((v.Cmd & VifCodeCmd.Unpack) != VifCodeCmd.Unpack)
|
||||
throw new ArgumentException("Not an UNPACK code", nameof(v));
|
||||
return new VifCodeUnpack { Value = v.Value };
|
||||
}
|
||||
}
|
||||
}
|
243
LibDgf/Ps2/Vif/VifEmulator.cs
Normal file
243
LibDgf/Ps2/Vif/VifEmulator.cs
Normal file
|
@ -0,0 +1,243 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Vif
|
||||
{
|
||||
public class VifEmulator
|
||||
{
|
||||
public delegate void VuMpgActivate(ushort? address, bool waitGif);
|
||||
|
||||
VuVector[] memory;
|
||||
VifRegisters registers;
|
||||
bool maskPath3;
|
||||
|
||||
public VifEmulator()
|
||||
{
|
||||
memory = new VuVector[1024];
|
||||
for (int i = 0; i < memory.Length; ++i)
|
||||
{
|
||||
memory[i] = new VuVector();
|
||||
}
|
||||
registers = new VifRegisters();
|
||||
}
|
||||
|
||||
public VuVector[] VuMemory => memory;
|
||||
public VifRegisters Registers => registers;
|
||||
|
||||
public void Process(BinaryReader br, int dataLength, VuMpgActivate onVuMpgActivate)
|
||||
{
|
||||
var startPos = br.BaseStream.Position;
|
||||
var endPos = startPos + dataLength;
|
||||
while (br.BaseStream.Position < endPos)
|
||||
{
|
||||
var vifcode = new VifCode { Value = br.ReadUInt32() };
|
||||
registers.Code = vifcode;
|
||||
//if ((vifcode.CmdWithoutInterrupt & VifCodeCmd.Unpack) != VifCodeCmd.Unpack)
|
||||
//{
|
||||
// Console.WriteLine($"VIFcode: {vifcode.CmdWithoutInterrupt} NUM={vifcode.Num} IMMEDIATE={vifcode.Immediate:x4}");
|
||||
//}
|
||||
switch (vifcode.CmdWithoutInterrupt)
|
||||
{
|
||||
case VifCodeCmd.Nop:
|
||||
break;
|
||||
case VifCodeCmd.StCycl:
|
||||
registers.Cycle = vifcode.Immediate;
|
||||
break;
|
||||
case VifCodeCmd.Offset:
|
||||
registers.Ofst = (uint)(vifcode.Immediate & 0x3ff);
|
||||
registers.Stat_Dbf = false;
|
||||
registers.TopS = registers.Base;
|
||||
break;
|
||||
case VifCodeCmd.Base:
|
||||
registers.Base = (uint)(vifcode.Immediate & 0x3ff);
|
||||
break;
|
||||
case VifCodeCmd.Itop:
|
||||
registers.ITopS = (uint)(vifcode.Immediate & 0x3ff);
|
||||
break;
|
||||
case VifCodeCmd.StMod:
|
||||
registers.Mode = (uint)(vifcode.Immediate & 3);
|
||||
break;
|
||||
case VifCodeCmd.MskPath3:
|
||||
maskPath3 = (vifcode.Immediate & 0x8000) != 0;
|
||||
break;
|
||||
case VifCodeCmd.Mark:
|
||||
registers.Mark = vifcode.Immediate;
|
||||
break;
|
||||
case VifCodeCmd.FlushE:
|
||||
case VifCodeCmd.Flush:
|
||||
case VifCodeCmd.FlushA:
|
||||
// Microprogram is run synchronously in emulation
|
||||
break;
|
||||
case VifCodeCmd.MsCal:
|
||||
registers.DoubleBufferSwap();
|
||||
onVuMpgActivate?.Invoke(vifcode.Immediate, false);
|
||||
break;
|
||||
case VifCodeCmd.MsCnt:
|
||||
registers.DoubleBufferSwap();
|
||||
onVuMpgActivate?.Invoke(null, false);
|
||||
break;
|
||||
case VifCodeCmd.MsCalF:
|
||||
registers.DoubleBufferSwap();
|
||||
onVuMpgActivate?.Invoke(vifcode.Immediate, true);
|
||||
break;
|
||||
case VifCodeCmd.StMask:
|
||||
registers.Mask = br.ReadUInt32();
|
||||
break;
|
||||
case VifCodeCmd.StRow:
|
||||
registers.R[0] = br.ReadUInt32();
|
||||
registers.R[1] = br.ReadUInt32();
|
||||
registers.R[2] = br.ReadUInt32();
|
||||
registers.R[3] = br.ReadUInt32();
|
||||
break;
|
||||
case VifCodeCmd.StCol:
|
||||
registers.C[0] = br.ReadUInt32();
|
||||
registers.C[1] = br.ReadUInt32();
|
||||
registers.C[2] = br.ReadUInt32();
|
||||
registers.C[3] = br.ReadUInt32();
|
||||
break;
|
||||
case VifCodeCmd.Mpg:
|
||||
{
|
||||
if (!CheckAlignment(br, startPos, 8))
|
||||
throw new InvalidDataException("MPG data is not aligned.");
|
||||
//Console.WriteLine($"MPG load at 0x{vifcode.Immediate * 8:x4}");
|
||||
// Skip MPG since we don't have a VU to execute it on
|
||||
int skipLength = vifcode.Num;
|
||||
if (skipLength == 0) skipLength = 256;
|
||||
skipLength *= 8;
|
||||
br.BaseStream.Seek(skipLength, SeekOrigin.Current);
|
||||
break;
|
||||
}
|
||||
case VifCodeCmd.Direct:
|
||||
case VifCodeCmd.DirectHl:
|
||||
{
|
||||
if (!CheckAlignment(br, startPos, 16))
|
||||
throw new InvalidDataException("Direct data is not aligned.");
|
||||
//Console.WriteLine($"Direct transfer");
|
||||
// TODO: handle GIFtag
|
||||
int skipLength = vifcode.Immediate;
|
||||
if (skipLength == 0) skipLength = 65536;
|
||||
skipLength *= 16;
|
||||
br.BaseStream.Seek(skipLength, SeekOrigin.Current);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if ((vifcode.Cmd & VifCodeCmd.Unpack) == VifCodeCmd.Unpack)
|
||||
{
|
||||
ProcessVifCodeUnpack(br);
|
||||
AlignReader(br, startPos, 4);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidDataException("Invalid VIFcode command");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessVifCodeUnpack(BinaryReader br)
|
||||
{
|
||||
var vifcode = (VifCodeUnpack)registers.Code;
|
||||
registers.Num = vifcode.Num;
|
||||
//Console.WriteLine($"VIFcode: {VifCodeCmd.Unpack} vn={vifcode.Vn} vl={vifcode.Vl} NUM={vifcode.Num} ADDR={vifcode.Address:x4} FLG={vifcode.Flag} USN={vifcode.Unsigned} m={vifcode.Mask}");
|
||||
int addr = (int)((vifcode.Flag ? registers.TopS : 0) + vifcode.Address);
|
||||
int cycle = 0;
|
||||
bool isV4_5 = vifcode.Vn == VifUnpackVnType.V4 && vifcode.Vl == VifUnpackVlType.L_5;
|
||||
while (registers.Num > 0)
|
||||
{
|
||||
VuVector result = default;
|
||||
bool doSkip = false;
|
||||
bool doMode;
|
||||
bool doMask;
|
||||
if (registers.CycleCl >= registers.CycleWl)
|
||||
{
|
||||
doMode = true;
|
||||
doMask = false;
|
||||
// Skipping write
|
||||
if (cycle < registers.CycleWl)
|
||||
{
|
||||
// Write when under write limit
|
||||
result = br.ReadOneVifCodeUnpack(vifcode);
|
||||
}
|
||||
|
||||
if (cycle == registers.CycleWl - 1)
|
||||
{
|
||||
doSkip = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Filling write
|
||||
throw new NotImplementedException("Filling write not implemented");
|
||||
}
|
||||
|
||||
// Write result
|
||||
result = ApplyMaskAndMode(result, doMode && !isV4_5, doMask);
|
||||
memory[addr++] = result;
|
||||
--registers.Num;
|
||||
|
||||
// TODO: figure out the proper behavior for filling write
|
||||
if (doSkip)
|
||||
{
|
||||
addr += registers.CycleCl - registers.CycleWl;
|
||||
cycle = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
++cycle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VuVector ApplyMaskAndMode(VuVector vector, bool doMode, bool doMask)
|
||||
{
|
||||
if (!doMode && !doMask) return vector;
|
||||
|
||||
uint x = vector.X.Packed;
|
||||
uint y = vector.Y.Packed;
|
||||
uint z = vector.Z.Packed;
|
||||
uint w = vector.W.Packed;
|
||||
|
||||
if (doMask)
|
||||
{
|
||||
throw new NotImplementedException("Masking not implemented");
|
||||
}
|
||||
|
||||
if (doMode)
|
||||
{
|
||||
switch (registers.ModeMod)
|
||||
{
|
||||
case VifMode.None:
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException("Addition decompression write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
return new VuVector
|
||||
{
|
||||
X = new VuFloat { Packed = x },
|
||||
Y = new VuFloat { Packed = y },
|
||||
Z = new VuFloat { Packed = z },
|
||||
W = new VuFloat { Packed = w },
|
||||
};
|
||||
}
|
||||
|
||||
// Alignment in bytes
|
||||
static void AlignReader(BinaryReader br, long startPos, int alignment)
|
||||
{
|
||||
var read = br.BaseStream.Position - startPos;
|
||||
var aligned = (read + alignment - 1) / alignment * alignment;
|
||||
br.BaseStream.Seek(aligned - read, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
static bool CheckAlignment(BinaryReader br, long startPos, int alignment)
|
||||
{
|
||||
var read = br.BaseStream.Position - startPos;
|
||||
var aligned = (read + alignment - 1) / alignment * alignment;
|
||||
return read == aligned;
|
||||
}
|
||||
}
|
||||
}
|
13
LibDgf/Ps2/Vif/VifMode.cs
Normal file
13
LibDgf/Ps2/Vif/VifMode.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Vif
|
||||
{
|
||||
public enum VifMode : byte
|
||||
{
|
||||
None,
|
||||
Offset,
|
||||
Difference
|
||||
}
|
||||
}
|
71
LibDgf/Ps2/Vif/VifRegisters.cs
Normal file
71
LibDgf/Ps2/Vif/VifRegisters.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Vif
|
||||
{
|
||||
public class VifRegisters
|
||||
{
|
||||
public uint[] R { get; } = new uint[4];
|
||||
public uint[] C { get; } = new uint[4];
|
||||
public uint Cycle { get; set; }
|
||||
public uint Mask { get; set; }
|
||||
public uint Mode { get; set; }
|
||||
public uint ITop { get; set; }
|
||||
public uint ITopS { get; set; }
|
||||
public uint Base { get; set; }
|
||||
public uint Ofst { get; set; }
|
||||
public uint Top { get; set; }
|
||||
public uint TopS { get; set; }
|
||||
public uint Mark { get; set; }
|
||||
public uint Num { get; set; }
|
||||
public VifCode Code { get; set; }
|
||||
|
||||
// Just this flag because the other ones are not that interesting
|
||||
public bool Stat_Dbf { get; set; }
|
||||
|
||||
public byte CycleCl
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)Cycle;
|
||||
}
|
||||
set
|
||||
{
|
||||
Cycle = (Cycle & 0xffffff00) | value;
|
||||
}
|
||||
}
|
||||
|
||||
public byte CycleWl
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)(Cycle >> 8);
|
||||
}
|
||||
set
|
||||
{
|
||||
Cycle = (Cycle & 0xffff00ff) | ((uint)value << 8);
|
||||
}
|
||||
}
|
||||
|
||||
public VifMode ModeMod
|
||||
{
|
||||
get
|
||||
{
|
||||
return (VifMode)(Mode & 3);
|
||||
}
|
||||
set
|
||||
{
|
||||
Mode = (uint)value & 3;
|
||||
}
|
||||
}
|
||||
|
||||
public void DoubleBufferSwap()
|
||||
{
|
||||
ITop = ITopS;
|
||||
Top = TopS;
|
||||
TopS = Base + (Stat_Dbf ? Ofst : 0);
|
||||
Stat_Dbf = !Stat_Dbf;
|
||||
}
|
||||
}
|
||||
}
|
14
LibDgf/Ps2/Vif/VifUnpackVlType.cs
Normal file
14
LibDgf/Ps2/Vif/VifUnpackVlType.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Vif
|
||||
{
|
||||
public enum VifUnpackVlType
|
||||
{
|
||||
L_32,
|
||||
L_16,
|
||||
L_8,
|
||||
L_5
|
||||
}
|
||||
}
|
14
LibDgf/Ps2/Vif/VifUnpackVnType.cs
Normal file
14
LibDgf/Ps2/Vif/VifUnpackVnType.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Vif
|
||||
{
|
||||
public enum VifUnpackVnType
|
||||
{
|
||||
S,
|
||||
V2,
|
||||
V3,
|
||||
V4
|
||||
}
|
||||
}
|
33
LibDgf/Ps2/Vif/VuFloat.cs
Normal file
33
LibDgf/Ps2/Vif/VuFloat.cs
Normal file
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Vif
|
||||
{
|
||||
public struct VuFloat
|
||||
{
|
||||
public uint Packed;
|
||||
|
||||
public static implicit operator double(VuFloat f)
|
||||
{
|
||||
ulong sign = (f.Packed >> 31) & 1;
|
||||
ulong exponent = (f.Packed >> 23) & 0xff;
|
||||
ulong mantissa = f.Packed & 0x7fffff;
|
||||
ulong doubleValue;
|
||||
if (exponent == 0)
|
||||
{
|
||||
doubleValue = sign << 63;
|
||||
}
|
||||
else
|
||||
{
|
||||
doubleValue = (sign << 63) | ((exponent + 1023 - 127) << 52) | (mantissa << 29);
|
||||
}
|
||||
return BitConverter.ToDouble(BitConverter.GetBytes(doubleValue), 0);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ((double)this).ToString();
|
||||
}
|
||||
}
|
||||
}
|
19
LibDgf/Ps2/Vif/VuVector.cs
Normal file
19
LibDgf/Ps2/Vif/VuVector.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace LibDgf.Ps2.Vif
|
||||
{
|
||||
public struct VuVector
|
||||
{
|
||||
public VuFloat X;
|
||||
public VuFloat Y;
|
||||
public VuFloat Z;
|
||||
public VuFloat W;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"<{X}, {Y}, {Z}, {W}>";
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue