From 2f9402ae5fa845893e9dec701a9a719a349a7b11 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 15 Feb 2021 12:35:14 +0100 Subject: [PATCH] Make import of files with erroneous extension a bit more robust. --- Penumbra/Importer/TexToolsImport.cs | 85 +++++++++++++++------------ Penumbra/Util/StringPathExtensions.cs | 18 ++++++ 2 files changed, 65 insertions(+), 38 deletions(-) create mode 100644 Penumbra/Util/StringPathExtensions.cs diff --git a/Penumbra/Importer/TexToolsImport.cs b/Penumbra/Importer/TexToolsImport.cs index 7d587314..cd322d6b 100644 --- a/Penumbra/Importer/TexToolsImport.cs +++ b/Penumbra/Importer/TexToolsImport.cs @@ -53,14 +53,11 @@ namespace Penumbra.Importer switch( modPackFile.Extension ) { case ".ttmp": - ImportV1ModPack( modPackFile ); - break; - case ".ttmp2": - ImportV2ModPack( modPackFile ); - break; - - default: + VerifyVersionAndImport(modPackFile); + break; + + default: throw new ArgumentException( $"Unrecognized modpack format: {modPackFile.Extension}", nameof(modPackFile) ); } @@ -88,15 +85,33 @@ namespace Penumbra.Importer return new MagicTempFileStreamManagerAndDeleterFuckery( fs ); } - private void ImportV1ModPack( FileInfo modPackFile ) + private void VerifyVersionAndImport(FileInfo modPackFile) + { + using var zfs = modPackFile.OpenRead(); + using var extractedModPack = new ZipFile( zfs ); + var mpl = extractedModPack.GetEntry( "TTMPL.mpl" ); + var modRaw = GetStringFromZipEntry( extractedModPack, mpl, Encoding.UTF8 ); + + // At least a better validation than going by the extension. + if (modRaw.Contains("\"TTMPVersion\":")) + { + if (modPackFile.Extension != ".ttmp2") + PluginLog.Warning($"File {modPackFile.FullName} seems to be a V2 TTMP, but has the wrong extension."); + ImportV2ModPack(modPackFile, extractedModPack, modRaw); + } + else + { + if (modPackFile.Extension != ".ttmp") + PluginLog.Warning($"File {modPackFile.FullName} seems to be a V1 TTMP, but has the wrong extension."); + ImportV1ModPack(modPackFile, extractedModPack, modRaw); + } + } + + private void ImportV1ModPack( FileInfo modPackFile, ZipFile extractedModPack, string modRaw ) { PluginLog.Log( " -> Importing V1 ModPack" ); - using var zfs = modPackFile.OpenRead(); - using var extractedModPack = new ZipFile( zfs ); - - var mpl = extractedModPack.GetEntry( "TTMPL.mpl" ); - var modListRaw = GetStringFromZipEntry( extractedModPack, mpl, Encoding.UTF8 ).Split( + var modListRaw = modRaw.Split( new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None ); @@ -129,33 +144,26 @@ namespace Penumbra.Importer ExtractSimpleModList( newModFolder, modList, modData ); } - private void ImportV2ModPack( FileInfo modPackFile ) + private void ImportV2ModPack( FileInfo modPackFile, ZipFile extractedModPack, string modRaw ) { - using var zfs = modPackFile.OpenRead(); - using var extractedModPack = new ZipFile( zfs ); - - var mpl = extractedModPack.GetEntry( "TTMPL.mpl" ); - var modList = JsonConvert.DeserializeObject< SimpleModPack >( GetStringFromZipEntry( extractedModPack, mpl, Encoding.UTF8 ) ); + var modList = JsonConvert.DeserializeObject< SimpleModPack >( modRaw ); if( modList.TTMPVersion.EndsWith( "s" ) ) { - ImportSimpleV2ModPack( extractedModPack ); + ImportSimpleV2ModPack( extractedModPack, modList ); return; } if( modList.TTMPVersion.EndsWith( "w" ) ) { - ImportExtendedV2ModPack( extractedModPack ); + ImportExtendedV2ModPack( extractedModPack, modRaw ); } } - private void ImportSimpleV2ModPack( ZipFile extractedModPack ) + private void ImportSimpleV2ModPack( ZipFile extractedModPack, SimpleModPack modList ) { PluginLog.Log( " -> Importing Simple V2 ModPack" ); - var mpl = extractedModPack.GetEntry( "TTMPL.mpl" ); - var modList = JsonConvert.DeserializeObject< SimpleModPack >( GetStringFromZipEntry( extractedModPack, mpl, Encoding.UTF8 ) ); - // Create a new ModMeta from the TTMP modlist info var modMeta = new ModMeta { @@ -179,12 +187,11 @@ namespace Penumbra.Importer ExtractSimpleModList( newModFolder, modList.SimpleModsList, modData ); } - private void ImportExtendedV2ModPack( ZipFile extractedModPack ) + private void ImportExtendedV2ModPack( ZipFile extractedModPack, string modRaw ) { PluginLog.Log( " -> Importing Extended V2 ModPack" ); - var mpl = extractedModPack.GetEntry( "TTMPL.mpl" ); - var modList = JsonConvert.DeserializeObject< ExtendedModPack >( GetStringFromZipEntry( extractedModPack, mpl, Encoding.UTF8 ) ); + var modList = JsonConvert.DeserializeObject< ExtendedModPack >( modRaw ); // Create a new ModMeta from the TTMP modlist info var modMeta = new ModMeta @@ -202,7 +209,7 @@ namespace Penumbra.Importer var newModFolder = new DirectoryInfo( Path.Combine( _outDirectory.FullName, - Path.GetFileNameWithoutExtension( modList.Name ) + Path.GetFileNameWithoutExtension( modList.Name ).ReplaceInvalidPathSymbols() ) ); newModFolder.Create(); @@ -216,10 +223,12 @@ namespace Penumbra.Importer // Iterate through all pages foreach( var page in modList.ModPackPages) { - foreach(var group in page.ModGroups) { - var groupFolder = new DirectoryInfo(Path.Combine(newModFolder.FullName, group.GroupName)); - foreach(var option in group.OptionList) { - var optionFolder = new DirectoryInfo( Path.Combine( groupFolder.FullName, option.Name ) ); + foreach(var group in page.ModGroups) + { + var groupFolder = new DirectoryInfo(Path.Combine(newModFolder.FullName, group.GroupName.ReplaceInvalidPathSymbols())); + foreach(var option in group.OptionList) + { + var optionFolder = new DirectoryInfo( Path.Combine( groupFolder.FullName, option.Name.ReplaceInvalidPathSymbols()) ); ExtractSimpleModList( optionFolder, option.ModsJsons, modData ); } AddMeta(newModFolder, groupFolder, group, modMeta); @@ -248,14 +257,14 @@ namespace Penumbra.Importer OptionDesc = String.IsNullOrEmpty(opt.Description) ? "" : opt.Description, OptionFiles = new Dictionary>() }; - var optDir = new DirectoryInfo(Path.Combine( groupFolder.FullName, opt.Name)); - if (optDir.Exists) + var optDir = new DirectoryInfo(Path.Combine( groupFolder.FullName, opt.Name.ReplaceInvalidPathSymbols())); + if (optDir.Exists) { foreach ( var file in optDir.EnumerateFiles("*.*", SearchOption.AllDirectories) ) { optio.AddFile(file.FullName.Substring(baseFolder.FullName.Length).TrimStart('\\'), file.FullName.Substring(optDir.FullName.Length).TrimStart('\\').Replace('\\','/')); - } - } + } + } Inf.Options.Add( optio ); } meta.Groups.Add( group.GroupName, Inf ); @@ -307,7 +316,7 @@ namespace Penumbra.Importer } catch( Exception ex ) { - PluginLog.LogError( ex, "Could not export mod." ); + PluginLog.LogError( ex, "Could not extract mod." ); } } diff --git a/Penumbra/Util/StringPathExtensions.cs b/Penumbra/Util/StringPathExtensions.cs new file mode 100644 index 00000000..027715cd --- /dev/null +++ b/Penumbra/Util/StringPathExtensions.cs @@ -0,0 +1,18 @@ +using System.IO; + +namespace Penumbra +{ + public static class StringPathExtensions + { + private static char[] _invalid = Path.GetInvalidFileNameChars(); + public static string ReplaceInvalidPathSymbols( this string s, string replacement = "_" ) + { + return string.Join( replacement, s.Split( _invalid ) ); + } + + public static string RemoveInvalidPathSymbols( this string s ) + { + return string.Concat( s.Split( _invalid ) ); + } + } +} \ No newline at end of file