From 3acf78f978b3fa8d12f42f26fad08c9cb4e9fb53 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 10 Oct 2023 15:23:48 -0400 Subject: [PATCH 1/4] initial stuff for exdschema --- src/Lumina/Excel/ExcelSheet.cs | 34 ++++++ src/Lumina/Excel/LinkUnion.cs | 210 +++++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 src/Lumina/Excel/LinkUnion.cs diff --git a/src/Lumina/Excel/ExcelSheet.cs b/src/Lumina/Excel/ExcelSheet.cs index cb7f11dd..e42b51ae 100644 --- a/src/Lumina/Excel/ExcelSheet.cs +++ b/src/Lumina/Excel/ExcelSheet.cs @@ -18,6 +18,16 @@ public ExcelSheet( ExcelHeaderFile headerFile, string name, Language requestedLa { } + public bool HasRow( uint row ) + { + return HasRow( row, UInt32.MaxValue ); + } + + public bool HasRow( uint row, uint subRow ) + { + return HasRowInternal( row, subRow ); + } + public T? GetRow( uint row ) { return GetRow( row, UInt32.MaxValue ); @@ -27,6 +37,30 @@ public ExcelSheet( ExcelHeaderFile headerFile, string name, Language requestedLa { return GetRowInternal( row, subRow ); } + + internal bool HasRowInternal( uint row, uint subRow ) + { + var cacheKey = GetCacheKey( row, subRow ); + + if( _rowCache.TryGetValue( cacheKey, out var cachedRow ) ) + { + return true; + } + + var page = GetPageForRow( row ); + if( page == null ) + { + return false; + } + + var parser = GetRowParser( page, row, subRow ); + if( parser == null ) + { + return false; + } + + return true; + } internal T? GetRowInternal( uint row, uint subRow ) { diff --git a/src/Lumina/Excel/LinkUnion.cs b/src/Lumina/Excel/LinkUnion.cs new file mode 100644 index 00000000..3d74f670 --- /dev/null +++ b/src/Lumina/Excel/LinkUnion.cs @@ -0,0 +1,210 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Lumina.Data; + +namespace Lumina.Excel; + +public class LinkUnion +{ + internal enum LinkUnionHandlerType + { + Match, + Switch, + } + + public class LinkUnionHandler + { + private LinkUnionHandlerType _type; + private LinkUnion _union; + + private HashSet<(uint, Type)> _caseTypesConsidered; + private HashSet<(uint, Type)> _caseTypesPermitted; + + private HashSet< Type > _typesConsidered; + private HashSet< Type > _typesPermitted; + + internal LinkUnionHandler( LinkUnionHandlerType type, LinkUnion union ) + { + _type = type; + _union = union; + _typesConsidered = new HashSet< Type >(); + _typesPermitted = _union.Cases.Select( h => h.Type ).ToHashSet(); + _caseTypesConsidered = new HashSet< (uint, Type) >(); + _caseTypesPermitted = _union.Cases.Select( h => (h.SwitchValue, h.Type) ).ToHashSet(); + } + + public LinkUnionHandler Case< T >( Action< LazyRow< T > > func ) where T : ExcelRow + { + var workingType = typeof( T ); + TypeCheck( workingType ); + + foreach( var handler in _union.Cases.Where( h => h.Type == workingType ) ) + { + if( handler.LazyRow is not LazyRow< T > lazyRow ) continue; + func( lazyRow ); + } + + _typesConsidered.Add( typeof( T ) ); + return this; + } + + public LinkUnionHandler Case< T >( uint switchValue, Action< LazyRow< T > > func ) where T : ExcelRow + { + var workingType = typeof( T ); + CaseCheck( switchValue, workingType ); + + foreach( var handler in _union.Cases.Where( h => h.SwitchValue == switchValue && h.Type == workingType ) ) + { + if( handler.LazyRow is not LazyRow< T > lazyRow ) continue; + func( lazyRow ); + } + + _caseTypesConsidered.Add( (switchValue, workingType) ); + return this; + } + + private void TypeCheck( Type type ) + { + if( _typesConsidered.Contains( type ) ) + { + throw new ArgumentException( $"Duplicate case for type {type.Name}" ); + } + + if( !_typesPermitted.Contains( type ) ) + { + throw new ArgumentException( $"Type {type.Name} is not permitted here" ); + } + } + + private void CaseCheck( uint value, Type type ) + { + if( _caseTypesConsidered.Contains( ( value, type ) ) ) + { + throw new ArgumentException( $"Duplicate case for value {value} {type}" ); + } + + if( !_caseTypesPermitted.Contains( (value, type) ) ) + { + throw new ArgumentException( $"Case {value}, {type} is not permitted here" ); + } + } + + public LinkUnionHandler Ignore< T >() where T : ExcelRow + { + _typesConsidered.Add( typeof( T ) ); + return this; + } + + public LinkUnionHandler Default( Action< ILazyRow > func ) + { + _typesConsidered.UnionWith( _typesPermitted ); + _caseTypesConsidered.UnionWith( _caseTypesPermitted ); + return this; + } + + public void Exhaust() + { + //TODO more on this + var unusedTypes = _typesPermitted.Except( _typesConsidered ); + var unusedCases = _caseTypesPermitted.Except( _caseTypesConsidered ); + + if( unusedTypes.Any() ) + { + throw new ArgumentException( $"Unmatched types: {string.Join( ", ", unusedTypes.Select( t => t.Name ) )}" ); + } + } + } + + public abstract class ILinkUnionCase + { + public uint SwitchValue { get; internal set; } + public uint SwitchColumnValue { get; internal set; } + public uint FieldRowId { get; internal set; } + public Type Type { get; internal set; } + public ILazyRow? LazyRow { get; internal set; } + } + + public class LinkUnionCase : ILinkUnionCase where T : ExcelRow + { + public LinkUnionCase( uint switchValue, uint switchColumnValue, uint fieldRowId, GameData gameData, Language language ) + { + SwitchValue = switchValue; + SwitchColumnValue = switchColumnValue; + FieldRowId = fieldRowId; + Type = typeof( T ); + + var rowExists = gameData.GetExcelSheet< T >().HasRow( fieldRowId ); + + if (SwitchValue == SwitchColumnValue && rowExists) + LazyRow = new LazyRow< T >( gameData, FieldRowId, language ); + } + + // public static ILinkUnionCase Create( uint switchValue, uint switchColumnValue, uint fieldRowId, GameData gameData, Language language, List possibleTypes ) + // { + // if( switchValue == switchColumnValue ) + // { + // Type firstFoundType = null; + // foreach( var possibleType in possibleTypes ) + // { + // var rawSheet = gameData.Excel.GetSheetRaw( possibleType.Name ); + // if( rawSheet.GetRow( fieldRowId ) != null ) + // { + // firstFoundType = possibleType; + // } + // } + // + // if( firstFoundType == null ) + // { + // throw new ArgumentException( $"Could not find any matching types for row {fieldRowId}" ); + // } + // + // var openCase = typeof(LinkUnionCase<>); + // var genericCase = openCase.MakeGenericType(firstFoundType); + // var instance = (ILinkUnionCase) Activator.CreateInstance(genericCase); + // + // var openLazyRow = typeof(LazyRow<>); + // var genericLazyRow = openLazyRow.MakeGenericType(firstFoundType); + // var lazyRowInstance = (ILazyRow) Activator.CreateInstance(genericLazyRow, gameData, fieldRowId, language); + // + // instance.Type = firstFoundType; + // instance.SwitchValue = switchValue; + // instance.SwitchColumnValue = switchColumnValue; + // instance.FieldRowId = fieldRowId; + // instance.LazyRow = lazyRowInstance; + // } + // + // // _switchValue = switchValue; + // // _switchColumnValue = switchColumnValue; + // // _fieldRowId = fieldRowId; + // // _type = typeof( T ); + // // + // // if( _switchValue == _switchColumnValue ) + // // { + // // foreach( var possibleType in possibleTypes ) + // // { + // // + // // } + // // _lazyRow = new LazyRow< T >( gameData, _fieldRowId, language ); + // // } + // } + } + + public uint RowId; + public List Cases = new(); + + public LinkUnionHandler Match() + { + return new LinkUnionHandler(LinkUnionHandlerType.Match, this); + } + + public LinkUnionHandler Switch() + { + return new LinkUnionHandler(LinkUnionHandlerType.Switch, this); + } + + public LinkUnionHandler OneOf() + { + return new LinkUnionHandler(LinkUnionHandlerType.Switch, this); + } +} \ No newline at end of file From 28f91d569718b3d23c874101257e7b0f90962b58 Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 12 Oct 2023 11:07:17 -0400 Subject: [PATCH 2/4] more changes --- src/Lumina/Excel/ExcelSheet.cs | 15 ++- src/Lumina/Excel/LazyRow.cs | 13 ++ src/Lumina/Excel/LinkUnion.cs | 210 --------------------------------- 3 files changed, 25 insertions(+), 213 deletions(-) delete mode 100644 src/Lumina/Excel/LinkUnion.cs diff --git a/src/Lumina/Excel/ExcelSheet.cs b/src/Lumina/Excel/ExcelSheet.cs index e42b51ae..043b18fc 100644 --- a/src/Lumina/Excel/ExcelSheet.cs +++ b/src/Lumina/Excel/ExcelSheet.cs @@ -52,12 +52,21 @@ internal bool HasRowInternal( uint row, uint subRow ) { return false; } - - var parser = GetRowParser( page, row, subRow ); - if( parser == null ) + + // TODO fix this so no exception lol + try + { + var parser = GetRowParser( page, row, subRow ); + if( parser == null ) + { + return false; + } + } + catch( IndexOutOfRangeException e ) { return false; } + return true; } diff --git a/src/Lumina/Excel/LazyRow.cs b/src/Lumina/Excel/LazyRow.cs index 8e644e2f..65d21dc1 100644 --- a/src/Lumina/Excel/LazyRow.cs +++ b/src/Lumina/Excel/LazyRow.cs @@ -22,6 +22,19 @@ public interface ILazyRow public ExcelRow? RawRow { get; } } + public class DefaultLazyRow : ILazyRow + { + public uint Row { get; set; } + public bool IsValueCreated => false; + public Language Language => Language.None; + public ExcelRow? RawRow => null; + + public DefaultLazyRow( uint rowId ) + { + Row = rowId; + } + } + /// /// Allows for sheet definitions to contain entries which will lazily load the referenced sheet row /// diff --git a/src/Lumina/Excel/LinkUnion.cs b/src/Lumina/Excel/LinkUnion.cs deleted file mode 100644 index 3d74f670..00000000 --- a/src/Lumina/Excel/LinkUnion.cs +++ /dev/null @@ -1,210 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Lumina.Data; - -namespace Lumina.Excel; - -public class LinkUnion -{ - internal enum LinkUnionHandlerType - { - Match, - Switch, - } - - public class LinkUnionHandler - { - private LinkUnionHandlerType _type; - private LinkUnion _union; - - private HashSet<(uint, Type)> _caseTypesConsidered; - private HashSet<(uint, Type)> _caseTypesPermitted; - - private HashSet< Type > _typesConsidered; - private HashSet< Type > _typesPermitted; - - internal LinkUnionHandler( LinkUnionHandlerType type, LinkUnion union ) - { - _type = type; - _union = union; - _typesConsidered = new HashSet< Type >(); - _typesPermitted = _union.Cases.Select( h => h.Type ).ToHashSet(); - _caseTypesConsidered = new HashSet< (uint, Type) >(); - _caseTypesPermitted = _union.Cases.Select( h => (h.SwitchValue, h.Type) ).ToHashSet(); - } - - public LinkUnionHandler Case< T >( Action< LazyRow< T > > func ) where T : ExcelRow - { - var workingType = typeof( T ); - TypeCheck( workingType ); - - foreach( var handler in _union.Cases.Where( h => h.Type == workingType ) ) - { - if( handler.LazyRow is not LazyRow< T > lazyRow ) continue; - func( lazyRow ); - } - - _typesConsidered.Add( typeof( T ) ); - return this; - } - - public LinkUnionHandler Case< T >( uint switchValue, Action< LazyRow< T > > func ) where T : ExcelRow - { - var workingType = typeof( T ); - CaseCheck( switchValue, workingType ); - - foreach( var handler in _union.Cases.Where( h => h.SwitchValue == switchValue && h.Type == workingType ) ) - { - if( handler.LazyRow is not LazyRow< T > lazyRow ) continue; - func( lazyRow ); - } - - _caseTypesConsidered.Add( (switchValue, workingType) ); - return this; - } - - private void TypeCheck( Type type ) - { - if( _typesConsidered.Contains( type ) ) - { - throw new ArgumentException( $"Duplicate case for type {type.Name}" ); - } - - if( !_typesPermitted.Contains( type ) ) - { - throw new ArgumentException( $"Type {type.Name} is not permitted here" ); - } - } - - private void CaseCheck( uint value, Type type ) - { - if( _caseTypesConsidered.Contains( ( value, type ) ) ) - { - throw new ArgumentException( $"Duplicate case for value {value} {type}" ); - } - - if( !_caseTypesPermitted.Contains( (value, type) ) ) - { - throw new ArgumentException( $"Case {value}, {type} is not permitted here" ); - } - } - - public LinkUnionHandler Ignore< T >() where T : ExcelRow - { - _typesConsidered.Add( typeof( T ) ); - return this; - } - - public LinkUnionHandler Default( Action< ILazyRow > func ) - { - _typesConsidered.UnionWith( _typesPermitted ); - _caseTypesConsidered.UnionWith( _caseTypesPermitted ); - return this; - } - - public void Exhaust() - { - //TODO more on this - var unusedTypes = _typesPermitted.Except( _typesConsidered ); - var unusedCases = _caseTypesPermitted.Except( _caseTypesConsidered ); - - if( unusedTypes.Any() ) - { - throw new ArgumentException( $"Unmatched types: {string.Join( ", ", unusedTypes.Select( t => t.Name ) )}" ); - } - } - } - - public abstract class ILinkUnionCase - { - public uint SwitchValue { get; internal set; } - public uint SwitchColumnValue { get; internal set; } - public uint FieldRowId { get; internal set; } - public Type Type { get; internal set; } - public ILazyRow? LazyRow { get; internal set; } - } - - public class LinkUnionCase : ILinkUnionCase where T : ExcelRow - { - public LinkUnionCase( uint switchValue, uint switchColumnValue, uint fieldRowId, GameData gameData, Language language ) - { - SwitchValue = switchValue; - SwitchColumnValue = switchColumnValue; - FieldRowId = fieldRowId; - Type = typeof( T ); - - var rowExists = gameData.GetExcelSheet< T >().HasRow( fieldRowId ); - - if (SwitchValue == SwitchColumnValue && rowExists) - LazyRow = new LazyRow< T >( gameData, FieldRowId, language ); - } - - // public static ILinkUnionCase Create( uint switchValue, uint switchColumnValue, uint fieldRowId, GameData gameData, Language language, List possibleTypes ) - // { - // if( switchValue == switchColumnValue ) - // { - // Type firstFoundType = null; - // foreach( var possibleType in possibleTypes ) - // { - // var rawSheet = gameData.Excel.GetSheetRaw( possibleType.Name ); - // if( rawSheet.GetRow( fieldRowId ) != null ) - // { - // firstFoundType = possibleType; - // } - // } - // - // if( firstFoundType == null ) - // { - // throw new ArgumentException( $"Could not find any matching types for row {fieldRowId}" ); - // } - // - // var openCase = typeof(LinkUnionCase<>); - // var genericCase = openCase.MakeGenericType(firstFoundType); - // var instance = (ILinkUnionCase) Activator.CreateInstance(genericCase); - // - // var openLazyRow = typeof(LazyRow<>); - // var genericLazyRow = openLazyRow.MakeGenericType(firstFoundType); - // var lazyRowInstance = (ILazyRow) Activator.CreateInstance(genericLazyRow, gameData, fieldRowId, language); - // - // instance.Type = firstFoundType; - // instance.SwitchValue = switchValue; - // instance.SwitchColumnValue = switchColumnValue; - // instance.FieldRowId = fieldRowId; - // instance.LazyRow = lazyRowInstance; - // } - // - // // _switchValue = switchValue; - // // _switchColumnValue = switchColumnValue; - // // _fieldRowId = fieldRowId; - // // _type = typeof( T ); - // // - // // if( _switchValue == _switchColumnValue ) - // // { - // // foreach( var possibleType in possibleTypes ) - // // { - // // - // // } - // // _lazyRow = new LazyRow< T >( gameData, _fieldRowId, language ); - // // } - // } - } - - public uint RowId; - public List Cases = new(); - - public LinkUnionHandler Match() - { - return new LinkUnionHandler(LinkUnionHandlerType.Match, this); - } - - public LinkUnionHandler Switch() - { - return new LinkUnionHandler(LinkUnionHandlerType.Switch, this); - } - - public LinkUnionHandler OneOf() - { - return new LinkUnionHandler(LinkUnionHandlerType.Switch, this); - } -} \ No newline at end of file From b5a74c7a39c795e2a4b1e7e999976ebcf8ab1150 Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 12 Oct 2023 23:43:44 -0400 Subject: [PATCH 3/4] additions for new sheets --- src/Lumina/Excel/ExcelSheet.cs | 43 ---------------------------------- src/Lumina/Excel/LazyRow.cs | 29 +++++++++++++++++++++-- 2 files changed, 27 insertions(+), 45 deletions(-) diff --git a/src/Lumina/Excel/ExcelSheet.cs b/src/Lumina/Excel/ExcelSheet.cs index 043b18fc..6c8d49c5 100644 --- a/src/Lumina/Excel/ExcelSheet.cs +++ b/src/Lumina/Excel/ExcelSheet.cs @@ -18,16 +18,6 @@ public ExcelSheet( ExcelHeaderFile headerFile, string name, Language requestedLa { } - public bool HasRow( uint row ) - { - return HasRow( row, UInt32.MaxValue ); - } - - public bool HasRow( uint row, uint subRow ) - { - return HasRowInternal( row, subRow ); - } - public T? GetRow( uint row ) { return GetRow( row, UInt32.MaxValue ); @@ -38,39 +28,6 @@ public bool HasRow( uint row, uint subRow ) return GetRowInternal( row, subRow ); } - internal bool HasRowInternal( uint row, uint subRow ) - { - var cacheKey = GetCacheKey( row, subRow ); - - if( _rowCache.TryGetValue( cacheKey, out var cachedRow ) ) - { - return true; - } - - var page = GetPageForRow( row ); - if( page == null ) - { - return false; - } - - // TODO fix this so no exception lol - try - { - var parser = GetRowParser( page, row, subRow ); - if( parser == null ) - { - return false; - } - } - catch( IndexOutOfRangeException e ) - { - return false; - } - - - return true; - } - internal T? GetRowInternal( uint row, uint subRow ) { var cacheKey = GetCacheKey( row, subRow ); diff --git a/src/Lumina/Excel/LazyRow.cs b/src/Lumina/Excel/LazyRow.cs index 65d21dc1..77ac580d 100644 --- a/src/Lumina/Excel/LazyRow.cs +++ b/src/Lumina/Excel/LazyRow.cs @@ -1,4 +1,8 @@ +using System; +using System.Linq; using Lumina.Data; +using Lumina.Data.Files.Excel; +using Lumina.Data.Structs.Excel; namespace Lumina.Excel { @@ -22,17 +26,38 @@ public interface ILazyRow public ExcelRow? RawRow { get; } } - public class DefaultLazyRow : ILazyRow + public class EmptyLazyRow : ILazyRow { + private static readonly ExcelDataPagination _blankPagination = new(); + public uint Row { get; set; } public bool IsValueCreated => false; public Language Language => Language.None; public ExcelRow? RawRow => null; - public DefaultLazyRow( uint rowId ) + public EmptyLazyRow( uint rowId ) { Row = rowId; } + + public static ILazyRow GetFirstLazyRowOrEmpty( GameData gameData, uint row, params string[] sheetNames ) + { + return GetFirstLazyRowOrEmpty( gameData, row, Language.None, sheetNames ); + } + + public static ILazyRow GetFirstLazyRowOrEmpty( GameData gameData, uint row, Language language, params string[] sheetNames ) + { + foreach( var sheetName in sheetNames ) + { + var exh = gameData.GetFile( $"exd/{sheetName}.exh" ); + if( exh == null ) continue; + var page = exh.DataPages.FirstOrDefault( s => row >= s.StartId && row <= s.StartId + s.RowCount, _blankPagination ); + if( page.Equals( _blankPagination ) ) continue; + + return new LazyRow(gameData, row, language); + } + return new EmptyLazyRow( row ); + } } /// From 32ef2c937126ba23f3e53bdce9f39a17786a1e47 Mon Sep 17 00:00:00 2001 From: liam <6005409+lmcintyre@users.noreply.github.com> Date: Tue, 17 Oct 2023 00:25:12 -0400 Subject: [PATCH 4/4] Discard changes to src/Lumina/Excel/ExcelSheet.cs --- src/Lumina/Excel/ExcelSheet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Lumina/Excel/ExcelSheet.cs b/src/Lumina/Excel/ExcelSheet.cs index 6c8d49c5..cb7f11dd 100644 --- a/src/Lumina/Excel/ExcelSheet.cs +++ b/src/Lumina/Excel/ExcelSheet.cs @@ -27,7 +27,7 @@ public ExcelSheet( ExcelHeaderFile headerFile, string name, Language requestedLa { return GetRowInternal( row, subRow ); } - + internal T? GetRowInternal( uint row, uint subRow ) { var cacheKey = GetCacheKey( row, subRow );