Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xbfs: Series S/X support #60

Merged
merged 4 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions LibXboxOne.Tests/XbfsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void TestXbfsHeaderParsing()
public void TestXbfsHeaderRehash()
{
XbfsHeader header = GetHeader();
header.Files[0].Length = 123;
header.Files[0].BlockCount = 123;

Assert.False(header.IsHashValid);
header.Rehash();
Expand All @@ -45,7 +45,7 @@ public void TestXbfsOutOfBounds()

// Write a file entry past the known filenames array
header.Files[XbfsFile.XbfsFilenames.Length + 2] = new XbfsEntry(){
LBA=0, Length=1, Reserved=0
LBA=0, BlockCount=1, Reserved=0
};

// Call to ToString will print filenames and should
Expand Down
15 changes: 12 additions & 3 deletions LibXboxOne/NAND/XbfsEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,18 @@ namespace LibXboxOne.Nand
public struct XbfsEntry
{
/* 0x0 */ public uint LBA;
/* 0x4 */ public uint Length;
/* 0x4 */ public uint BlockCount;
/* 0x8 */ public ulong Reserved;

public long Length => BlockCount * XbfsFile.BlockSize;

public long Offset(XbfsFlavor flavor) {
var offset = LBA * XbfsFile.BlockSize;
if (flavor == XbfsFlavor.XboxSeries && offset >= XbfsFile.SeriesOffsetDiff)
offset -= XbfsFile.SeriesOffsetDiff;
return offset;
}

public override string ToString()
{
return ToString(false);
Expand All @@ -18,8 +27,8 @@ public override string ToString()
public string ToString(bool formatted)
{
var b = new StringBuilder();
b.Append($"LBA: 0x{LBA:X} (0x{LBA * 0x1000:X}), ");
b.Append($"Length: 0x{Length:X} (0x{Length * 0x1000:X}), ");
b.Append($"LBA: 0x{LBA:X} (One: 0x{Offset(XbfsFlavor.XboxOne):X} Series: 0x{Offset(XbfsFlavor.XboxSeries):X}), ");
b.Append($"Length: 0x{BlockCount:X} (0x{Length:X}), ");
b.Append($"Reserved: 0x{Reserved:X}");

return b.ToString();
Expand Down
72 changes: 52 additions & 20 deletions LibXboxOne/NAND/XbfsFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,25 @@

namespace LibXboxOne.Nand
{
public enum XbfsFlavor {
XboxOne,
XboxSeries,
Invalid,
}

public enum NandSize: ulong
{
EMMC_LOGICAL = 0x13B00_0000,
EMMC_PHYSICAL = 0x13C00_0000,
NVME_SERIES = 0x4000_0000,
}

public class XbfsFile
{
public static readonly int SeriesOffsetDiff = 0x6000;
public static readonly int BlockSize = 0x1000;
public static readonly int[] XbfsOffsets = { 0x10000, 0x810000, 0x820000 };
public static readonly int[] XbfsOffsetsXboxOne = { 0x1_0000, 0x81_0000, 0x82_0000 };
public static readonly int[] XbfsOffsetsXboxSeries = { 0x0, 0x1800_8000 };
public static string[] XbfsFilenames =
{
"1smcbl_a.bin", // 0
Expand Down Expand Up @@ -50,24 +65,26 @@ public class XbfsFile

private readonly IO _io;

public XbfsFlavor Flavor = XbfsFlavor.Invalid;
public int[] HeaderOffsets;
public List<XbfsHeader> XbfsHeaders;

public readonly string FilePath;
public long FileSize => _io.Stream.Length;

public XbfsFile(string path)
{
FilePath = path;
_io = new IO(path);
}

public static long FromLBA(uint lba)
{
return lba * BlockSize;
}

public static uint ToLBA(long offset)
{
return (uint)(offset / BlockSize);
public static XbfsFlavor FlavorFromSize(long size) {
return size switch
{
(long)NandSize.EMMC_LOGICAL or (long)NandSize.EMMC_PHYSICAL => XbfsFlavor.XboxOne,
(long)NandSize.NVME_SERIES => XbfsFlavor.XboxSeries,
_ => XbfsFlavor.Invalid,
};
}

/// <summary>
Expand Down Expand Up @@ -132,9 +149,17 @@ public static int GetFileindexForName(string name)

public bool Load()
{
Flavor = FlavorFromSize(FileSize);

HeaderOffsets = Flavor switch {
XbfsFlavor.XboxOne => XbfsOffsetsXboxOne,
XbfsFlavor.XboxSeries => XbfsOffsetsXboxSeries,
_ => throw new InvalidDataException($"Invalid xbfs filesize: {FileSize:X}"),
};

// read each XBFS header
XbfsHeaders = new List<XbfsHeader>();
foreach (int offset in XbfsOffsets)
foreach (int offset in HeaderOffsets)
{
_io.Stream.Position = offset;
var header = _io.Reader.ReadStruct<XbfsHeader>();
Expand All @@ -158,10 +183,10 @@ public long SeekToFile(string fileName)
if (idx >= XbfsHeaders[i].Files.Length)
continue;
var ent = XbfsHeaders[i].Files[idx];
if (ent.Length == 0)
if (ent.BlockCount == 0)
continue;
_io.Stream.Position = FromLBA(ent.LBA);
size = FromLBA(ent.Length);
_io.Stream.Position = ent.Offset(Flavor);
size = ent.Length;
}
return size;
}
Expand All @@ -178,8 +203,8 @@ public string GetXbfsInfo()
var ent = XbfsHeaders[i].Files[y];
if (ent.Length == 0)
continue;
long start = FromLBA(ent.LBA);
long length = FromLBA(ent.Length);
long start = ent.Offset(Flavor);
long length = ent.Length;
long end = start + length;
string addInfo = $"{end:X} {i}_{y}";
if (info.ContainsKey(start))
Expand All @@ -199,6 +224,11 @@ public string GetXbfsInfo()

public void ExtractXbfsData(string folderPath)
{
if (!Directory.Exists(folderPath)) {
Console.WriteLine($"Creating output folder for xbfs extraction '{folderPath}'");
Directory.CreateDirectory(folderPath);
}

var doneAddrs = new List<long>();
for (int i = 0; i < XbfsHeaders.Count; i++)
{
Expand All @@ -207,15 +237,15 @@ public void ExtractXbfsData(string folderPath)
for (int y = 0; y < XbfsHeaders[i].Files.Length; y++)
{
var ent = XbfsHeaders[i].Files[y];
if (ent.Length == 0)
if (ent.BlockCount == 0)
continue;

var xbfsFilename = GetFilenameForIndex(y);
string fileName = $"{FromLBA(ent.LBA):X}_{FromLBA(ent.Length):X}_{i}_{y}_{xbfsFilename}";
string fileName = $"{ent.Offset(Flavor):X}_{ent.Length:X}_{i}_{y}_{xbfsFilename ?? "unknown"}";

long read = 0;
long total = FromLBA(ent.Length);
_io.Stream.Position = FromLBA(ent.LBA);
long total = ent.Length;
_io.Stream.Position = ent.Offset(Flavor);

bool writeFile = true;
if (doneAddrs.Contains(_io.Stream.Position))
Expand Down Expand Up @@ -257,13 +287,15 @@ public string ToString(bool formatted)
{
var b = new StringBuilder();
b.AppendLine("XbfsFile");
b.AppendLine($"Flavor: {Flavor}");
b.AppendLine($"Size: 0x{FileSize:X} ({FileSize / 1024 / 1024} MB)");
b.AppendLine();
for (int i = 0; i < XbfsHeaders.Count; i++)
{
if(!XbfsHeaders[i].IsValid)
continue;

b.AppendLine($"XbfsHeader slot {i}: (0x{XbfsOffsets[i]:X})");
b.AppendLine($"XbfsHeader slot {i}: (0x{HeaderOffsets[i]:X})");
b.Append(XbfsHeaders[i].ToString(formatted));
}
return b.ToString();
Expand Down
2 changes: 1 addition & 1 deletion LibXboxOne/NAND/XbfsHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public string ToString(bool formatted)
b.AppendLineSpace(fmt + $"Reserved18: 0x{Reserved18:X}");
b.AppendLineSpace(fmt + $"Reserved3C0: {Reserved3C0.ToHexString()}");
b.AppendLineSpace(fmt + $"System XVID: {SystemXVID}");
b.AppendLineSpace(fmt + $"XBFS header hash: {Environment.NewLine}{fmt}{XbfsHash.ToHexString()}");
b.AppendLineSpace(fmt + $"XBFS header hash: {Environment.NewLine}{fmt}{XbfsHash.ToHexString()} (Valid: {IsHashValid})");
b.AppendLine();

for(int i = 0; i < Files.Length; i++)
Expand Down
Loading