mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 19:14:27 +00:00
361 lines
12 KiB
C#
361 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using SabreTools.Core.Tools;
|
|
using SabreTools.Hashing;
|
|
using SabreTools.IO;
|
|
using SabreTools.IO.Extensions;
|
|
using SabreTools.IO.Logging;
|
|
|
|
namespace SabreTools.FileTypes
|
|
{
|
|
/// <summary>
|
|
/// Represents a folder for reading and writing
|
|
/// </summary>
|
|
public class Folder : BaseFile, IFolder
|
|
{
|
|
#region Protected instance variables
|
|
|
|
/// <summary>
|
|
/// Hashes that are available for children
|
|
/// </summary>
|
|
protected HashType[] _hashTypes = [HashType.CRC32, HashType.MD5, HashType.SHA1];
|
|
|
|
/// <summary>
|
|
/// Set of children file objects
|
|
/// </summary>
|
|
protected List<BaseFile>? _children;
|
|
|
|
/// <summary>
|
|
/// Logging object
|
|
/// </summary>
|
|
protected Logger _logger;
|
|
|
|
/// <summary>
|
|
/// Flag specific to Folder to omit Machine name from output path
|
|
/// </summary>
|
|
private readonly bool _writeToParent;
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
/// <summary>
|
|
/// Create a new Folder with no base file
|
|
/// </summary>
|
|
/// <param name="writeToParent">True to write directly to parent, false otherwise</param>
|
|
public Folder(bool writeToParent = false) : base()
|
|
{
|
|
_writeToParent = writeToParent;
|
|
_logger = new Logger(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new Folder from the given file
|
|
/// </summary>
|
|
/// <param name="filename">Name of the folder to use</param>
|
|
/// <param name="writeToParent">True to write directly to parent, false otherwise</param>
|
|
public Folder(string filename, bool writeToParent = false) : base(filename)
|
|
{
|
|
_writeToParent = writeToParent;
|
|
_logger = new Logger(this);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Extraction
|
|
|
|
/// <inheritdoc/>
|
|
public bool CopyAll(string outDir)
|
|
{
|
|
// If we have an invalid filename
|
|
if (Filename == null)
|
|
return false;
|
|
|
|
// Copy all files from the current folder to the output directory recursively
|
|
try
|
|
{
|
|
// Make sure the folders exist
|
|
Directory.CreateDirectory(Filename);
|
|
Directory.CreateDirectory(outDir);
|
|
|
|
DirectoryCopy(Filename, outDir, true);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// https://docs.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories
|
|
private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs)
|
|
{
|
|
// Get the subdirectories for the specified directory.
|
|
DirectoryInfo dir = new(sourceDirName);
|
|
|
|
if (!dir.Exists)
|
|
{
|
|
throw new DirectoryNotFoundException(
|
|
"Source directory does not exist or could not be found: "
|
|
+ sourceDirName);
|
|
}
|
|
|
|
DirectoryInfo[] dirs = dir.GetDirectories();
|
|
// If the destination directory doesn't exist, create it.
|
|
if (!Directory.Exists(destDirName))
|
|
{
|
|
Directory.CreateDirectory(destDirName);
|
|
}
|
|
|
|
// Get the files in the directory and copy them to the new location.
|
|
FileInfo[] files = dir.GetFiles();
|
|
foreach (FileInfo file in files)
|
|
{
|
|
string temppath = Path.Combine(destDirName, file.Name);
|
|
file.CopyTo(temppath, false);
|
|
}
|
|
|
|
// If copying subdirectories, copy them and their contents to new location.
|
|
if (copySubDirs)
|
|
{
|
|
foreach (DirectoryInfo subdir in dirs)
|
|
{
|
|
string temppath = Path.Combine(destDirName, subdir.Name);
|
|
DirectoryCopy(subdir.FullName, temppath, copySubDirs);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public string? CopyToFile(string entryName, string outDir)
|
|
{
|
|
string? realentry = null;
|
|
|
|
// If we have an invalid filename
|
|
if (Filename == null)
|
|
return null;
|
|
|
|
// Copy single file from the current folder to the output directory, if exists
|
|
try
|
|
{
|
|
// Make sure the folders exist
|
|
Directory.CreateDirectory(Filename);
|
|
Directory.CreateDirectory(outDir);
|
|
|
|
// Get all files from the input directory
|
|
List<string> files = PathTool.GetFilesOrdered(Filename);
|
|
|
|
// Now sort through to find the first file that matches
|
|
string? match = files.Find(s => s.EndsWith(entryName));
|
|
|
|
// If we had a file, copy that over to the new name
|
|
if (!string.IsNullOrEmpty(match))
|
|
{
|
|
realentry = match;
|
|
File.Copy(match, Path.Combine(outDir, entryName));
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex);
|
|
return realentry;
|
|
}
|
|
|
|
return realentry;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public (Stream?, string?) GetEntryStream(string entryName)
|
|
{
|
|
// If we have an invalid filename
|
|
if (Filename == null)
|
|
return (null, null);
|
|
|
|
// Copy single file from the current folder to the output directory, if exists
|
|
try
|
|
{
|
|
// Make sure the folders exist
|
|
Directory.CreateDirectory(Filename);
|
|
|
|
// Get all files from the input directory
|
|
List<string> files = PathTool.GetFilesOrdered(Filename);
|
|
|
|
// Now sort through to find the first file that matches
|
|
string? match = files.Find(s => s.EndsWith(entryName));
|
|
|
|
// If we had a file, open and return the stream
|
|
if (!string.IsNullOrEmpty(match))
|
|
{
|
|
var stream = File.OpenRead(match);
|
|
return (stream, match);
|
|
}
|
|
|
|
return (null, null);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex);
|
|
return (null, null);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Information
|
|
|
|
/// <summary>
|
|
/// Set the hash type that can be included in children
|
|
/// </summary>
|
|
public void SetHashType(HashType hashType)
|
|
=> SetHashTypes([hashType]);
|
|
|
|
/// <summary>
|
|
/// Set the hash types that can be included in children
|
|
/// </summary>
|
|
public void SetHashTypes(HashType[] hashTypes)
|
|
=> _hashTypes = hashTypes;
|
|
|
|
/// <inheritdoc/>
|
|
public List<BaseFile>? GetChildren()
|
|
{
|
|
// If we have an invalid filename
|
|
if (Filename == null)
|
|
return null;
|
|
|
|
if (_children == null || _children.Count == 0)
|
|
{
|
|
_children = [];
|
|
#if NET20 || NET35
|
|
foreach (string file in Directory.GetFiles(Filename, "*"))
|
|
#else
|
|
foreach (string file in Directory.EnumerateFiles(Filename, "*", SearchOption.TopDirectoryOnly))
|
|
#endif
|
|
{
|
|
BaseFile? nf = FileTypeTool.GetInfo(file, _hashTypes);
|
|
if (nf != null)
|
|
_children.Add(nf);
|
|
}
|
|
|
|
#if NET20 || NET35
|
|
foreach (string dir in Directory.GetDirectories(Filename, "*"))
|
|
#else
|
|
foreach (string dir in Directory.EnumerateDirectories(Filename, "*", SearchOption.TopDirectoryOnly))
|
|
#endif
|
|
{
|
|
var fl = new Folder(dir);
|
|
_children.Add(fl);
|
|
}
|
|
}
|
|
|
|
return _children;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public List<string>? GetEmptyFolders()
|
|
{
|
|
return Filename.ListEmpty();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Writing
|
|
|
|
/// <inheritdoc/>
|
|
public bool Write(string inputFile, string outDir, BaseFile? baseFile)
|
|
{
|
|
FileStream fs = File.OpenRead(inputFile);
|
|
return Write(fs, outDir, baseFile);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public bool Write(Stream? inputStream, string outDir, BaseFile? baseFile)
|
|
{
|
|
// If either input is null or empty, return
|
|
if (inputStream == null || baseFile == null || baseFile.Filename == null)
|
|
return false;
|
|
|
|
// If the stream is not readable, return
|
|
if (!inputStream.CanRead)
|
|
return false;
|
|
|
|
// Set internal variables
|
|
FileStream? outputStream = null;
|
|
|
|
// Get the output folder name from the first rebuild rom
|
|
string fileName;
|
|
if (_writeToParent)
|
|
fileName = Path.Combine(outDir, TextHelper.RemovePathUnsafeCharacters(baseFile.Filename) ?? string.Empty);
|
|
else
|
|
#if NET20 || NET35
|
|
fileName = Path.Combine(Path.Combine(outDir, TextHelper.RemovePathUnsafeCharacters(baseFile.Parent) ?? string.Empty), TextHelper.RemovePathUnsafeCharacters(baseFile.Filename) ?? string.Empty);
|
|
#else
|
|
fileName = Path.Combine(outDir, TextHelper.RemovePathUnsafeCharacters(baseFile.Parent) ?? string.Empty, TextHelper.RemovePathUnsafeCharacters(baseFile.Filename) ?? string.Empty);
|
|
#endif
|
|
|
|
// Replace any incorrect directory characters
|
|
if (Path.DirectorySeparatorChar == '\\')
|
|
fileName = fileName.Replace('/', '\\');
|
|
else if (Path.DirectorySeparatorChar == '/')
|
|
fileName = fileName.Replace('\\', '/');
|
|
|
|
try
|
|
{
|
|
// If the full output path doesn't exist, create it
|
|
string? dir = Path.GetDirectoryName(fileName);
|
|
if (dir != null && !Directory.Exists(dir))
|
|
Directory.CreateDirectory(dir);
|
|
|
|
// Overwrite output files by default
|
|
outputStream = File.Create(fileName);
|
|
if (outputStream == null)
|
|
return false;
|
|
|
|
if (inputStream.CanSeek)
|
|
inputStream.Seek(0, SeekOrigin.Begin);
|
|
|
|
// Copy the input stream to the output
|
|
int bufferSize = 4096 * 128;
|
|
byte[] ibuffer = new byte[bufferSize];
|
|
int ilen;
|
|
while ((ilen = inputStream.Read(ibuffer, 0, bufferSize)) > 0)
|
|
{
|
|
outputStream.Write(ibuffer, 0, ilen);
|
|
outputStream.Flush();
|
|
}
|
|
|
|
outputStream.Dispose();
|
|
|
|
// Try to set the creation time
|
|
try
|
|
{
|
|
if (!string.IsNullOrEmpty(baseFile.Date))
|
|
File.SetCreationTime(fileName, DateTime.Parse(baseFile.Date));
|
|
}
|
|
catch { }
|
|
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.Error(ex);
|
|
return false;
|
|
}
|
|
finally
|
|
{
|
|
outputStream?.Dispose();
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public bool Write(List<string> inputFiles, string outDir, List<BaseFile>? baseFiles)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|