mirror of
https://github.com/aaru-dps/Aaru.git
synced 2026-04-05 21:44:17 +00:00
129 lines
4.5 KiB
C#
129 lines
4.5 KiB
C#
// /***************************************************************************
|
|
// Aaru Data Preservation Suite
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Filename : DataMap.cs
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
//
|
|
// Component : Aaru.Decryption.Ngcw (GPL-3.0-or-later).
|
|
//
|
|
// --[ Description ] ----------------------------------------------------------
|
|
//
|
|
// FST-based data region map for classifying disc sectors as data or junk.
|
|
//
|
|
// --[ License ] --------------------------------------------------------------
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
// Copyright © 2019-2026 Natalia Portillo
|
|
// ****************************************************************************/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using Aaru.Helpers;
|
|
|
|
namespace Aaru.Decryption.Ngcw;
|
|
|
|
/// <summary>A contiguous region of file data on disc.</summary>
|
|
public readonly struct DataRegion : IComparable<DataRegion>
|
|
{
|
|
public readonly ulong Offset;
|
|
public readonly ulong Length;
|
|
|
|
public DataRegion(ulong offset, ulong length)
|
|
{
|
|
Offset = offset;
|
|
Length = length;
|
|
}
|
|
|
|
public int CompareTo(DataRegion other) => Offset.CompareTo(other.Offset);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorted map of file data regions parsed from a Nintendo GameCube/Wii FST.
|
|
/// Used to classify disc sectors as data (file content / system area) or potential junk.
|
|
/// </summary>
|
|
public static class DataMap
|
|
{
|
|
/// <summary>
|
|
/// Build a data region map from an FST (File System Table).
|
|
/// </summary>
|
|
/// <param name="fst">Raw FST bytes.</param>
|
|
/// <param name="dataStart">Base offset added to each file's offset (typically 0).</param>
|
|
/// <param name="addressShift">
|
|
/// Left-shift applied to the file offset field.
|
|
/// 0 for GameCube, 2 for Wii.
|
|
/// </param>
|
|
/// <returns>Sorted array of data regions, or null on error.</returns>
|
|
public static DataRegion[] BuildFromFst(byte[] fst, ulong dataStart, int addressShift)
|
|
{
|
|
if(fst == null || fst.Length < 12) return null;
|
|
|
|
var totalEntries = BigEndianBitConverter.ToUInt32(fst, 8);
|
|
|
|
if(totalEntries * 12 > (uint)fst.Length) return null;
|
|
|
|
List<DataRegion> regions = new();
|
|
|
|
for(uint i = 1; i < totalEntries; i++)
|
|
{
|
|
var entryOffset = (int)(i * 12);
|
|
|
|
if(fst[entryOffset] != 0) // not a file
|
|
continue;
|
|
|
|
ulong off = (ulong)BigEndianBitConverter.ToUInt32(fst, entryOffset + 4) << addressShift;
|
|
ulong len = BigEndianBitConverter.ToUInt32(fst, entryOffset + 8);
|
|
|
|
if(len == 0) continue;
|
|
|
|
regions.Add(new DataRegion(dataStart + off, len));
|
|
}
|
|
|
|
regions.Sort();
|
|
|
|
return regions.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if a byte range overlaps any data region using binary search.
|
|
/// </summary>
|
|
/// <param name="regions">Sorted data regions.</param>
|
|
/// <param name="offset">Start byte offset to check.</param>
|
|
/// <param name="length">Length of the range to check.</param>
|
|
/// <returns><c>true</c> if the range overlaps a data region.</returns>
|
|
public static bool IsDataRegion(DataRegion[] regions, ulong offset, ulong length)
|
|
{
|
|
if(regions == null || regions.Length == 0) return false;
|
|
|
|
var lo = 0;
|
|
int hi = regions.Length - 1;
|
|
|
|
while(lo <= hi)
|
|
{
|
|
int mid = lo + (hi - lo) / 2;
|
|
ulong end = regions[mid].Offset + regions[mid].Length;
|
|
|
|
if(end <= offset)
|
|
lo = mid + 1;
|
|
else if(regions[mid].Offset >= offset + length)
|
|
hi = mid - 1;
|
|
else
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
} |