mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Add support for creating replay mod capture files (#1246)
* Add test replay handler * Fix incorrect built raw packet * Fix incorrect built raw packet * Add filter * Add not working zip lib * Add dotNetZip lib and complete basic function * Update ReplayHandler.cs * Complete Replay handler Without client player handling * Complete replay mod - New ChatBot OnNetworkPacket event * Add auto-backup and command for Replay Mod * Add ReplayMod description to readme * Small naming changes, fix compile error on .NET4.0 * ReplayHandler slight optimizations Use Path.Combine to automatically use Windows '\' or Linux '/' Move re-usable common parts outside the Replay handler Small optimizations in building JSON strings Co-authored-by: ORelio <oreliogitantispam.l0gin@spamgourmet.com>
This commit is contained in:
parent
cd1badb9d6
commit
7e20e409a8
47 changed files with 32732 additions and 21 deletions
901
MinecraftClient/Protocol/Handlers/Compression/Zip/Shared.cs
Normal file
901
MinecraftClient/Protocol/Handlers/Compression/Zip/Shared.cs
Normal file
|
|
@ -0,0 +1,901 @@
|
|||
// Shared.cs
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Copyright (c) 2006-2011 Dino Chiesa.
|
||||
// All rights reserved.
|
||||
//
|
||||
// This code module is part of DotNetZip, a zipfile class library.
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This code is licensed under the Microsoft Public License.
|
||||
// See the file License.txt for the license details.
|
||||
// More info on: http://dotnetzip.codeplex.com
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// Last Saved: <2011-August-02 19:41:01>
|
||||
//
|
||||
// ------------------------------------------------------------------
|
||||
//
|
||||
// This module defines some shared utility classes and methods.
|
||||
//
|
||||
// Created: Tue, 27 Mar 2007 15:30
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Permissions;
|
||||
|
||||
namespace Ionic.Zip
|
||||
{
|
||||
/// <summary>
|
||||
/// Collects general purpose utility methods.
|
||||
/// </summary>
|
||||
internal static class SharedUtilities
|
||||
{
|
||||
/// private null constructor
|
||||
//private SharedUtilities() { }
|
||||
|
||||
// workitem 8423
|
||||
public static Int64 GetFileLength(string fileName)
|
||||
{
|
||||
if (!File.Exists(fileName))
|
||||
throw new System.IO.FileNotFoundException(fileName);
|
||||
|
||||
long fileLength = 0L;
|
||||
FileShare fs = FileShare.ReadWrite;
|
||||
#if !NETCF
|
||||
// FileShare.Delete is not defined for the Compact Framework
|
||||
fs |= FileShare.Delete;
|
||||
#endif
|
||||
using (var s = File.Open(fileName, FileMode.Open, FileAccess.Read, fs))
|
||||
{
|
||||
fileLength = s.Length;
|
||||
}
|
||||
return fileLength;
|
||||
}
|
||||
|
||||
|
||||
[System.Diagnostics.Conditional("NETCF")]
|
||||
public static void Workaround_Ladybug318918(Stream s)
|
||||
{
|
||||
// This is a workaround for this issue:
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/details/318918
|
||||
// It's required only on NETCF.
|
||||
s.Flush();
|
||||
}
|
||||
|
||||
|
||||
#if LEGACY
|
||||
/// <summary>
|
||||
/// Round the given DateTime value to an even second value.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Round up in the case of an odd second value. The rounding does not consider
|
||||
/// fractional seconds.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This is useful because the Zip spec allows storage of time only to the nearest
|
||||
/// even second. So if you want to compare the time of an entry in the archive with
|
||||
/// it's actual time in the filesystem, you need to round the actual filesystem
|
||||
/// time, or use a 2-second threshold for the comparison.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This is most nautrally an extension method for the DateTime class but this
|
||||
/// library is built for .NET 2.0, not for .NET 3.5; This means extension methods
|
||||
/// are a no-no.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="source">The DateTime value to round</param>
|
||||
/// <returns>The ruonded DateTime value</returns>
|
||||
public static DateTime RoundToEvenSecond(DateTime source)
|
||||
{
|
||||
// round to nearest second:
|
||||
if ((source.Second % 2) == 1)
|
||||
source += new TimeSpan(0, 0, 1);
|
||||
|
||||
DateTime dtRounded = new DateTime(source.Year, source.Month, source.Day, source.Hour, source.Minute, source.Second);
|
||||
//if (source.Millisecond >= 500) dtRounded = dtRounded.AddSeconds(1);
|
||||
return dtRounded;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if YOU_LIKE_REDUNDANT_CODE
|
||||
internal static string NormalizePath(string path)
|
||||
{
|
||||
// remove leading single dot slash
|
||||
if (path.StartsWith(".\\")) path = path.Substring(2);
|
||||
|
||||
// remove intervening dot-slash
|
||||
path = path.Replace("\\.\\", "\\");
|
||||
|
||||
// remove double dot when preceded by a directory name
|
||||
var re = new System.Text.RegularExpressions.Regex(@"^(.*\\)?([^\\\.]+\\\.\.\\)(.+)$");
|
||||
path = re.Replace(path, "$1$3");
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
private static System.Text.RegularExpressions.Regex doubleDotRegex1 =
|
||||
new System.Text.RegularExpressions.Regex(@"^(.*/)?([^/\\.]+/\\.\\./)(.+)$");
|
||||
|
||||
private static string SimplifyFwdSlashPath(string path)
|
||||
{
|
||||
if (path.StartsWith("./")) path = path.Substring(2);
|
||||
path = path.Replace("/./", "/");
|
||||
|
||||
// Replace foo/anything/../bar with foo/bar
|
||||
path = doubleDotRegex1.Replace(path, "$1$3");
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Utility routine for transforming path names from filesystem format (on Windows that means backslashes) to
|
||||
/// a format suitable for use within zipfiles. This means trimming the volume letter and colon (if any) And
|
||||
/// swapping backslashes for forward slashes.
|
||||
/// </summary>
|
||||
/// <param name="pathName">source path.</param>
|
||||
/// <returns>transformed path</returns>
|
||||
public static string NormalizePathForUseInZipFile(string pathName)
|
||||
{
|
||||
// boundary case
|
||||
if (String.IsNullOrEmpty(pathName)) return pathName;
|
||||
|
||||
// trim volume if necessary
|
||||
if ((pathName.Length >= 2) && ((pathName[1] == ':') && (pathName[2] == '\\')))
|
||||
pathName = pathName.Substring(3);
|
||||
|
||||
// swap slashes
|
||||
pathName = pathName.Replace('\\', '/');
|
||||
|
||||
// trim all leading slashes
|
||||
while (pathName.StartsWith("/")) pathName = pathName.Substring(1);
|
||||
|
||||
return SimplifyFwdSlashPath(pathName);
|
||||
}
|
||||
|
||||
|
||||
static System.Text.Encoding ibm437 = System.Text.Encoding.GetEncoding("IBM437");
|
||||
static System.Text.Encoding utf8 = System.Text.Encoding.GetEncoding("UTF-8");
|
||||
|
||||
internal static byte[] StringToByteArray(string value, System.Text.Encoding encoding)
|
||||
{
|
||||
byte[] a = encoding.GetBytes(value);
|
||||
return a;
|
||||
}
|
||||
internal static byte[] StringToByteArray(string value)
|
||||
{
|
||||
return StringToByteArray(value, ibm437);
|
||||
}
|
||||
|
||||
//internal static byte[] Utf8StringToByteArray(string value)
|
||||
//{
|
||||
// return StringToByteArray(value, utf8);
|
||||
//}
|
||||
|
||||
//internal static string StringFromBuffer(byte[] buf, int maxlength)
|
||||
//{
|
||||
// return StringFromBuffer(buf, maxlength, ibm437);
|
||||
//}
|
||||
|
||||
internal static string Utf8StringFromBuffer(byte[] buf)
|
||||
{
|
||||
return StringFromBuffer(buf, utf8);
|
||||
}
|
||||
|
||||
internal static string StringFromBuffer(byte[] buf, System.Text.Encoding encoding)
|
||||
{
|
||||
// this form of the GetString() method is required for .NET CF compatibility
|
||||
string s = encoding.GetString(buf, 0, buf.Length);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
internal static int ReadSignature(System.IO.Stream s)
|
||||
{
|
||||
int x = 0;
|
||||
try { x = _ReadFourBytes(s, "n/a"); }
|
||||
catch (BadReadException) { }
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
internal static int ReadEntrySignature(System.IO.Stream s)
|
||||
{
|
||||
// handle the case of ill-formatted zip archives - includes a data descriptor
|
||||
// when none is expected.
|
||||
int x = 0;
|
||||
try
|
||||
{
|
||||
x = _ReadFourBytes(s, "n/a");
|
||||
if (x == ZipConstants.ZipEntryDataDescriptorSignature)
|
||||
{
|
||||
// advance past data descriptor - 12 bytes if not zip64
|
||||
s.Seek(12, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(s);
|
||||
x = _ReadFourBytes(s, "n/a");
|
||||
if (x != ZipConstants.ZipEntrySignature)
|
||||
{
|
||||
// Maybe zip64 was in use for the prior entry.
|
||||
// Therefore, skip another 8 bytes.
|
||||
s.Seek(8, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(s);
|
||||
x = _ReadFourBytes(s, "n/a");
|
||||
if (x != ZipConstants.ZipEntrySignature)
|
||||
{
|
||||
// seek back to the first spot
|
||||
s.Seek(-24, SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(s);
|
||||
x = _ReadFourBytes(s, "n/a");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (BadReadException) { }
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
internal static int ReadInt(System.IO.Stream s)
|
||||
{
|
||||
return _ReadFourBytes(s, "Could not read block - no data! (position 0x{0:X8})");
|
||||
}
|
||||
|
||||
private static int _ReadFourBytes(System.IO.Stream s, string message)
|
||||
{
|
||||
int n = 0;
|
||||
byte[] block = new byte[4];
|
||||
#if NETCF
|
||||
// workitem 9181
|
||||
// Reading here in NETCF sometimes reads "backwards". Seems to happen for
|
||||
// larger files. Not sure why. Maybe an error in caching. If the data is:
|
||||
//
|
||||
// 00100210: 9efa 0f00 7072 6f6a 6563 742e 6963 7750 ....project.icwP
|
||||
// 00100220: 4b05 0600 0000 0006 0006 0091 0100 008e K...............
|
||||
// 00100230: 0010 0000 00 .....
|
||||
//
|
||||
// ...and the stream Position is 10021F, then a Read of 4 bytes is returning
|
||||
// 50776369, instead of 06054b50. This seems to happen the 2nd time Read()
|
||||
// is called from that Position..
|
||||
//
|
||||
// submitted to connect.microsoft.com
|
||||
// https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=318918#tabs
|
||||
//
|
||||
for (int i = 0; i < block.Length; i++)
|
||||
{
|
||||
n+= s.Read(block, i, 1);
|
||||
}
|
||||
#else
|
||||
n = s.Read(block, 0, block.Length);
|
||||
#endif
|
||||
if (n != block.Length) throw new BadReadException(String.Format(message, s.Position));
|
||||
int data = unchecked((((block[3] * 256 + block[2]) * 256) + block[1]) * 256 + block[0]);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds a signature in the zip stream. This is useful for finding
|
||||
/// the end of a zip entry, for example, or the beginning of the next ZipEntry.
|
||||
/// </summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Scans through 64k at a time.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// If the method fails to find the requested signature, the stream Position
|
||||
/// after completion of this method is unchanged. If the method succeeds in
|
||||
/// finding the requested signature, the stream position after completion is
|
||||
/// direct AFTER the signature found in the stream.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
///
|
||||
/// <param name="stream">The stream to search</param>
|
||||
/// <param name="SignatureToFind">The 4-byte signature to find</param>
|
||||
/// <returns>The number of bytes read</returns>
|
||||
internal static long FindSignature(System.IO.Stream stream, int SignatureToFind)
|
||||
{
|
||||
long startingPosition = stream.Position;
|
||||
|
||||
int BATCH_SIZE = 65536; // 8192;
|
||||
byte[] targetBytes = new byte[4];
|
||||
targetBytes[0] = (byte)(SignatureToFind >> 24);
|
||||
targetBytes[1] = (byte)((SignatureToFind & 0x00FF0000) >> 16);
|
||||
targetBytes[2] = (byte)((SignatureToFind & 0x0000FF00) >> 8);
|
||||
targetBytes[3] = (byte)(SignatureToFind & 0x000000FF);
|
||||
byte[] batch = new byte[BATCH_SIZE];
|
||||
int n = 0;
|
||||
bool success = false;
|
||||
do
|
||||
{
|
||||
n = stream.Read(batch, 0, batch.Length);
|
||||
if (n != 0)
|
||||
{
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (batch[i] == targetBytes[3])
|
||||
{
|
||||
long curPosition = stream.Position;
|
||||
stream.Seek(i - n, System.IO.SeekOrigin.Current);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(stream);
|
||||
|
||||
// workitem 7711
|
||||
int sig = ReadSignature(stream);
|
||||
|
||||
success = (sig == SignatureToFind);
|
||||
if (!success)
|
||||
{
|
||||
stream.Seek(curPosition, System.IO.SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(stream);
|
||||
}
|
||||
else
|
||||
break; // out of for loop
|
||||
}
|
||||
}
|
||||
}
|
||||
else break;
|
||||
if (success) break;
|
||||
|
||||
} while (true);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
stream.Seek(startingPosition, System.IO.SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Workaround_Ladybug318918(stream);
|
||||
return -1; // or throw?
|
||||
}
|
||||
|
||||
// subtract 4 for the signature.
|
||||
long bytesRead = (stream.Position - startingPosition) - 4;
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
||||
// If I have a time in the .NET environment, and I want to use it for
|
||||
// SetWastWriteTime() etc, then I need to adjust it for Win32.
|
||||
internal static DateTime AdjustTime_Reverse(DateTime time)
|
||||
{
|
||||
if (time.Kind == DateTimeKind.Utc) return time;
|
||||
DateTime adjusted = time;
|
||||
if (DateTime.Now.IsDaylightSavingTime() && !time.IsDaylightSavingTime())
|
||||
adjusted = time - new System.TimeSpan(1, 0, 0);
|
||||
|
||||
else if (!DateTime.Now.IsDaylightSavingTime() && time.IsDaylightSavingTime())
|
||||
adjusted = time + new System.TimeSpan(1, 0, 0);
|
||||
|
||||
return adjusted;
|
||||
}
|
||||
|
||||
#if NECESSARY
|
||||
// If I read a time from a file with GetLastWriteTime() (etc), I need
|
||||
// to adjust it for display in the .NET environment.
|
||||
internal static DateTime AdjustTime_Forward(DateTime time)
|
||||
{
|
||||
if (time.Kind == DateTimeKind.Utc) return time;
|
||||
DateTime adjusted = time;
|
||||
if (DateTime.Now.IsDaylightSavingTime() && !time.IsDaylightSavingTime())
|
||||
adjusted = time + new System.TimeSpan(1, 0, 0);
|
||||
|
||||
else if (!DateTime.Now.IsDaylightSavingTime() && time.IsDaylightSavingTime())
|
||||
adjusted = time - new System.TimeSpan(1, 0, 0);
|
||||
|
||||
return adjusted;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
internal static DateTime PackedToDateTime(Int32 packedDateTime)
|
||||
{
|
||||
// workitem 7074 & workitem 7170
|
||||
if (packedDateTime == 0xFFFF || packedDateTime == 0)
|
||||
return new System.DateTime(1995, 1, 1, 0, 0, 0, 0); // return a fixed date when none is supplied.
|
||||
|
||||
Int16 packedTime = unchecked((Int16)(packedDateTime & 0x0000ffff));
|
||||
Int16 packedDate = unchecked((Int16)((packedDateTime & 0xffff0000) >> 16));
|
||||
|
||||
int year = 1980 + ((packedDate & 0xFE00) >> 9);
|
||||
int month = (packedDate & 0x01E0) >> 5;
|
||||
int day = packedDate & 0x001F;
|
||||
|
||||
int hour = (packedTime & 0xF800) >> 11;
|
||||
int minute = (packedTime & 0x07E0) >> 5;
|
||||
//int second = packedTime & 0x001F;
|
||||
int second = (packedTime & 0x001F) * 2;
|
||||
|
||||
// validation and error checking.
|
||||
// this is not foolproof but will catch most errors.
|
||||
if (second >= 60) { minute++; second = 0; }
|
||||
if (minute >= 60) { hour++; minute = 0; }
|
||||
if (hour >= 24) { day++; hour = 0; }
|
||||
|
||||
DateTime d = System.DateTime.Now;
|
||||
bool success= false;
|
||||
try
|
||||
{
|
||||
d = new System.DateTime(year, month, day, hour, minute, second, 0);
|
||||
success= true;
|
||||
}
|
||||
catch (System.ArgumentOutOfRangeException)
|
||||
{
|
||||
if (year == 1980 && (month == 0 || day == 0))
|
||||
{
|
||||
try
|
||||
{
|
||||
d = new System.DateTime(1980, 1, 1, hour, minute, second, 0);
|
||||
success= true;
|
||||
}
|
||||
catch (System.ArgumentOutOfRangeException)
|
||||
{
|
||||
try
|
||||
{
|
||||
d = new System.DateTime(1980, 1, 1, 0, 0, 0, 0);
|
||||
success= true;
|
||||
}
|
||||
catch (System.ArgumentOutOfRangeException) { }
|
||||
|
||||
}
|
||||
}
|
||||
// workitem 8814
|
||||
// my god, I can't believe how many different ways applications
|
||||
// can mess up a simple date format.
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
while (year < 1980) year++;
|
||||
while (year > 2030) year--;
|
||||
while (month < 1) month++;
|
||||
while (month > 12) month--;
|
||||
while (day < 1) day++;
|
||||
while (day > 28) day--;
|
||||
while (minute < 0) minute++;
|
||||
while (minute > 59) minute--;
|
||||
while (second < 0) second++;
|
||||
while (second > 59) second--;
|
||||
d = new System.DateTime(year, month, day, hour, minute, second, 0);
|
||||
success= true;
|
||||
}
|
||||
catch (System.ArgumentOutOfRangeException) { }
|
||||
}
|
||||
}
|
||||
if (!success)
|
||||
{
|
||||
string msg = String.Format("y({0}) m({1}) d({2}) h({3}) m({4}) s({5})", year, month, day, hour, minute, second);
|
||||
throw new ZipException(String.Format("Bad date/time format in the zip file. ({0})", msg));
|
||||
|
||||
}
|
||||
// workitem 6191
|
||||
//d = AdjustTime_Reverse(d);
|
||||
d = DateTime.SpecifyKind(d, DateTimeKind.Local);
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
internal
|
||||
static Int32 DateTimeToPacked(DateTime time)
|
||||
{
|
||||
// The time is passed in here only for purposes of writing LastModified to the
|
||||
// zip archive. It should always be LocalTime, but we convert anyway. And,
|
||||
// since the time is being written out, it needs to be adjusted.
|
||||
|
||||
time = time.ToLocalTime();
|
||||
// workitem 7966
|
||||
//time = AdjustTime_Forward(time);
|
||||
|
||||
// see http://www.vsft.com/hal/dostime.htm for the format
|
||||
UInt16 packedDate = (UInt16)((time.Day & 0x0000001F) | ((time.Month << 5) & 0x000001E0) | (((time.Year - 1980) << 9) & 0x0000FE00));
|
||||
UInt16 packedTime = (UInt16)((time.Second / 2 & 0x0000001F) | ((time.Minute << 5) & 0x000007E0) | ((time.Hour << 11) & 0x0000F800));
|
||||
|
||||
Int32 result = (Int32)(((UInt32)(packedDate << 16)) | packedTime);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create a pseudo-random filename, suitable for use as a temporary
|
||||
/// file, and open it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The System.IO.Path.GetRandomFileName() method is not available on
|
||||
/// the Compact Framework, so this library provides its own substitute
|
||||
/// on NETCF.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This method produces a filename of the form
|
||||
/// DotNetZip-xxxxxxxx.tmp, where xxxxxxxx is replaced by randomly
|
||||
/// chosen characters, and creates that file.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static void CreateAndOpenUniqueTempFile(string dir,
|
||||
out Stream fs,
|
||||
out string filename)
|
||||
{
|
||||
// workitem 9763
|
||||
// http://dotnet.org.za/markn/archive/2006/04/15/51594.aspx
|
||||
// try 3 times:
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
filename = Path.Combine(dir, InternalGetTempFileName());
|
||||
fs = new FileStream(filename, FileMode.CreateNew);
|
||||
return;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
if (i == 2) throw;
|
||||
}
|
||||
}
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
#if NETCF || SILVERLIGHT
|
||||
public static string InternalGetTempFileName()
|
||||
{
|
||||
return "DotNetZip-" + GenerateRandomStringImpl(8,0) + ".tmp";
|
||||
}
|
||||
|
||||
internal static string GenerateRandomStringImpl(int length, int delta)
|
||||
{
|
||||
bool WantMixedCase = (delta == 0);
|
||||
System.Random rnd = new System.Random();
|
||||
|
||||
string result = "";
|
||||
char[] a = new char[length];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
// delta == 65 means uppercase
|
||||
// delta == 97 means lowercase
|
||||
if (WantMixedCase)
|
||||
delta = (rnd.Next(2) == 0) ? 65 : 97;
|
||||
a[i] = (char)(rnd.Next(26) + delta);
|
||||
}
|
||||
|
||||
result = new System.String(a);
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
public static string InternalGetTempFileName()
|
||||
{
|
||||
return "DotNetZip-" + Path.GetRandomFileName().Substring(0, 8) + ".tmp";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Workitem 7889: handle ERROR_LOCK_VIOLATION during read
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This could be gracefully handled with an extension attribute, but
|
||||
/// This assembly is built for .NET 2.0, so I cannot use them.
|
||||
/// </remarks>
|
||||
internal static int ReadWithRetry(System.IO.Stream s, byte[] buffer, int offset, int count, string FileName)
|
||||
{
|
||||
int n = 0;
|
||||
bool done = false;
|
||||
#if !NETCF && !SILVERLIGHT
|
||||
int retries = 0;
|
||||
#endif
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
n = s.Read(buffer, offset, count);
|
||||
done = true;
|
||||
}
|
||||
#if NETCF || SILVERLIGHT
|
||||
catch (System.IO.IOException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
#else
|
||||
catch (System.IO.IOException ioexc1)
|
||||
{
|
||||
// Check if we can call GetHRForException,
|
||||
// which makes unmanaged code calls.
|
||||
var p = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
|
||||
if (p.IsUnrestricted())
|
||||
{
|
||||
uint hresult = _HRForException(ioexc1);
|
||||
if (hresult != 0x80070021) // ERROR_LOCK_VIOLATION
|
||||
throw new System.IO.IOException(String.Format("Cannot read file {0}", FileName), ioexc1);
|
||||
retries++;
|
||||
if (retries > 10)
|
||||
throw new System.IO.IOException(String.Format("Cannot read file {0}, at offset 0x{1:X8} after 10 retries", FileName, offset), ioexc1);
|
||||
|
||||
// max time waited on last retry = 250 + 10*550 = 5.75s
|
||||
// aggregate time waited after 10 retries: 250 + 55*550 = 30.5s
|
||||
System.Threading.Thread.Sleep(250 + retries * 550);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The permission.Demand() failed. Therefore, we cannot call
|
||||
// GetHRForException, and cannot do the subtle handling of
|
||||
// ERROR_LOCK_VIOLATION. Just bail.
|
||||
throw;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
while (!done);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
#if !NETCF
|
||||
// workitem 8009
|
||||
//
|
||||
// This method must remain separate.
|
||||
//
|
||||
// Marshal.GetHRForException() is needed to do special exception handling for
|
||||
// the read. But, that method requires UnmanagedCode permissions, and is marked
|
||||
// with LinkDemand for UnmanagedCode. In an ASP.NET medium trust environment,
|
||||
// where UnmanagedCode is restricted, will generate a SecurityException at the
|
||||
// time of JIT of the method that calls a method that is marked with LinkDemand
|
||||
// for UnmanagedCode. The SecurityException, if it is restricted, will occur
|
||||
// when this method is JITed.
|
||||
//
|
||||
// The Marshal.GetHRForException() is factored out of ReadWithRetry in order to
|
||||
// avoid the SecurityException at JIT compile time. Because _HRForException is
|
||||
// called only when the UnmanagedCode is allowed. This means .NET never
|
||||
// JIT-compiles this method when UnmanagedCode is disallowed, and thus never
|
||||
// generates the JIT-compile time exception.
|
||||
//
|
||||
#endif
|
||||
private static uint _HRForException(System.Exception ex1)
|
||||
{
|
||||
return unchecked((uint)System.Runtime.InteropServices.Marshal.GetHRForException(ex1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A decorator stream. It wraps another stream, and performs bookkeeping
|
||||
/// to keep track of the stream Position.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In some cases, it is not possible to get the Position of a stream, let's
|
||||
/// say, on a write-only output stream like ASP.NET's
|
||||
/// <c>Response.OutputStream</c>, or on a different write-only stream
|
||||
/// provided as the destination for the zip by the application. In this
|
||||
/// case, programmers can use this counting stream to count the bytes read
|
||||
/// or written.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Consider the scenario of an application that saves a self-extracting
|
||||
/// archive (SFX), that uses a custom SFX stub.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Saving to a filesystem file, the application would open the
|
||||
/// filesystem file (getting a <c>FileStream</c>), save the custom sfx stub
|
||||
/// into it, and then call <c>ZipFile.Save()</c>, specifying the same
|
||||
/// FileStream. <c>ZipFile.Save()</c> does the right thing for the zipentry
|
||||
/// offsets, by inquiring the Position of the <c>FileStream</c> before writing
|
||||
/// any data, and then adding that initial offset into any ZipEntry
|
||||
/// offsets in the zip directory. Everything works fine.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Now suppose the application is an ASPNET application and it saves
|
||||
/// directly to <c>Response.OutputStream</c>. It's not possible for DotNetZip to
|
||||
/// inquire the <c>Position</c>, so the offsets for the SFX will be wrong.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The workaround is for the application to use this class to wrap
|
||||
/// <c>HttpResponse.OutputStream</c>, then write the SFX stub and the ZipFile
|
||||
/// into that wrapper stream. Because <c>ZipFile.Save()</c> can inquire the
|
||||
/// <c>Position</c>, it will then do the right thing with the offsets.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class CountingStream : System.IO.Stream
|
||||
{
|
||||
// workitem 12374: this class is now public
|
||||
private System.IO.Stream _s;
|
||||
private Int64 _bytesWritten;
|
||||
private Int64 _bytesRead;
|
||||
private Int64 _initialOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The constructor.
|
||||
/// </summary>
|
||||
/// <param name="stream">The underlying stream</param>
|
||||
public CountingStream(System.IO.Stream stream)
|
||||
: base()
|
||||
{
|
||||
_s = stream;
|
||||
try
|
||||
{
|
||||
_initialOffset = _s.Position;
|
||||
}
|
||||
catch
|
||||
{
|
||||
_initialOffset = 0L;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the wrapped stream.
|
||||
/// </summary>
|
||||
public Stream WrappedStream
|
||||
{
|
||||
get
|
||||
{
|
||||
return _s;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The count of bytes written out to the stream.
|
||||
/// </summary>
|
||||
public Int64 BytesWritten
|
||||
{
|
||||
get { return _bytesWritten; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// the count of bytes that have been read from the stream.
|
||||
/// </summary>
|
||||
public Int64 BytesRead
|
||||
{
|
||||
get { return _bytesRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjust the byte count on the stream.
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name='delta'>
|
||||
/// the number of bytes to subtract from the count.
|
||||
/// </param>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Subtract delta from the count of bytes written to the stream.
|
||||
/// This is necessary when seeking back, and writing additional data,
|
||||
/// as happens in some cases when saving Zip files.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public void Adjust(Int64 delta)
|
||||
{
|
||||
_bytesWritten -= delta;
|
||||
if (_bytesWritten < 0)
|
||||
throw new InvalidOperationException();
|
||||
if (_s as CountingStream != null)
|
||||
((CountingStream)_s).Adjust(delta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The read method.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to hold the data read from the stream.</param>
|
||||
/// <param name="offset">the offset within the buffer to copy the first byte read.</param>
|
||||
/// <param name="count">the number of bytes to read.</param>
|
||||
/// <returns>the number of bytes read, after decryption and decompression.</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int n = _s.Read(buffer, offset, count);
|
||||
_bytesRead += n;
|
||||
return n;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write data into the stream.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer holding data to write to the stream.</param>
|
||||
/// <param name="offset">the offset within that data array to find the first byte to write.</param>
|
||||
/// <param name="count">the number of bytes to write.</param>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count == 0) return;
|
||||
_s.Write(buffer, offset, count);
|
||||
_bytesWritten += count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the stream can be read.
|
||||
/// </summary>
|
||||
public override bool CanRead
|
||||
{
|
||||
get { return _s.CanRead; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether it is possible to call Seek() on the stream.
|
||||
/// </summary>
|
||||
public override bool CanSeek
|
||||
{
|
||||
get { return _s.CanSeek; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether it is possible to call Write() on the stream.
|
||||
/// </summary>
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return _s.CanWrite; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the underlying stream.
|
||||
/// </summary>
|
||||
public override void Flush()
|
||||
{
|
||||
_s.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The length of the underlying stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { return _s.Length; } // bytesWritten??
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the sum of number of bytes written, plus the initial
|
||||
/// offset before writing.
|
||||
/// </summary>
|
||||
public long ComputedPosition
|
||||
{
|
||||
get { return _initialOffset + _bytesWritten; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The Position of the stream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get { return _s.Position; }
|
||||
set
|
||||
{
|
||||
_s.Seek(value, System.IO.SeekOrigin.Begin);
|
||||
// workitem 10178
|
||||
Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_s);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seek in the stream.
|
||||
/// </summary>
|
||||
/// <param name="offset">the offset point to seek to</param>
|
||||
/// <param name="origin">the reference point from which to seek</param>
|
||||
/// <returns>The new position</returns>
|
||||
public override long Seek(long offset, System.IO.SeekOrigin origin)
|
||||
{
|
||||
return _s.Seek(offset, origin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the length of the underlying stream. Be careful with this!
|
||||
/// </summary>
|
||||
///
|
||||
/// <param name='value'>the length to set on the underlying stream.</param>
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
_s.SetLength(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue