Skip to content

Commit

Permalink
Merge pull request #60 from emoose/xbfs/series_s_x_support
Browse files Browse the repository at this point in the history
Xbfs: Series S/X support
  • Loading branch information
tuxuser authored Aug 9, 2023
2 parents 893f73e + fc2cfaf commit dbae6ba
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 26 deletions.
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

0 comments on commit dbae6ba

Please sign in to comment.