From 63407f775fc6574ec444a8a05933c77d1b3abf8e Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sun, 18 Apr 2021 23:03:34 +0200 Subject: [PATCH 1/5] Fix imgui deprecation warnings, some formatting. --- Penumbra/UI/MenuTabs/TabEffective.cs | 4 +- .../TabInstalled/TabInstalledDetails.cs | 42 ++++++++++--------- .../TabInstalled/TabInstalledDetailsEdit.cs | 4 +- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/Penumbra/UI/MenuTabs/TabEffective.cs b/Penumbra/UI/MenuTabs/TabEffective.cs index 33a1d8b1..a5e7e3c9 100644 --- a/Penumbra/UI/MenuTabs/TabEffective.cs +++ b/Penumbra/UI/MenuTabs/TabEffective.cs @@ -53,14 +53,14 @@ namespace Penumbra.UI return; } - if( ImGui.ListBoxHeader( "##effective_files", AutoFillSize ) ) + if( ImGui.BeginListBox( "##effective_files", AutoFillSize ) ) { foreach( var file in _mods.ResolvedFiles ) { DrawFileLine( file.Value, file.Key ); } - ImGui.ListBoxFooter(); + ImGui.EndListBox(); } ImGui.EndTabItem(); diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs index 18aec077..72c369b8 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs @@ -45,8 +45,8 @@ namespace Penumbra.UI private const string LabelConfigurationTab = "Configuration"; private const string TooltipFilesTab = - "Green files replace their standard game path counterpart (not in any option) or are in all options of a Single-Select option.\n" + - "Yellow files are restricted to some options."; + "Green files replace their standard game path counterpart (not in any option) or are in all options of a Single-Select option.\n" + + "Yellow files are restricted to some options."; private const float TextSizePadding = 5f; private const float OptionSelectionWidth = 140f; @@ -130,8 +130,11 @@ namespace Penumbra.UI } // This is only drawn when we have a mod selected, so we can forgive nulls. - private ModInfo Mod => _selector.Mod()!; - private ModMeta Meta => Mod.Mod.Meta; + private ModInfo Mod + => _selector.Mod()!; + + private ModMeta Meta + => Mod.Mod.Meta; private void Save() { @@ -193,10 +196,10 @@ namespace Penumbra.UI if( ImGui.BeginTabItem( LabelChangedItemsTab ) ) { ImGui.SetNextItemWidth( -1 ); - if( ImGui.ListBoxHeader( LabelChangedItemsHeader, AutoFillSize ) ) + if( ImGui.BeginListBox( LabelChangedItemsHeader, AutoFillSize ) ) { _changedItemsList ??= Meta.ChangedItems - .Select( ( I, index ) => ( $"{LabelChangedItemIdx}{index}", I ) ).ToArray(); + .Select( ( I, index ) => ( $"{LabelChangedItemIdx}{index}", I ) ).ToArray(); for( var i = 0; i < Meta.ChangedItems.Count; ++i ) { @@ -213,14 +216,14 @@ namespace Penumbra.UI { ImGui.SetNextItemWidth( -1 ); if( ImGui.InputText( LabelChangedItemNew, ref newItem, 128, flags ) - && newItem.Length > 0 ) + && newItem.Length > 0 ) { Meta.ChangedItems.Add( newItem ); _selector.SaveCurrentMod(); } } - ImGui.ListBoxFooter(); + ImGui.EndListBox(); } ImGui.EndTabItem(); @@ -239,7 +242,7 @@ namespace Penumbra.UI } ImGui.SetNextItemWidth( -1 ); - if( ImGui.ListBoxHeader( LabelConflictsHeader, AutoFillSize ) ) + if( ImGui.BeginListBox( LabelConflictsHeader, AutoFillSize ) ) { foreach( var kv in Mod.Mod.FileConflicts ) { @@ -258,7 +261,7 @@ namespace Penumbra.UI ImGui.Unindent( 15 ); } - ImGui.ListBoxFooter(); + ImGui.EndListBox(); } ImGui.EndTabItem(); @@ -274,10 +277,11 @@ namespace Penumbra.UI if( ImGui.BeginTabItem( LabelFileSwapTab ) ) { _fileSwapOffset ??= Meta.FileSwaps - .Max( P => ImGui.CalcTextSize( P.Key ).X ) + TextSizePadding; + .Max( P => ImGui.CalcTextSize( P.Key ).X ) + + TextSizePadding; ImGui.SetNextItemWidth( -1 ); - if( ImGui.ListBoxHeader( LabelFileSwapHeader, AutoFillSize ) ) + if( ImGui.BeginListBox( LabelFileSwapHeader, AutoFillSize ) ) { foreach( var file in Meta.FileSwaps ) { @@ -288,7 +292,7 @@ namespace Penumbra.UI ImGui.Selectable( file.Value ); } - ImGui.ListBoxFooter(); + ImGui.EndListBox(); } ImGui.EndTabItem(); @@ -308,7 +312,7 @@ namespace Penumbra.UI var len = Mod.Mod.ModBasePath.FullName.Length; _fullFilenameList = Mod.Mod.ModFiles - .Select( F => ( F, false, ColorGreen, new RelPath( F, Mod.Mod.ModBasePath ) ) ).ToArray(); + .Select( F => ( F, false, ColorGreen, new RelPath( F, Mod.Mod.ModBasePath ) ) ).ToArray(); if( Meta.Groups.Count == 0 ) { @@ -353,7 +357,7 @@ namespace Penumbra.UI } ImGui.SetNextItemWidth( -1 ); - if( ImGui.ListBoxHeader( LabelFileListHeader, AutoFillSize ) ) + if( ImGui.BeginListBox( LabelFileListHeader, AutoFillSize ) ) { UpdateFilenameList(); foreach( var file in _fullFilenameList! ) @@ -363,7 +367,7 @@ namespace Penumbra.UI ImGui.PopStyleColor(); } - ImGui.ListBoxFooter(); + ImGui.EndListBox(); } else { @@ -389,7 +393,7 @@ namespace Penumbra.UI } if( path[ TextDefaultGamePath.Length ] != '-' - || !int.TryParse( path.Substring( TextDefaultGamePath.Length + 1 ), out removeFolders ) ) + || !int.TryParse( path.Substring( TextDefaultGamePath.Length + 1 ), out removeFolders ) ) { return -1; } @@ -442,7 +446,7 @@ namespace Penumbra.UI else { changed = gamePaths - .Aggregate( changed, ( current, gamePath ) => current | option.AddFile( relName, gamePath ) ); + .Aggregate( changed, ( current, gamePath ) => current | option.AddFile( relName, gamePath ) ); } } @@ -545,7 +549,7 @@ namespace Penumbra.UI { string tmp = gamePath; if( ImGui.InputText( $"##{fileName}_{gamePath}", ref tmp, 128, ImGuiInputTextFlags.EnterReturnsTrue ) - && tmp != gamePath ) + && tmp != gamePath ) { gamePaths.Remove( gamePath ); if( tmp.Length > 0 ) diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs index 7be378d8..c2407826 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs @@ -86,7 +86,7 @@ namespace Penumbra.UI } ImGui.SetNextItemWidth( -1 ); - if( ImGui.ListBoxHeader( LabelFileListHeader, AutoFillSize - new Vector2( 0, 1.5f * ImGui.GetTextLineHeight() ) ) ) + if( ImGui.BeginListBox( LabelFileListHeader, AutoFillSize - new Vector2( 0, 1.5f * ImGui.GetTextLineHeight() ) ) ) { for( var i = 0; i < Mod!.Mod.ModFiles.Count; ++i ) { @@ -94,7 +94,7 @@ namespace Penumbra.UI } } - ImGui.ListBoxFooter(); + ImGui.EndListBox(); DrawGroupRow(); ImGui.EndTabItem(); From 7c56de8c81d10dff7ef8cbd73ce56eb3fadfc0c5 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sun, 18 Apr 2021 23:14:48 +0200 Subject: [PATCH 2/5] Fixed formatting in SqPackStream, applied null-checking, replaced user-facing asserts with throws. --- Penumbra/Importer/TexToolsImport.cs | 12 +- Penumbra/Util/PenumbraSqPackStream.cs | 250 +++++++++++++++----------- 2 files changed, 151 insertions(+), 111 deletions(-) diff --git a/Penumbra/Importer/TexToolsImport.cs b/Penumbra/Importer/TexToolsImport.cs index ad4975af..4ea6dc9b 100644 --- a/Penumbra/Importer/TexToolsImport.cs +++ b/Penumbra/Importer/TexToolsImport.cs @@ -65,7 +65,7 @@ namespace Penumbra.Importer } // You can in no way rely on any file paths in TTMPs so we need to just do this, sorry - private ZipEntry FindZipEntry( ZipFile file, string fileName ) + private static ZipEntry? FindZipEntry( ZipFile file, string fileName ) { for( var i = 0; i < file.Count; i++ ) { @@ -84,7 +84,10 @@ namespace Penumbra.Importer // write shitty zip garbage to disk var entry = FindZipEntry( file, entryName ); - Debug.Assert( entry != null, $"Could not find in mod zip: {entryName}" ); + if( entry == null ) + { + throw new FileNotFoundException( $"ZIP does not contain a file named {entryName}." ); + } using var s = file.GetInputStream( entry ); @@ -100,7 +103,10 @@ namespace Penumbra.Importer using var extractedModPack = new ZipFile( zfs ); var mpl = FindZipEntry( extractedModPack, "TTMPL.mpl" ); - Debug.Assert( mpl != null, "Could not find mod meta in ZIP." ); + if( mpl == null ) + { + throw new FileNotFoundException( "ZIP does not contain a TTMPL.mpl file." ); + } var modRaw = GetStringFromZipEntry( extractedModPack, mpl, Encoding.UTF8 ); diff --git a/Penumbra/Util/PenumbraSqPackStream.cs b/Penumbra/Util/PenumbraSqPackStream.cs index 185cf2c2..4c461f8f 100644 --- a/Penumbra/Util/PenumbraSqPackStream.cs +++ b/Penumbra/Util/PenumbraSqPackStream.cs @@ -1,14 +1,10 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; -using System.Linq; using System.Runtime.InteropServices; using System.Text; -using System.Threading.Tasks; using Lumina; -using Lumina.Data; using Lumina.Data.Structs; namespace Penumbra.Util @@ -19,12 +15,14 @@ namespace Penumbra.Util protected BinaryReader Reader { get; set; } - public PenumbraSqPackStream( FileInfo file ) : this( file.OpenRead() ) {} + public PenumbraSqPackStream( FileInfo file ) + : this( file.OpenRead() ) + { } public PenumbraSqPackStream( Stream stream ) { BaseStream = stream; - Reader = new BinaryReader( BaseStream ); + Reader = new BinaryReader( BaseStream ); } public SqPackHeader GetSqPackHeader() @@ -48,7 +46,7 @@ namespace Penumbra.Util BaseStream.Position = offset; var fileInfo = Reader.ReadStructure< SqPackFileInfo >(); - var file = Activator.CreateInstance< T >(); + var file = Activator.CreateInstance< T >(); // check if we need to read the extended model header or just default to the standard file header if( fileInfo.Type == FileType.Model ) @@ -59,32 +57,31 @@ namespace Penumbra.Util file.FileInfo = new PenumbraFileInfo { - HeaderSize = modelFileInfo.Size, - Type = modelFileInfo.Type, - BlockCount = modelFileInfo.UsedNumberOfBlocks, + HeaderSize = modelFileInfo.Size, + Type = modelFileInfo.Type, + BlockCount = modelFileInfo.UsedNumberOfBlocks, RawFileSize = modelFileInfo.RawFileSize, - Offset = offset, + Offset = offset, // todo: is this useful? - ModelBlock = modelFileInfo + ModelBlock = modelFileInfo, }; } else { file.FileInfo = new PenumbraFileInfo { - HeaderSize = fileInfo.Size, - Type = fileInfo.Type, - BlockCount = fileInfo.NumberOfBlocks, + HeaderSize = fileInfo.Size, + Type = fileInfo.Type, + BlockCount = fileInfo.NumberOfBlocks, RawFileSize = fileInfo.RawFileSize, - Offset = offset + Offset = offset, }; } switch( fileInfo.Type ) { - case FileType.Empty: - throw new FileNotFoundException( $"The file located at 0x{offset:x} is empty." ); + case FileType.Empty: throw new FileNotFoundException( $"The file located at 0x{offset:x} is empty." ); case FileType.Standard: ReadStandardFile( file, ms ); @@ -98,16 +95,17 @@ namespace Penumbra.Util ReadTextureFile( file, ms ); break; - default: - throw new NotImplementedException( $"File Type {(UInt32)fileInfo.Type} is not implemented." ); + default: throw new NotImplementedException( $"File Type {( uint )fileInfo.Type} is not implemented." ); } file.Data = ms.ToArray(); if( file.Data.Length != file.FileInfo.RawFileSize ) + { Debug.WriteLine( "Read data size does not match file size." ); + } - file.FileStream = new MemoryStream( file.Data, false ); - file.Reader = new BinaryReader( file.FileStream ); + file.FileStream = new MemoryStream( file.Data, false ); + file.Reader = new BinaryReader( file.FileStream ); file.FileStream.Position = 0; file.LoadFile(); @@ -117,7 +115,7 @@ namespace Penumbra.Util private void ReadStandardFile( PenumbraFileResource resource, MemoryStream ms ) { - var blocks = Reader.ReadStructures< DatStdFileBlockInfos >( (int)resource.FileInfo.BlockCount ); + var blocks = Reader.ReadStructures< DatStdFileBlockInfos >( ( int )resource.FileInfo!.BlockCount ); foreach( var block in blocks ) { @@ -128,9 +126,10 @@ namespace Penumbra.Util ms.Position = 0; } - private unsafe void ReadModelFile( PenumbraFileResource resource, MemoryStream ms ) { - var mdlBlock = resource.FileInfo.ModelBlock; - long baseOffset = resource.FileInfo.Offset + resource.FileInfo.HeaderSize; + private unsafe void ReadModelFile( PenumbraFileResource resource, MemoryStream ms ) + { + var mdlBlock = resource.FileInfo!.ModelBlock; + var baseOffset = resource.FileInfo.Offset + resource.FileInfo.HeaderSize; // 1/1/3/3/3 stack/runtime/vertex/egeo/index // TODO: consider testing if this is more reliable than the Explorer method @@ -139,89 +138,111 @@ namespace Penumbra.Util // but it seems to work fine in explorer... int totalBlocks = mdlBlock.StackBlockNum; totalBlocks += mdlBlock.RuntimeBlockNum; - for( int i = 0; i < 3; i++ ) + for( var i = 0; i < 3; i++ ) + { totalBlocks += mdlBlock.VertexBufferBlockNum[ i ]; - for( int i = 0; i < 3; i++ ) - totalBlocks += mdlBlock.EdgeGeometryVertexBufferBlockNum[ i ]; - for( int i = 0; i < 3; i++ ) - totalBlocks += mdlBlock.IndexBufferBlockNum[ i ]; + } - var compressedBlockSizes = Reader.ReadStructures< UInt16 >( totalBlocks ); - int currentBlock = 0; - int stackSize; - int runtimeSize; - int[] vertexDataOffsets = new int[3]; - int[] indexDataOffsets = new int[3]; - int[] vertexBufferSizes = new int[3]; - int[] indexBufferSizes = new int[3]; + for( var i = 0; i < 3; i++ ) + { + totalBlocks += mdlBlock.EdgeGeometryVertexBufferBlockNum[ i ]; + } + + for( var i = 0; i < 3; i++ ) + { + totalBlocks += mdlBlock.IndexBufferBlockNum[ i ]; + } + + var compressedBlockSizes = Reader.ReadStructures< ushort >( totalBlocks ); + var currentBlock = 0; + var vertexDataOffsets = new int[3]; + var indexDataOffsets = new int[3]; + var vertexBufferSizes = new int[3]; + var indexBufferSizes = new int[3]; ms.Seek( 0x44, SeekOrigin.Begin ); - + Reader.Seek( baseOffset + mdlBlock.StackOffset ); - long stackStart = ms.Position; - for( int i = 0; i < mdlBlock.StackBlockNum; i++ ) { - long lastPos = Reader.BaseStream.Position; + var stackStart = ms.Position; + for( var i = 0; i < mdlBlock.StackBlockNum; i++ ) + { + var lastPos = Reader.BaseStream.Position; ReadFileBlock( ms ); Reader.Seek( lastPos + compressedBlockSizes[ currentBlock ] ); currentBlock++; } - long stackEnd = ms.Position; - stackSize = (int) ( stackEnd - stackStart ); + var stackEnd = ms.Position; + var stackSize = ( int )( stackEnd - stackStart ); Reader.Seek( baseOffset + mdlBlock.RuntimeOffset ); - long runtimeStart = ms.Position; - for( int i = 0; i < mdlBlock.RuntimeBlockNum; i++ ) { - long lastPos = Reader.BaseStream.Position; + var runtimeStart = ms.Position; + for( var i = 0; i < mdlBlock.RuntimeBlockNum; i++ ) + { + var lastPos = Reader.BaseStream.Position; ReadFileBlock( ms ); Reader.Seek( lastPos + compressedBlockSizes[ currentBlock ] ); currentBlock++; } - long runtimeEnd = ms.Position; - runtimeSize = (int) ( runtimeEnd - runtimeStart ); + var runtimeEnd = ms.Position; + var runtimeSize = ( int )( runtimeEnd - runtimeStart ); - for( int i = 0; i < 3; i++ ) { - - if( mdlBlock.VertexBufferBlockNum[ i ] != 0 ) { - int currentVertexOffset = (int) ms.Position; + for( var i = 0; i < 3; i++ ) + { + if( mdlBlock.VertexBufferBlockNum[ i ] != 0 ) + { + var currentVertexOffset = ( int )ms.Position; if( i == 0 || currentVertexOffset != vertexDataOffsets[ i - 1 ] ) + { vertexDataOffsets[ i ] = currentVertexOffset; + } else + { vertexDataOffsets[ i ] = 0; + } Reader.Seek( baseOffset + mdlBlock.VertexBufferOffset[ i ] ); - for( int j = 0; j < mdlBlock.VertexBufferBlockNum[ i ]; j++ ) { - long lastPos = Reader.BaseStream.Position; - vertexBufferSizes[ i ] += (int) ReadFileBlock( ms ); + for( var j = 0; j < mdlBlock.VertexBufferBlockNum[ i ]; j++ ) + { + var lastPos = Reader.BaseStream.Position; + vertexBufferSizes[ i ] += ( int )ReadFileBlock( ms ); Reader.Seek( lastPos + compressedBlockSizes[ currentBlock ] ); currentBlock++; } } - if( mdlBlock.EdgeGeometryVertexBufferBlockNum[ i ] != 0 ) { - for( int j = 0; j < mdlBlock.EdgeGeometryVertexBufferBlockNum[ i ]; j++ ) { - long lastPos = Reader.BaseStream.Position; + if( mdlBlock.EdgeGeometryVertexBufferBlockNum[ i ] != 0 ) + { + for( var j = 0; j < mdlBlock.EdgeGeometryVertexBufferBlockNum[ i ]; j++ ) + { + var lastPos = Reader.BaseStream.Position; ReadFileBlock( ms ); Reader.Seek( lastPos + compressedBlockSizes[ currentBlock ] ); currentBlock++; } } - if( mdlBlock.IndexBufferBlockNum[ i ] != 0 ) { - int currentIndexOffset = (int) ms.Position; + if( mdlBlock.IndexBufferBlockNum[ i ] != 0 ) + { + var currentIndexOffset = ( int )ms.Position; if( i == 0 || currentIndexOffset != indexDataOffsets[ i - 1 ] ) + { indexDataOffsets[ i ] = currentIndexOffset; + } else + { indexDataOffsets[ i ] = 0; + } // i guess this is only needed in the vertex area, for i = 0 // Reader.Seek( baseOffset + mdlBlock.IndexBufferOffset[ i ] ); - for( int j = 0; j < mdlBlock.IndexBufferBlockNum[ i ]; j++ ) { - long lastPos = Reader.BaseStream.Position; - indexBufferSizes[ i ] += (int) ReadFileBlock( ms ); + for( var j = 0; j < mdlBlock.IndexBufferBlockNum[ i ]; j++ ) + { + var lastPos = Reader.BaseStream.Position; + indexBufferSizes[ i ] += ( int )ReadFileBlock( ms ); Reader.Seek( lastPos + compressedBlockSizes[ currentBlock ] ); currentBlock++; } @@ -234,33 +255,45 @@ namespace Penumbra.Util ms.Write( BitConverter.GetBytes( runtimeSize ) ); ms.Write( BitConverter.GetBytes( mdlBlock.VertexDeclarationNum ) ); ms.Write( BitConverter.GetBytes( mdlBlock.MaterialNum ) ); - for( int i = 0; i < 3; i++ ) + for( var i = 0; i < 3; i++ ) + { ms.Write( BitConverter.GetBytes( vertexDataOffsets[ i ] ) ); - for( int i = 0; i < 3; i++ ) + } + + for( var i = 0; i < 3; i++ ) + { ms.Write( BitConverter.GetBytes( indexDataOffsets[ i ] ) ); - for( int i = 0; i < 3; i++ ) + } + + for( var i = 0; i < 3; i++ ) + { ms.Write( BitConverter.GetBytes( vertexBufferSizes[ i ] ) ); - for( int i = 0; i < 3; i++ ) + } + + for( var i = 0; i < 3; i++ ) + { ms.Write( BitConverter.GetBytes( indexBufferSizes[ i ] ) ); - ms.Write( new [] {mdlBlock.NumLods} ); + } + + ms.Write( new[] { mdlBlock.NumLods } ); ms.Write( BitConverter.GetBytes( mdlBlock.IndexBufferStreamingEnabled ) ); ms.Write( BitConverter.GetBytes( mdlBlock.EdgeGeometryEnabled ) ); - ms.Write( new byte[] {0} ); + ms.Write( new byte[] { 0 } ); } private void ReadTextureFile( PenumbraFileResource resource, MemoryStream ms ) { - var blocks = Reader.ReadStructures< LodBlock >( (int)resource.FileInfo.BlockCount ); + var blocks = Reader.ReadStructures< LodBlock >( ( int )resource.FileInfo!.BlockCount ); // if there is a mipmap header, the comp_offset // will not be 0 - uint mipMapSize = blocks[ 0 ].CompressedOffset; + var mipMapSize = blocks[ 0 ].CompressedOffset; if( mipMapSize != 0 ) { - long originalPos = BaseStream.Position; + var originalPos = BaseStream.Position; BaseStream.Position = resource.FileInfo.Offset + resource.FileInfo.HeaderSize; - ms.Write( Reader.ReadBytes( (int)mipMapSize ) ); + ms.Write( Reader.ReadBytes( ( int )mipMapSize ) ); BaseStream.Position = originalPos; } @@ -269,12 +302,12 @@ namespace Penumbra.Util for( byte i = 0; i < blocks.Count; i++ ) { // start from comp_offset - long runningBlockTotal = blocks[ i ].CompressedOffset + resource.FileInfo.Offset + resource.FileInfo.HeaderSize; + var runningBlockTotal = blocks[ i ].CompressedOffset + resource.FileInfo.Offset + resource.FileInfo.HeaderSize; ReadFileBlock( runningBlockTotal, ms, true ); - for( int j = 1; j < blocks[ i ].BlockCount; j++ ) + for( var j = 1; j < blocks[ i ].BlockCount; j++ ) { - runningBlockTotal += (UInt32)Reader.ReadInt16(); + runningBlockTotal += ( uint )Reader.ReadInt16(); ReadFileBlock( runningBlockTotal, ms, true ); } @@ -283,25 +316,24 @@ namespace Penumbra.Util } } - protected uint ReadFileBlock( MemoryStream dest, bool resetPosition = false ) { - return ReadFileBlock( Reader.BaseStream.Position, dest, resetPosition ); - } + protected uint ReadFileBlock( MemoryStream dest, bool resetPosition = false ) + => ReadFileBlock( Reader.BaseStream.Position, dest, resetPosition ); protected uint ReadFileBlock( long offset, MemoryStream dest, bool resetPosition = false ) { - long originalPosition = BaseStream.Position; + var originalPosition = BaseStream.Position; BaseStream.Position = offset; var blockHeader = Reader.ReadStructure< DatBlockHeader >(); - + // uncompressed block if( blockHeader.CompressedSize == 32000 ) { - dest.Write( Reader.ReadBytes( (int)blockHeader.UncompressedSize ) ); + dest.Write( Reader.ReadBytes( ( int )blockHeader.UncompressedSize ) ); return blockHeader.UncompressedSize; } - var data = Reader.ReadBytes( (int)blockHeader.UncompressedSize ); + var data = Reader.ReadBytes( ( int )blockHeader.UncompressedSize ); using( var compressedStream = new MemoryStream( data ) ) { @@ -311,7 +343,9 @@ namespace Penumbra.Util } if( resetPosition ) + { BaseStream.Position = originalPosition; + } return blockHeader.UncompressedSize; } @@ -323,10 +357,10 @@ namespace Penumbra.Util public class PenumbraFileInfo { - public UInt32 HeaderSize; + public uint HeaderSize; public FileType Type; - public UInt32 RawFileSize; - public UInt32 BlockCount; + public uint RawFileSize; + public uint BlockCount; public long Offset { get; internal set; } @@ -336,20 +370,20 @@ namespace Penumbra.Util public class PenumbraFileResource { public PenumbraFileResource() - { - } + { } - public PenumbraFileInfo FileInfo { get; internal set; } + public PenumbraFileInfo? FileInfo { get; internal set; } - public byte[] Data { get; internal set; } + public byte[] Data { get; internal set; } = new byte[0]; - public Span< byte > DataSpan => Data.AsSpan(); + public Span< byte > DataSpan + => Data.AsSpan(); - public MemoryStream FileStream { get; internal set; } + public MemoryStream? FileStream { get; internal set; } - public BinaryReader Reader { get; internal set; } + public BinaryReader? Reader { get; internal set; } - public ParsedFilePath FilePath { get; internal set; } + public ParsedFilePath? FilePath { get; internal set; } /// /// Called once the files are read out from the dats. Used to further parse the file into usable data structures. @@ -380,22 +414,22 @@ namespace Penumbra.Util } [StructLayout( LayoutKind.Sequential )] - struct DatBlockHeader + private struct DatBlockHeader { - public UInt32 Size; - public UInt32 unknown1; - public UInt32 CompressedSize; - public UInt32 UncompressedSize; + public uint Size; + public uint unknown1; + public uint CompressedSize; + public uint UncompressedSize; }; [StructLayout( LayoutKind.Sequential )] - struct LodBlock + private struct LodBlock { - public UInt32 CompressedOffset; - public UInt32 CompressedSize; - public UInt32 DecompressedSize; - public UInt32 BlockOffset; - public UInt32 BlockCount; + public uint CompressedOffset; + public uint CompressedSize; + public uint DecompressedSize; + public uint BlockOffset; + public uint BlockCount; } } -} +} \ No newline at end of file From 67415db7c027d3174514d269bf39743ae9e8604e Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 19 Apr 2021 00:41:18 +0200 Subject: [PATCH 3/5] Fixed GamePath Serialization for FileSwaps, added FileSwap Edit menu, some formatting. --- Penumbra/Models/ModMeta.cs | 2 + .../TabInstalled/TabInstalledDetails.cs | 6 + .../TabInstalled/TabInstalledDetailsEdit.cs | 108 +++++++++++++++--- Penumbra/Util/PenumbraPath.cs | 44 +++++-- 4 files changed, 135 insertions(+), 25 deletions(-) diff --git a/Penumbra/Models/ModMeta.cs b/Penumbra/Models/ModMeta.cs index 7c1c7828..2030556c 100644 --- a/Penumbra/Models/ModMeta.cs +++ b/Penumbra/Models/ModMeta.cs @@ -20,6 +20,8 @@ namespace Penumbra.Models public List< string > ChangedItems { get; set; } = new(); + + [JsonProperty( ItemConverterType = typeof( GamePathConverter ))] public Dictionary< GamePath, GamePath > FileSwaps { get; } = new(); public Dictionary< string, OptionGroup > Groups { get; set; } = new(); diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs index 72c369b8..054b1af6 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs @@ -269,6 +269,12 @@ namespace Penumbra.UI private void DrawFileSwapTab() { + if( _editMode ) + { + DrawFileSwapTabEdit(); + return; + } + if( !Meta.FileSwaps.Any() ) { return; diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs index c2407826..c8a338e7 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs @@ -25,13 +25,13 @@ namespace Penumbra.UI private const char GamePathsSeparator = ';'; private static readonly string TooltipFilesTabEdit = - $"{TooltipFilesTab}\n" + - $"Red Files are replaced in another group or a different option in this group, but not contained in the current option."; + $"{TooltipFilesTab}\n" + + $"Red Files are replaced in another group or a different option in this group, but not contained in the current option."; private static readonly string TooltipGamePathsEdit = - $"Enter all game paths to add or remove, separated by '{GamePathsSeparator}'.\n" + - $"Use '{TextDefaultGamePath}' to add the original file path." + - $"Use '{TextDefaultGamePath}-#' to skip the first # relative directories."; + $"Enter all game paths to add or remove, separated by '{GamePathsSeparator}'.\n" + + $"Use '{TextDefaultGamePath}' to add the original file path." + + $"Use '{TextDefaultGamePath}-#' to skip the first # relative directories."; private const float MultiEditBoxWidth = 300f; @@ -109,7 +109,8 @@ namespace Penumbra.UI { var groupName = group.GroupName; if( ImGuiCustom.BeginFramedGroupEdit( ref groupName ) - && groupName != group.GroupName && !Meta!.Groups.ContainsKey( groupName ) ) + && groupName != group.GroupName + && !Meta!.Groups.ContainsKey( groupName ) ) { var oldConf = Mod!.Settings[ group.GroupName ]; Meta.Groups.Remove( group.GroupName ); @@ -120,7 +121,7 @@ namespace Penumbra.UI { GroupName = groupName, SelectionType = SelectType.Multi, - Options = group.Options + Options = group.Options, }; Mod.Settings[ groupName ] = oldConf; } @@ -137,7 +138,7 @@ namespace Penumbra.UI ImGui.SetCursorPosX( nameBoxStart ); ImGui.SetNextItemWidth( MultiEditBoxWidth ); if( ImGui.InputText( $"##new_{group.GroupName}_l", ref newOption, 64, ImGuiInputTextFlags.EnterReturnsTrue ) - && newOption.Length != 0 ) + && newOption.Length != 0 ) { group.Options.Add( new Option() { OptionName = newOption, OptionDesc = "", OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >() } ); @@ -200,7 +201,7 @@ namespace Penumbra.UI { var groupName = group.GroupName; if( ImGui.InputText( $"##{groupName}_add", ref groupName, 64, ImGuiInputTextFlags.EnterReturnsTrue ) - && !Meta!.Groups.ContainsKey( groupName ) ) + && !Meta!.Groups.ContainsKey( groupName ) ) { var oldConf = Mod!.Settings[ group.GroupName ]; if( groupName != group.GroupName ) @@ -215,7 +216,7 @@ namespace Penumbra.UI { GroupName = groupName, Options = group.Options, - SelectionType = SelectType.Single + SelectionType = SelectType.Single, } ); Mod.Settings[ groupName ] = oldConf; } @@ -245,7 +246,7 @@ namespace Penumbra.UI { OptionName = newName, OptionDesc = "", - OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >() + OptionFiles = new Dictionary< RelPath, HashSet< GamePath > >(), } ); } } @@ -264,7 +265,7 @@ namespace Penumbra.UI group.Options[ code ] = new Option() { OptionName = newName, OptionDesc = group.Options[ code ].OptionDesc, - OptionFiles = group.Options[ code ].OptionFiles + OptionFiles = group.Options[ code ].OptionFiles, }; } @@ -304,7 +305,7 @@ namespace Penumbra.UI { GroupName = newGroup, SelectionType = selectType, - Options = new List< Option >() + Options = new List< Option >(), }; Mod.Settings[ newGroup ] = 0; @@ -362,6 +363,87 @@ namespace Penumbra.UI DrawAddMultiGroupField(); } + + private void DrawFileSwapTabEdit() + { + const string arrow = " -> "; + + if( ImGui.BeginTabItem( LabelFileSwapTab ) ) + { + ImGui.SetNextItemWidth( -1 ); + if( ImGui.BeginListBox( LabelFileSwapHeader, AutoFillSize ) ) + { + var swaps = Meta.FileSwaps.Keys.ToArray(); + + var arrowWidth = ImGui.CalcTextSize( arrow ).X; + var width = ( ImGui.GetWindowWidth() - arrowWidth - 4 * ImGui.GetStyle().ItemSpacing.X ) / 2; + for( var idx = 0; idx < swaps.Length + 1; ++idx ) + { + var key = idx == swaps.Length ? GamePath.GenerateUnchecked( "" ) : swaps[ idx ]; + var value = idx == swaps.Length ? GamePath.GenerateUnchecked( "" ) : Meta.FileSwaps[ key ]; + string keyString = key; + string valueString = value; + + ImGui.SetNextItemWidth( width ); + if( ImGui.InputTextWithHint( $"##swapLhs_{idx}", "Enter new file to be replaced...", ref keyString, + GamePath.MaxGamePathLength, ImGuiInputTextFlags.EnterReturnsTrue ) ) + { + var newKey = new GamePath( keyString ); + if( newKey.CompareTo( key ) != 0 ) + { + if( idx < swaps.Length ) + { + Meta.FileSwaps.Remove( key ); + } + + if( newKey != string.Empty ) + { + Meta.FileSwaps[ newKey ] = value; + } + + _selector.SaveCurrentMod(); + if( Mod.Enabled ) + { + Save(); + } + } + } + + if( idx < swaps.Length ) + { + ImGui.SameLine(); + ImGui.TextUnformatted( arrow ); + ImGui.SameLine(); + + ImGui.SetNextItemWidth( width ); + if( ImGui.InputTextWithHint( $"##swapRhs_{idx}", "Enter new replacement path...", ref valueString, + GamePath.MaxGamePathLength, + ImGuiInputTextFlags.EnterReturnsTrue ) ) + { + var newValue = new GamePath( valueString ); + if( newValue.CompareTo( value ) != 0 ) + { + Meta.FileSwaps[ key ] = newValue; + _selector.SaveCurrentMod(); + if( Mod.Enabled ) + { + Save(); + } + } + } + } + } + + ImGui.EndListBox(); + } + + ImGui.EndTabItem(); + } + else + { + _fileSwapOffset = null; + } + } } } } \ No newline at end of file diff --git a/Penumbra/Util/PenumbraPath.cs b/Penumbra/Util/PenumbraPath.cs index 55174bf6..4b7707b5 100644 --- a/Penumbra/Util/PenumbraPath.cs +++ b/Penumbra/Util/PenumbraPath.cs @@ -1,6 +1,8 @@ using System; using System.IO; using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace Penumbra.Util { @@ -29,7 +31,7 @@ namespace Penumbra.Util => _path = CheckPre( file, baseDir ) ? Trim( Substring( file, baseDir ) ) : ""; public RelPath( GamePath gamePath ) - => _path = gamePath ? ReplaceSlash( gamePath ) : ""; + => _path = ReplaceSlash( gamePath ); private static bool CheckPre( FileInfo file, DirectoryInfo baseDir ) => file.FullName.StartsWith( baseDir.FullName ) && file.FullName.Length < MaxRelPathLength; @@ -43,9 +45,6 @@ namespace Penumbra.Util private static string Trim( string path ) => path.TrimStart( '\\' ); - public static implicit operator bool( RelPath relPath ) - => relPath._path.Length > 0; - public static implicit operator string( RelPath relPath ) => relPath._path; @@ -56,9 +55,9 @@ namespace Penumbra.Util { return rhs switch { - string => string.Compare( _path, _path, StringComparison.InvariantCulture ), + string path => string.Compare( _path, path, StringComparison.InvariantCulture ), RelPath path => string.Compare( _path, path._path, StringComparison.InvariantCulture ), - _ => -1 + _ => -1, }; } @@ -120,10 +119,7 @@ namespace Penumbra.Util => new( path, true ); public static GamePath GenerateUncheckedLower( string path ) - => new( Lower(path), true ); - - public static implicit operator bool( GamePath gamePath ) - => gamePath._path.Length > 0; + => new( Lower( path ), true ); public static implicit operator string( GamePath gamePath ) => gamePath._path; @@ -143,11 +139,35 @@ namespace Penumbra.Util { string path => string.Compare( _path, path, StringComparison.InvariantCulture ), GamePath path => string.Compare( _path, path._path, StringComparison.InvariantCulture ), - _ => -1 + _ => -1, }; } public override string ToString() => _path; } -} \ No newline at end of file + + public class GamePathConverter : JsonConverter + { + public override bool CanConvert( Type objectType ) + => objectType == typeof( GamePath ); + + public override object ReadJson( JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer ) + { + var token = JToken.Load( reader ); + return token.ToObject< GamePath >(); + } + + public override bool CanWrite + => true; + + public override void WriteJson( JsonWriter writer, object? value, JsonSerializer serializer ) + { + if( value != null ) + { + var v = ( GamePath) value; + serializer.Serialize( writer, v.ToString() ); + } + } + } +} From 4655364b98def6c8049194428e6176d950bc0ca8 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 19 Apr 2021 00:52:28 +0200 Subject: [PATCH 4/5] Added hints to input texts in edit mode. --- .../MenuTabs/TabInstalled/TabInstalledDetails.cs | 4 ++-- .../TabInstalled/TabInstalledDetailsEdit.cs | 16 +++++++++------- .../TabInstalled/TabInstalledSelector.cs | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs index 054b1af6..2822e633 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetails.cs @@ -215,7 +215,7 @@ namespace Penumbra.UI if( _editMode ) { ImGui.SetNextItemWidth( -1 ); - if( ImGui.InputText( LabelChangedItemNew, ref newItem, 128, flags ) + if( ImGui.InputTextWithHint( LabelChangedItemNew, "Enter new changed item...", ref newItem, 128, flags ) && newItem.Length > 0 ) { Meta.ChangedItems.Add( newItem ); @@ -483,7 +483,7 @@ namespace Penumbra.UI ImGui.TextUnformatted( LabelGamePathsEdit ); ImGui.SameLine(); ImGui.SetNextItemWidth( -1 ); - ImGui.InputText( LabelGamePathsEditBox, ref _currentGamePaths, 128 ); + ImGui.InputTextWithHint( LabelGamePathsEditBox, "Hover for help...", ref _currentGamePaths, 128 ); if( ImGui.IsItemHovered() ) { ImGui.SetTooltip( TooltipGamePathsEdit ); diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs index c8a338e7..e4fd80af 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledDetailsEdit.cs @@ -137,7 +137,8 @@ namespace Penumbra.UI var newOption = ""; ImGui.SetCursorPosX( nameBoxStart ); ImGui.SetNextItemWidth( MultiEditBoxWidth ); - if( ImGui.InputText( $"##new_{group.GroupName}_l", ref newOption, 64, ImGuiInputTextFlags.EnterReturnsTrue ) + if( ImGui.InputTextWithHint( $"##new_{group.GroupName}_l", "Add new option...", ref newOption, 64, + ImGuiInputTextFlags.EnterReturnsTrue ) && newOption.Length != 0 ) { group.Options.Add( new Option() @@ -315,12 +316,13 @@ namespace Penumbra.UI private void DrawAddSingleGroupField( float labelEditPos ) { - var newGroup = ""; + const string hint = "Add new Single Group..."; + var newGroup = ""; if( labelEditPos == CheckMarkSize ) { ImGui.SetCursorPosX( CheckMarkSize ); ImGui.SetNextItemWidth( MultiEditBoxWidth ); - if( ImGui.InputText( LabelNewSingleGroup, ref newGroup, 64, ImGuiInputTextFlags.EnterReturnsTrue ) ) + if( ImGui.InputTextWithHint( LabelNewSingleGroup, hint, ref newGroup, 64, ImGuiInputTextFlags.EnterReturnsTrue ) ) { AddNewGroup( newGroup, SelectType.Single ); } @@ -328,7 +330,7 @@ namespace Penumbra.UI else { ImGuiCustom.RightJustifiedLabel( labelEditPos, LabelNewSingleGroup ); - if( ImGui.InputText( LabelNewSingleGroupEdit, ref newGroup, 64, ImGuiInputTextFlags.EnterReturnsTrue ) ) + if( ImGui.InputTextWithHint( LabelNewSingleGroupEdit, hint, ref newGroup, 64, ImGuiInputTextFlags.EnterReturnsTrue ) ) { AddNewGroup( newGroup, SelectType.Single ); } @@ -340,7 +342,7 @@ namespace Penumbra.UI var newGroup = ""; ImGui.SetCursorPosX( CheckMarkSize ); ImGui.SetNextItemWidth( MultiEditBoxWidth ); - if( ImGui.InputText( LabelNewMultiGroup, ref newGroup, 64, ImGuiInputTextFlags.EnterReturnsTrue ) ) + if( ImGui.InputTextWithHint( LabelNewMultiGroup, "Add new Multi Group...", ref newGroup, 64, ImGuiInputTextFlags.EnterReturnsTrue ) ) { AddNewGroup( newGroup, SelectType.Multi ); } @@ -404,7 +406,7 @@ namespace Penumbra.UI _selector.SaveCurrentMod(); if( Mod.Enabled ) { - Save(); + _selector.ReloadCurrentMod(); } } } @@ -427,7 +429,7 @@ namespace Penumbra.UI _selector.SaveCurrentMod(); if( Mod.Enabled ) { - Save(); + _selector.ReloadCurrentMod(); } } } diff --git a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs index 5a5937ef..fb74739a 100644 --- a/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs +++ b/Penumbra/UI/MenuTabs/TabInstalled/TabInstalledSelector.cs @@ -121,7 +121,7 @@ namespace Penumbra.UI { ImGui.SetNextItemWidth( SelectorButtonSizes.X * 4 ); var tmp = _modFilter; - if( ImGui.InputText( LabelModFilter, ref tmp, 256 ) ) + if( ImGui.InputTextWithHint( LabelModFilter, "Filter Mods...", ref tmp, 256 ) ) { _modFilter = tmp.ToLowerInvariant(); } From c91a28ae1d06460d1a09490f1f27a8322a35b5a2 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 19 Apr 2021 17:16:52 +0200 Subject: [PATCH 5/5] Add Empty to Path types. --- Penumbra/Util/PenumbraPath.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Penumbra/Util/PenumbraPath.cs b/Penumbra/Util/PenumbraPath.cs index 4b7707b5..86717e12 100644 --- a/Penumbra/Util/PenumbraPath.cs +++ b/Penumbra/Util/PenumbraPath.cs @@ -51,6 +51,9 @@ namespace Penumbra.Util public static explicit operator RelPath( string relPath ) => new( relPath ); + public bool Empty + => _path.Length == 0; + public int CompareTo( object rhs ) { return rhs switch @@ -127,6 +130,9 @@ namespace Penumbra.Util public static explicit operator GamePath( string gamePath ) => new( gamePath ); + public bool Empty + => _path.Length == 0; + public string Filename() { var idx = _path.LastIndexOf( "/", StringComparison.Ordinal );