diff --git a/src/Lumina/Excel/ExcelModule.cs b/src/Lumina/Excel/ExcelModule.cs
index 706f3820..76aab28f 100644
--- a/src/Lumina/Excel/ExcelModule.cs
+++ b/src/Lumina/Excel/ExcelModule.cs
@@ -79,19 +79,10 @@ public ExcelModule( GameData gameData )
/// that may be created anew or reused from a previous invocation of this method.
///
/// Sheet was not a .
- ///
+ ///
public ExcelSheet< T > GetSheet< T >( Language? language = null, string? name = null ) where T : struct, IExcelRow< T >
{
- var attr = GetSheetAttributes( typeof( T ) ) ?? throw new SheetAttributeMissingException( null, nameof( T ) );
- name ??= attr.Name ?? throw new SheetNameEmptyException( nameof( name ) );
-
- var rawSheet = GetRawSheetCore( name, language, out var variant );
-
- if( VerifySheetChecksums && attr?.ColumnHash is { } hash && hash != rawSheet.ColumnHash )
- throw new MismatchedColumnHashException( hash, rawSheet.ColumnHash, nameof( T ) );
-
- if( variant != ExcelVariant.Default )
- throw new NotSupportedException( $"Specified sheet variant {variant} is not supported; was expecting {ExcelVariant.Default}." );
+ var rawSheet = GetRawSheet( language, name );
return new ExcelSheet< T >( rawSheet );
}
@@ -106,18 +97,9 @@ public ExcelSheet< T > GetSheet< T >( Language? language = null, string? name =
///
public SubrowExcelSheet< T > GetSubrowSheet< T >( Language? language = null, string? name = null ) where T : struct, IExcelSubrow< T >
{
- var attr = GetSheetAttributes( typeof( T ) ) ?? throw new SheetAttributeMissingException( null, nameof( T ) );
- name ??= attr.Name ?? throw new SheetNameEmptyException( nameof( name ) );
-
- var rawSheet = GetRawSheetCore( name, language, out var variant );
-
- if( VerifySheetChecksums && attr?.ColumnHash is { } hash && hash != rawSheet.ColumnHash )
- throw new MismatchedColumnHashException( hash, rawSheet.ColumnHash, nameof( T ) );
-
- if ( variant != ExcelVariant.Subrows )
- throw new NotSupportedException( $"Specified sheet variant {variant} is not supported; was expecting {ExcelVariant.Subrows}." );
+ var rawSheet = GetRawSubrowSheet( language, name );
- return new SubrowExcelSheet< T >( (RawSubrowExcelSheet)rawSheet );
+ return new SubrowExcelSheet< T >( rawSheet );
}
/// Loads a typed .
@@ -176,6 +158,56 @@ public IExcelSheet GetBaseSheet( Type rowType, Language? language = null, string
throw new InvalidOperationException( "Something went wrong" );
}
+ /// Loads a .
+ /// The requested sheet language. Leave or empty to use the default language.
+ /// The requested explicit sheet name. Leave to use 's sheet name. Explicit names are necessary for quest/dungeon/cutscene sheets.
+ /// An excel sheet corresponding to , , and
+ /// that may be created anew or reused from a previous invocation of this method.
+ ///
+ /// Sheet was not a .
+ ///
+ [EditorBrowsable( EditorBrowsableState.Advanced )]
+ public RawExcelSheet GetRawSheet( Language? language = null, string? name = null ) where T : struct, IExcelRow
+ {
+ var attr = GetSheetAttributes( typeof( T ) ) ?? throw new SheetAttributeMissingException( null, nameof( T ) );
+ name ??= attr.Name ?? throw new SheetNameEmptyException( nameof( name ) );
+
+ var rawSheet = GetRawSheetCore( name, language, out var variant );
+
+ if( VerifySheetChecksums && attr?.ColumnHash is { } hash && hash != rawSheet.ColumnHash )
+ throw new MismatchedColumnHashException( hash, rawSheet.ColumnHash, nameof( T ) );
+
+ if( variant != ExcelVariant.Default )
+ throw new NotSupportedException( $"Specified sheet variant {variant} is not supported; was expecting {ExcelVariant.Default}." );
+
+ return rawSheet;
+ }
+
+ /// Loads an .
+ /// The requested sheet language. Leave or empty to use the default language.
+ /// The requested explicit sheet name. Leave to use 's sheet name. Explicit names are necessary for quest/dungeon/cutscene sheets.
+ /// An excel sheet corresponding to , , and
+ /// that may be created anew or reused from a previous invocation of this method.
+ ///
+ /// Sheet was not a .
+ ///
+ [EditorBrowsable( EditorBrowsableState.Advanced )]
+ public RawSubrowExcelSheet GetRawSubrowSheet( Language? language = null, string? name = null ) where T : struct, IExcelSubrow
+ {
+ var attr = GetSheetAttributes( typeof( T ) ) ?? throw new SheetAttributeMissingException( null, nameof( T ) );
+ name ??= attr.Name ?? throw new SheetNameEmptyException( nameof( name ) );
+
+ var rawSheet = GetRawSheetCore( name, language, out var variant );
+
+ if( VerifySheetChecksums && attr?.ColumnHash is { } hash && hash != rawSheet.ColumnHash )
+ throw new MismatchedColumnHashException( hash, rawSheet.ColumnHash, nameof( T ) );
+
+ if( variant != ExcelVariant.Subrows )
+ throw new NotSupportedException( $"Specified sheet variant {variant} is not supported; was expecting {ExcelVariant.Subrows}." );
+
+ return (RawSubrowExcelSheet)rawSheet;
+ }
+
/// Loads a .
/// The requested sheet name.
/// The requested sheet language. Leave or empty to use the default language.
diff --git a/src/Lumina/Excel/IExtendedExcelRow.cs b/src/Lumina/Excel/IExtendedExcelRow.cs
new file mode 100644
index 00000000..e96d6ddb
--- /dev/null
+++ b/src/Lumina/Excel/IExtendedExcelRow.cs
@@ -0,0 +1,41 @@
+using System;
+
+namespace Lumina.Excel;
+
+///
+/// An extended interface for to provide additional functionality and extension methods.
+///
+/// The type that implements the interface.
+public interface IExtendedExcelRow< T > : IExcelRow< T >, IEquatable< T > where T : struct, IExcelRow< T >, IExtendedExcelRow< T >, IEquatable< T >
+{
+ ///
+ /// Gets the containing the data for this row.
+ ///
+ ExcelPage Page { get; }
+
+ ///
+ /// Gets the row offset in the .
+ ///
+ uint Offset { get; }
+
+ bool IEquatable< T >.Equals( T other ) =>
+ Page == other.Page && RowId == other.RowId;
+
+ ///
+ /// Indicates whether and are equal.
+ ///
+ /// The left parameter.
+ /// The right parameter.
+ /// if and are equal; otherwise.
+ virtual static bool operator ==( T l, T r ) =>
+ l.Equals( r );
+
+ ///
+ /// Indicates whether and are not equal.
+ ///
+ /// The left parameter.
+ /// The right parameter.
+ /// if and are not equal; otherwise.
+ virtual static bool operator !=( T l, T r ) =>
+ !l.Equals( r );
+}
diff --git a/src/Lumina/Excel/IExtendedExcelSubrow.cs b/src/Lumina/Excel/IExtendedExcelSubrow.cs
new file mode 100644
index 00000000..6b4dc9e7
--- /dev/null
+++ b/src/Lumina/Excel/IExtendedExcelSubrow.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace Lumina.Excel;
+
+///
+/// An extended interface for to provide additional functionality and extension methods.
+///
+///
+public interface IExtendedExcelSubrow< T > : IExcelSubrow, IEquatable where T : struct, IExcelSubrow< T >, IExtendedExcelSubrow< T >, IEquatable
+{
+ ///
+ ExcelPage Page { get; }
+
+ ///
+ uint Offset { get; }
+
+ bool IEquatable.Equals( T other ) =>
+ Page == other.Page && RowId == other.RowId && SubrowId == other.SubrowId;
+
+ ///
+ virtual static bool operator ==( T l, T r ) =>
+ l.Equals( r );
+
+ ///
+ virtual static bool operator !=( T l, T r ) =>
+ !l.Equals( r );
+}
diff --git a/src/Lumina/Excel/RawExcelSheet.cs b/src/Lumina/Excel/RawExcelSheet.cs
index 8255080b..62d04557 100644
--- a/src/Lumina/Excel/RawExcelSheet.cs
+++ b/src/Lumina/Excel/RawExcelSheet.cs
@@ -200,6 +200,13 @@ public bool HasRow( uint rowId )
return !Unsafe.IsNullRef( in rowIndexRef );
}
+ [MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
+ internal T? GetRowOrDefault( uint rowId ) where T : struct, IExcelRow
+ {
+ ref readonly var lookup = ref GetRowLookupOrNullRef( rowId );
+ return Unsafe.IsNullRef( in lookup ) ? null : UnsafeCreateRow( in lookup );
+ }
+
/// Gets a row lookup at the given index, if possible.
/// Index of the desired row.
/// Lookup data for the desired row, or a null reference if no corresponding row exists.
diff --git a/src/Lumina/Excel/RawRow.cs b/src/Lumina/Excel/RawRow.cs
index 0e966ae0..ca365632 100644
--- a/src/Lumina/Excel/RawRow.cs
+++ b/src/Lumina/Excel/RawRow.cs
@@ -12,7 +12,7 @@ namespace Lumina.Excel;
/// This type is designed to be used to read from arbitrary columns and offsets.
///
[Sheet]
-public readonly struct RawRow( ExcelPage page, uint offset, uint row ) : IExcelRow
+public readonly struct RawRow( ExcelPage page, uint offset, uint row ) : IExcelRow, IExtendedExcelRow
{
///
/// The associated of the row.
diff --git a/src/Lumina/Excel/RawSubrowExcelSheet.cs b/src/Lumina/Excel/RawSubrowExcelSheet.cs
index e6a0ee72..a4e09700 100644
--- a/src/Lumina/Excel/RawSubrowExcelSheet.cs
+++ b/src/Lumina/Excel/RawSubrowExcelSheet.cs
@@ -46,4 +46,11 @@ public ushort GetSubrowCount( uint rowId )
ref readonly var lookup = ref GetRowLookupOrNullRef( rowId );
return Unsafe.IsNullRef( in lookup ) ? throw new ArgumentOutOfRangeException( nameof( rowId ), rowId, null ) : lookup.SubrowCount;
}
+
+ [MethodImpl( MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization )]
+ public SubrowCollection? GetRowOrDefault( uint rowId ) where T : struct, IExcelSubrow
+ {
+ ref readonly var lookup = ref GetRowLookupOrNullRef( rowId );
+ return Unsafe.IsNullRef( in lookup ) ? null : new( this, in lookup );
+ }
}
\ No newline at end of file
diff --git a/src/Lumina/Excel/RowRef{T}.cs b/src/Lumina/Excel/RowRef{T}.cs
index 119542d9..24d39134 100644
--- a/src/Lumina/Excel/RowRef{T}.cs
+++ b/src/Lumina/Excel/RowRef{T}.cs
@@ -12,12 +12,12 @@ namespace Lumina.Excel;
/// The associated language of the referenced row. Leave to use 's default language.
public struct RowRef< T >( ExcelModule? module, uint rowId, Language? language = null ) where T : struct, IExcelRow< T >
{
- private ExcelSheet< T >? _sheet = null;
- private ExcelSheet< T >? Sheet {
+ private RawExcelSheet? _sheet = null;
+ private RawExcelSheet? Sheet {
get {
if( module == null )
return null;
- return _sheet ??= module.GetSheet< T >(
+ return _sheet ??= module.GetRawSheet< T >(
language == Data.Language.None ?
null : // Use default language if null (or fall back to None)
language
@@ -52,7 +52,12 @@ private ExcelSheet< T >? Sheet {
///
/// Attempts to get the referenced row value. Is if does not exist in the sheet.
///
- public T? ValueNullable => Sheet?.GetRowOrDefault( rowId );
+ public T? ValueNullable => Sheet?.GetRowOrDefault( rowId );
+
+ public RowRef( RawExcelSheet sheet, uint rowId ) : this( sheet.Module, rowId, sheet.Language )
+ {
+ _sheet = sheet;
+ }
private readonly RowRef ToGeneric() => RowRef.Create< T >( module, rowId, language );
diff --git a/src/Lumina/Excel/SubrowCollection.cs b/src/Lumina/Excel/SubrowCollection.cs
index ce032a0d..70a59d34 100644
--- a/src/Lumina/Excel/SubrowCollection.cs
+++ b/src/Lumina/Excel/SubrowCollection.cs
@@ -12,14 +12,28 @@ namespace Lumina.Excel;
{
private readonly RawExcelSheet.RowOffsetLookup _lookup;
- internal SubrowCollection( SubrowExcelSheet< T > sheet, scoped ref readonly RawExcelSheet.RowOffsetLookup lookup )
+ internal SubrowCollection( RawSubrowExcelSheet sheet, scoped ref readonly RawExcelSheet.RowOffsetLookup lookup )
{
- Sheet = sheet;
+ _rawSheet = sheet;
_lookup = lookup;
}
+ [Obsolete( "Use RawSheet instead; Create an issue on GitHub if RawSheet does not fit your use case." )]
+ internal SubrowCollection( SubrowExcelSheet sheet, scoped ref readonly RawExcelSheet.RowOffsetLookup lookup )
+ {
+ _sheet = sheet;
+ _lookup = lookup;
+ }
+
+ private RawSubrowExcelSheet? _rawSheet { get; }
+ private SubrowExcelSheet? _sheet { get; }
+
+ /// Gets the associated raw sheet.
+ public RawSubrowExcelSheet RawSheet => _rawSheet ?? _sheet!.RawSheet;
+
/// Gets the associated sheet.
- public SubrowExcelSheet< T > Sheet { get; }
+ [Obsolete("Use RawSheet instead; Create an issue on GitHub if RawSheet does not fit your use case.")]
+ public SubrowExcelSheet Sheet => _sheet ?? new( RawSheet );
/// Gets the Row ID of the subrows contained within.
public uint RowId => _lookup.RowId;
@@ -35,7 +49,7 @@ public T this[ int index ] {
get {
ArgumentOutOfRangeException.ThrowIfNegative( index );
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual( index, Count );
- return Sheet.RawSheet.UnsafeCreateSubrow< T >( in _lookup, unchecked( (ushort) index ) );
+ return RawSheet.UnsafeCreateSubrow< T >( in _lookup, unchecked( (ushort) index ) );
}
}
@@ -61,7 +75,7 @@ public int IndexOf( T item )
if( item.RowId != RowId || item.SubrowId >= Count )
return -1;
- var row = Sheet.RawSheet.UnsafeCreateSubrow< T >( in _lookup, item.SubrowId );
+ var row = RawSheet.UnsafeCreateSubrow< T >( in _lookup, item.SubrowId );
return EqualityComparer< T >.Default.Equals( item, row ) ? item.SubrowId : -1;
}
@@ -76,7 +90,7 @@ public void CopyTo( T[] array, int arrayIndex )
if( Count > array.Length - arrayIndex )
throw new ArgumentException( "The number of elements in the source list is greater than the available space." );
for( var i = 0; i < Count; i++ )
- array[ arrayIndex++ ] = Sheet.RawSheet.UnsafeCreateSubrow< T >( in _lookup, unchecked( (ushort) i ) );
+ array[ arrayIndex++ ] = RawSheet.UnsafeCreateSubrow< T >( in _lookup, unchecked( (ushort) i ) );
}
///
@@ -105,7 +119,7 @@ public bool MoveNext()
// UnsafeCreateSubrow must be called only when the preconditions are validated.
// If it is to be called on-demand from get_Current, then it may end up being called with invalid parameters,
// so we create the instance in advance here.
- Current = subrowCollection.Sheet.RawSheet.UnsafeCreateSubrow< T >( in subrowCollection._lookup, unchecked( (ushort) _index ) );
+ Current = subrowCollection.RawSheet.UnsafeCreateSubrow< T >( in subrowCollection._lookup, unchecked( (ushort) _index ) );
return true;
}
diff --git a/src/Lumina/Excel/SubrowExcelSheet.cs b/src/Lumina/Excel/SubrowExcelSheet.cs
index 2629ac1a..bb89c658 100644
--- a/src/Lumina/Excel/SubrowExcelSheet.cs
+++ b/src/Lumina/Excel/SubrowExcelSheet.cs
@@ -46,7 +46,7 @@ public sealed class SubrowExcelSheet< T >( RawSubrowExcelSheet sheet ) : ISubrow
public SubrowCollection< T >? GetRowOrDefault( uint rowId )
{
ref readonly var lookup = ref RawSheet.GetRowLookupOrNullRef( rowId );
- return Unsafe.IsNullRef( in lookup ) ? null : new( this, in lookup );
+ return Unsafe.IsNullRef( in lookup ) ? null : new( RawSheet, in lookup );
}
///
@@ -64,7 +64,7 @@ public bool TryGetRow( uint rowId, out SubrowCollection< T > row )
return false;
}
- row = new( this, in lookup );
+ row = new( RawSheet, in lookup );
return true;
}
@@ -77,7 +77,7 @@ public bool TryGetRow( uint rowId, out SubrowCollection< T > row )
public SubrowCollection< T > GetRow( uint rowId )
{
ref readonly var lookup = ref RawSheet.GetRowLookupOrNullRef( rowId );
- return Unsafe.IsNullRef( in lookup ) ? throw new ArgumentOutOfRangeException( nameof( rowId ), rowId, null ) : new( this, in lookup );
+ return Unsafe.IsNullRef( in lookup ) ? throw new ArgumentOutOfRangeException( nameof( rowId ), rowId, null ) : new( RawSheet, in lookup );
}
///
@@ -91,7 +91,7 @@ public SubrowCollection< T > GetRowAt( int rowIndex )
ArgumentOutOfRangeException.ThrowIfNegative( rowIndex );
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual( rowIndex, RawSheet.OffsetLookupTable.Length );
- return new( this, in RawSheet.UnsafeGetRowLookupAt( rowIndex ) );
+ return new( RawSheet, in RawSheet.UnsafeGetRowLookupAt( rowIndex ) );
}
///
@@ -179,7 +179,7 @@ public T GetSubrowAt( int rowIndex, ushort subrowId )
public bool HasRow( uint rowId ) => RawSheet.HasRow( rowId );
///
- public bool Contains( SubrowCollection< T > item ) => ReferenceEquals( item.Sheet, this ) && RawSheet.HasRow( item.RowId );
+ public bool Contains( SubrowCollection< T > item ) => ReferenceEquals( item.RawSheet, this ) && RawSheet.HasRow( item.RowId );
///
public void CopyTo( SubrowCollection< T >[] array, int arrayIndex )
@@ -189,7 +189,7 @@ public void CopyTo( SubrowCollection< T >[] array, int arrayIndex )
if( Count > array.Length - arrayIndex )
throw new ArgumentException( "The number of elements in the source list is greater than the available space." );
foreach( var lookup in RawSheet.OffsetLookupTable )
- array[ arrayIndex++ ] = new( this, in lookup );
+ array[ arrayIndex++ ] = new( RawSheet, in lookup );
}
void ICollection< SubrowCollection< T > >.Add( SubrowCollection< T > item ) => throw new NotSupportedException();
@@ -228,7 +228,7 @@ public bool MoveNext()
// UnsafeGetRowLookupAt must be called only when the preconditions are validated.
// If it is to be called on-demand from get_Current, then it may end up being called with invalid parameters,
// so we create the instance in advance here.
- Current = new( sheet, in sheet.RawSheet.UnsafeGetRowLookupAt( _index ) );
+ Current = new( sheet.RawSheet, in sheet.RawSheet.UnsafeGetRowLookupAt( _index ) );
return true;
}
diff --git a/src/Lumina/Excel/SubrowRef{T}.cs b/src/Lumina/Excel/SubrowRef{T}.cs
index 489d100d..8fbddb51 100644
--- a/src/Lumina/Excel/SubrowRef{T}.cs
+++ b/src/Lumina/Excel/SubrowRef{T}.cs
@@ -12,12 +12,12 @@ namespace Lumina.Excel;
/// The associated language of the referenced row. Leave to use 's default language.
public struct SubrowRef< T >( ExcelModule? module, uint rowId, Language? language = null ) where T : struct, IExcelSubrow< T >
{
- private SubrowExcelSheet< T >? _sheet = null;
- private SubrowExcelSheet< T >? Sheet {
+ private RawSubrowExcelSheet? _sheet = null;
+ private RawSubrowExcelSheet? Sheet {
get {
if( module == null )
return null;
- return _sheet ??= module.GetSubrowSheet< T >(
+ return _sheet ??= module.GetRawSubrowSheet< T >(
language == Data.Language.None ?
null : // Use default language if null (or fall back to None)
language
@@ -54,6 +54,11 @@ private SubrowExcelSheet< T >? Sheet {
///
public SubrowCollection< T >? ValueNullable => Sheet?.GetRowOrDefault( rowId );
+ public SubrowRef( RawSubrowExcelSheet sheet, uint rowId ) : this(sheet.Module, rowId, sheet.Language)
+ {
+ _sheet = sheet;
+ }
+
private readonly RowRef ToGeneric() => RowRef.CreateSubrow< T >( module, rowId );
///
diff --git a/src/Lumina/Extensions/ExcelExtensions.cs b/src/Lumina/Extensions/ExcelExtensions.cs
new file mode 100644
index 00000000..aa202a8e
--- /dev/null
+++ b/src/Lumina/Extensions/ExcelExtensions.cs
@@ -0,0 +1,65 @@
+using Lumina.Excel;
+
+namespace Lumina.Extensions;
+
+///
+/// Extensions for , , and .
+///
+public static class RowExcelExtensions
+{
+ public static RowRef GetRowRef(this ExcelSheet sheet, uint rowId) where T : struct, IExcelRow
+ {
+ return new RowRef( sheet.RawSheet, rowId );
+ }
+
+ public static RowRef GetGenericRowRef( this ExcelSheet sheet, uint rowId ) where T : struct, IExcelRow
+ {
+ return RowRef.Create( sheet.Module, rowId, sheet.Language );
+ }
+
+ public static RowRef AsRowRef( this T row ) where T : struct, IExcelRow, IExtendedExcelRow
+ {
+ return new RowRef( row.Page.Sheet, row.RowId );
+ }
+
+ public static RowRef AsGenericRowRef( this T row ) where T : struct, IExcelRow, IExtendedExcelRow
+ {
+ return RowRef.Create( row.Page.Module, row.RowId, row.Page.Language );
+ }
+
+ public static RawRow AsRawRow( this T row ) where T : struct, IExcelRow, IExtendedExcelRow
+ {
+ return new RawRow( row.Page, row.Offset, row.RowId );
+ }
+}
+
+///
+/// Extensions for , , and .
+///
+public static class SubrowExcelExtensions
+{
+ public static SubrowRef GetRowRef( this SubrowExcelSheet sheet, uint rowId ) where T : struct, IExcelSubrow
+ {
+ return new SubrowRef( sheet.RawSheet, rowId );
+ }
+
+ public static RowRef GetGenericRowRef( this SubrowExcelSheet sheet, uint rowId ) where T : struct, IExcelSubrow
+ {
+ return RowRef.CreateSubrow( sheet.Module, rowId, sheet.Language );
+ }
+
+ public static SubrowRef AsRowRef( this T row ) where T : struct, IExcelSubrow, IExtendedExcelSubrow
+ {
+ return new SubrowRef( (RawSubrowExcelSheet)row.Page.Sheet, row.RowId );
+ }
+
+ public static RowRef AsGenericRowRef( this T row ) where T : struct, IExcelSubrow, IExtendedExcelSubrow
+ {
+ return RowRef.CreateSubrow( row.Page.Module, row.RowId, row.Page.Language );
+ }
+
+ public static RawSubrow AsRawRow( this T row ) where T : struct, IExcelSubrow, IExtendedExcelSubrow
+ {
+ return new RawSubrow( row.Page, row.Offset, row.RowId, row.SubrowId );
+ }
+}