mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-07-08 18:06:41 +00:00
XDVDFS FileWriter (#83)
This commit is contained in:
@@ -19,7 +19,7 @@ namespace SabreTools.Data.Models.XDVDFS
|
||||
/// Seemingly unused 8 bytes after the signature, should be zeroed
|
||||
/// </summary>
|
||||
/// <remarks>8 bytes</remarks>
|
||||
public byte[] Unusued8Bytes { get; set; } = new byte[8];
|
||||
public byte[] Unused8Bytes { get; set; } = new byte[8];
|
||||
|
||||
/// <summary>
|
||||
/// Version number of xblayout(?) tool used to master the filesystem
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace SabreTools.Data.Models.XDVDFS
|
||||
/// <summary>
|
||||
/// UInt32 size of the root directory descriptor in bytes
|
||||
/// </summary>
|
||||
/// <remarks>Little-endian</remarks>
|
||||
public uint RootSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.XDVDFS;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
#pragma warning disable IDE0017 // Simplify object initialization
|
||||
@@ -94,7 +95,7 @@ namespace SabreTools.Serialization.Readers
|
||||
var signature = System.Text.Encoding.ASCII.GetString(obj.Signature);
|
||||
if (!signature.Equals(Constants.LayoutDescriptorSignature))
|
||||
return null;
|
||||
obj.Unusued8Bytes = data.ReadBytes(8);
|
||||
obj.Unused8Bytes = data.ReadBytes(8);
|
||||
|
||||
obj.XBLayoutVersion = ParseFourPartVersionType(data);
|
||||
obj.XBPremasterVersion = ParseFourPartVersionType(data);
|
||||
@@ -208,7 +209,8 @@ namespace SabreTools.Serialization.Readers
|
||||
// If invalid record read or next descriptor cannot fit in the current sector, skip ahead
|
||||
if (dr is null || (data.Position - initialOffset) % Constants.SectorSize > (Constants.SectorSize - Constants.MinimumRecordLength))
|
||||
{
|
||||
data.SeekIfPossible(Constants.SectorSize - (int)((data.Position - initialOffset) % Constants.SectorSize), SeekOrigin.Current);
|
||||
int padding = Constants.SectorSize - (int)((data.Position - initialOffset) % Constants.SectorSize);
|
||||
obj.Padding = data.ReadBytes(padding);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -219,6 +221,7 @@ namespace SabreTools.Serialization.Readers
|
||||
|
||||
obj.DirectoryRecords = [.. records];
|
||||
|
||||
// Previously set padding assumed to be all 0xFF up to sector boundaries
|
||||
int remainder = Constants.SectorSize - (int)(size % Constants.SectorSize);
|
||||
if (remainder > 0 && remainder < Constants.SectorSize)
|
||||
obj.Padding = data.ReadBytes(remainder);
|
||||
@@ -235,11 +238,12 @@ namespace SabreTools.Serialization.Readers
|
||||
{
|
||||
var obj = new DirectoryRecord();
|
||||
|
||||
obj.LeftChildOffset = data.ReadUInt16LittleEndian();
|
||||
obj.RightChildOffset = data.ReadUInt16LittleEndian();
|
||||
if (obj.LeftChildOffset == 0xFFFF && obj.RightChildOffset == 0xFFFF)
|
||||
byte[] start = data.PeekBytes(4);
|
||||
if (start.EqualsExactly([0xFF, 0xFF, 0xFF, 0xFF]))
|
||||
return null;
|
||||
|
||||
obj.LeftChildOffset = data.ReadUInt16LittleEndian();
|
||||
obj.RightChildOffset = data.ReadUInt16LittleEndian();
|
||||
obj.ExtentOffset = data.ReadUInt32LittleEndian();
|
||||
obj.ExtentSize = data.ReadUInt32LittleEndian();
|
||||
obj.FileFlags = (FileFlags)data.ReadByteValue();
|
||||
|
||||
16
SabreTools.Serialization.Writers.Test/XDVDFSTests.cs
Normal file
16
SabreTools.Serialization.Writers.Test/XDVDFSTests.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System.IO;
|
||||
using Xunit;
|
||||
|
||||
namespace SabreTools.Serialization.Writers.Test
|
||||
{
|
||||
public class XDVDFSTests
|
||||
{
|
||||
[Fact]
|
||||
public void SerializeFile_Null_False()
|
||||
{
|
||||
var serializer = new XDVDFS();
|
||||
bool actual = serializer.SerializeFile(null, null);
|
||||
Assert.False(actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
135
SabreTools.Serialization.Writers/XDVDFS.cs
Normal file
135
SabreTools.Serialization.Writers/XDVDFS.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.Data.Models.XDVDFS;
|
||||
using SabreTools.IO;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Numerics;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Serialization.Writers
|
||||
{
|
||||
public class XDVDFS : IFileWriter<Volume>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Debug { get; set; } = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool SerializeFile(Volume? obj, string? path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return false;
|
||||
|
||||
if(obj is null || !ValidateVolume(obj))
|
||||
return false;
|
||||
|
||||
// Create the file stream
|
||||
using var fs = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
|
||||
SerializeHeader(fs, obj);
|
||||
|
||||
// Loop over all directory descriptors in order of offset
|
||||
uint[] keys = new uint[obj.DirectoryDescriptors.Count];
|
||||
obj.DirectoryDescriptors.Keys.CopyTo(keys, 0);
|
||||
Array.Sort(keys);
|
||||
|
||||
// Write directory descriptors
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
uint sectorOffset = keys[i];
|
||||
fs.Seek(sectorOffset * Constants.SectorSize, SeekOrigin.Begin);
|
||||
SerializeDirectoryDescriptor(fs, obj.DirectoryDescriptors[sectorOffset]);
|
||||
}
|
||||
|
||||
fs.Flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ValidateVolume(Volume? obj)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (obj?.VolumeDescriptor?.StartSignature is null)
|
||||
return false;
|
||||
|
||||
// If the magic doesn't match
|
||||
string magic = Encoding.ASCII.GetString(obj.VolumeDescriptor.StartSignature);
|
||||
if (magic != Constants.VolumeDescriptorSignature)
|
||||
return false;
|
||||
|
||||
// Validate model
|
||||
if (obj.ReservedArea.Length != 0x10000)
|
||||
return false;
|
||||
if (obj.VolumeDescriptor.Reserved.Length != 1991)
|
||||
return false;
|
||||
if (obj.VolumeDescriptor.EndSignature.Length != 20)
|
||||
return false;
|
||||
if (obj.LayoutDescriptor is not null)
|
||||
{
|
||||
if (obj.LayoutDescriptor.Signature.Length != 24)
|
||||
return false;
|
||||
if (obj.LayoutDescriptor.Unused8Bytes.Length != 8)
|
||||
return false;
|
||||
if (obj.LayoutDescriptor.Reserved.Length != 1968)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void SerializeHeader(Stream stream, Volume obj)
|
||||
{
|
||||
stream.Write(obj.ReservedArea, 0, obj.ReservedArea.Length);
|
||||
|
||||
stream.Write(obj.VolumeDescriptor.StartSignature, 0, obj.VolumeDescriptor.StartSignature.Length);
|
||||
stream.WriteLittleEndian(obj.VolumeDescriptor.RootOffset);
|
||||
stream.WriteLittleEndian(obj.VolumeDescriptor.RootSize);
|
||||
stream.WriteLittleEndian(obj.VolumeDescriptor.MasteringTimestamp);
|
||||
stream.WriteByte(obj.VolumeDescriptor.UnknownByte);
|
||||
stream.Write(obj.VolumeDescriptor.Reserved, 0, obj.VolumeDescriptor.Reserved.Length);
|
||||
stream.Write(obj.VolumeDescriptor.EndSignature, 0, obj.VolumeDescriptor.EndSignature.Length);
|
||||
|
||||
if (obj.LayoutDescriptor is not null)
|
||||
{
|
||||
stream.Write(obj.LayoutDescriptor.Signature, 0, obj.LayoutDescriptor.Signature.Length);
|
||||
stream.Write(obj.LayoutDescriptor.Unused8Bytes, 0, obj.LayoutDescriptor.Unused8Bytes.Length);
|
||||
SerializeFourPartVersionType(stream, obj.LayoutDescriptor.XBLayoutVersion);
|
||||
SerializeFourPartVersionType(stream, obj.LayoutDescriptor.XBPremasterVersion);
|
||||
SerializeFourPartVersionType(stream, obj.LayoutDescriptor.XBGameDiscVersion);
|
||||
SerializeFourPartVersionType(stream, obj.LayoutDescriptor.XBOther1Version);
|
||||
SerializeFourPartVersionType(stream, obj.LayoutDescriptor.XBOther2Version);
|
||||
SerializeFourPartVersionType(stream, obj.LayoutDescriptor.XBOther3Version);
|
||||
stream.Write(obj.LayoutDescriptor.Reserved, 0, obj.LayoutDescriptor.Reserved.Length);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SerializeFourPartVersionType(Stream stream, FourPartVersionType obj)
|
||||
{
|
||||
stream.WriteLittleEndian(obj.Major);
|
||||
stream.WriteLittleEndian(obj.Minor);
|
||||
stream.WriteLittleEndian(obj.Build);
|
||||
stream.WriteLittleEndian(obj.Revision);
|
||||
}
|
||||
|
||||
public static void SerializeDirectoryDescriptor(Stream stream, DirectoryDescriptor obj)
|
||||
{
|
||||
foreach (var dr in obj.DirectoryRecords)
|
||||
SerializeDirectoryRecord(stream, dr);
|
||||
if (obj.Padding is not null && obj.Padding.Length > 0)
|
||||
stream.Write(obj.Padding, 0, obj.Padding.Length);
|
||||
}
|
||||
|
||||
public static void SerializeDirectoryRecord(Stream stream, DirectoryRecord obj)
|
||||
{
|
||||
stream.WriteLittleEndian(obj.LeftChildOffset);
|
||||
stream.WriteLittleEndian(obj.RightChildOffset);
|
||||
stream.WriteLittleEndian(obj.ExtentOffset);
|
||||
stream.WriteLittleEndian(obj.ExtentSize);
|
||||
stream.WriteByte((byte)obj.FileFlags);
|
||||
stream.WriteByte(obj.FilenameLength);
|
||||
stream.Write(obj.Filename, 0, obj.Filename.Length);
|
||||
if (obj.Padding is not null && obj.Padding.Length > 0)
|
||||
stream.Write(obj.Padding, 0, obj.Padding.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ namespace SabreTools.Wrappers
|
||||
builder.AppendLine(" -------------------------");
|
||||
|
||||
builder.AppendLine(Encoding.ASCII.GetString(ld.Signature), " Signature");
|
||||
builder.AppendLine(ld.Unusued8Bytes, " Unusued 8 Bytes");
|
||||
builder.AppendLine(ld.Unused8Bytes, " Unusued 8 Bytes");
|
||||
builder.AppendLine(GetVersionString(ld.XBLayoutVersion), " xblayout Version");
|
||||
builder.AppendLine(GetVersionString(ld.XBPremasterVersion), " xbpremaster Version");
|
||||
builder.AppendLine(GetVersionString(ld.XBGameDiscVersion), " xbgamedisc Version");
|
||||
|
||||
30
SabreTools.Wrappers/XDVDFS.Writing.cs
Normal file
30
SabreTools.Wrappers/XDVDFS.Writing.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.Data.Models.XDVDFS;
|
||||
using SabreTools.Numerics.Extensions;
|
||||
|
||||
namespace SabreTools.Wrappers
|
||||
{
|
||||
public partial class XDVDFS : IWritable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Write(string outputDirectory, bool includeDebug)
|
||||
{
|
||||
// Get the base path
|
||||
string outputFilename = Filename is null
|
||||
? Guid.NewGuid().ToString()
|
||||
: Path.GetFileName(Filename);
|
||||
outputFilename += ".xiso";
|
||||
string outputPath = Path.Combine(outputDirectory, outputFilename);
|
||||
|
||||
var writer = new SabreTools.Serialization.Writers.XDVDFS();
|
||||
if (!writer.SerializeFile(Model, outputPath))
|
||||
{
|
||||
if (includeDebug) Console.WriteLine("Model was invalid, cannot write!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user