using Ionic.Zlib;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
#if NET3
using Net3_Proxy;
using Path = Net3_Proxy.Path;
#endif
namespace IPA.Logging.Printers
{
///
/// A abstract class that provides the utilities to write to a GZip file.
///
public abstract class GZFilePrinter : LogPrinter, IDisposable
{
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool CreateHardLink(
string lpFileName,
string lpExistingFileName,
IntPtr lpSecurityAttributes
);
#if NET4
private const RegexOptions reOptions = RegexOptions.Compiled;
#elif NET3 // Needed because Compiled doesn't exist in Unity's .NET 3 runtime
private const RegexOptions reOptions = RegexOptions.None;
#endif
internal static Regex removeControlCodes = new Regex("\x1b\\[\\d+m", reOptions);
private FileInfo fileInfo;
///
/// The that writes to the GZip file.
///
/// the writer to the underlying filestream
protected StreamWriter FileWriter;
private FileStream fstream;
///
/// Gets the for the file to write to.
///
/// the file to write to
protected abstract FileInfo GetFileInfo();
private const string latestFormat = "_latest{0}";
private void InitLog()
{
try
{
if (fileInfo == null)
{ // first init
fileInfo = GetFileInfo();
var ext = fileInfo.Extension;
var symlink = new FileInfo(Path.Combine(fileInfo.DirectoryName ?? throw new InvalidOperationException(), string.Format(latestFormat, ext)));
if (symlink.Exists) symlink.Delete();
foreach (var file in fileInfo.Directory.EnumerateFiles("*.log", SearchOption.TopDirectoryOnly))
{
if (file.Equals(fileInfo)) continue;
if (file.Extension == ".gz") continue;
CompressOldLog(file);
}
fileInfo.Create().Close();
try
{
if (!CreateHardLink(symlink.FullName, fileInfo.FullName, IntPtr.Zero))
{
var error = Marshal.GetLastWin32Error();
Logger.Default.Error($"Hardlink creation failed ({error})");
}
}
catch (Exception e)
{
Logger.Default.Error("Error creating latest hardlink!");
Logger.Default.Error(e);
}
}
}
catch (Exception e)
{
Logger.Default.Error("Error initializing log!");
Logger.Default.Error(e);
}
}
private static async void CompressOldLog(FileInfo file)
{
Logger.Default.Debug($"Compressing log file {file}");
var newFile = new FileInfo(file.FullName + ".gz");
using (var istream = file.OpenRead())
using (var ostream = newFile.Create())
using (var gz = new GZipStream(ostream, CompressionMode.Compress, CompressionLevel.BestCompression, false))
await istream.CopyToAsync(gz);
file.Delete();
}
///
/// Called at the start of any print session.
///
public sealed override void StartPrint()
{
InitLog();
fstream = fileInfo.Open(FileMode.Append, FileAccess.Write);
FileWriter = new StreamWriter(fstream, new UTF8Encoding(false));
}
///
/// Called at the end of any print session.
///
public sealed override void EndPrint()
{
FileWriter.Flush();
fstream.Flush();
FileWriter.Dispose();
fstream.Dispose();
FileWriter = null;
fstream = null;
}
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Disposes the file printer.
///
/// does nothing
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
FileWriter.Flush();
fstream.Flush();
FileWriter.Close();
fstream.Close();
FileWriter.Dispose();
fstream.Dispose();
}
}
}
}