Initial code commit.
							parent
							
								
									a356722e98
								
							
						
					
					
						commit
						9ce60b396f
					
				|  | @ -0,0 +1,22 @@ | |||
|  | ||||
| Microsoft Visual Studio Solution File, Format Version 12.00 | ||||
| # Visual Studio 2013 | ||||
| VisualStudioVersion = 12.0.30723.0 | ||||
| MinimumVisualStudioVersion = 10.0.40219.1 | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GarrysMod.AddonCreator", "GarrysMod.AddonCreator\GarrysMod.AddonCreator.csproj", "{E33D031D-7866-40F9-9362-2776CB4A7B5B}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| 		Debug|Any CPU = Debug|Any CPU | ||||
| 		Release|Any CPU = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||||
| 		{E33D031D-7866-40F9-9362-2776CB4A7B5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{E33D031D-7866-40F9-9362-2776CB4A7B5B}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{E33D031D-7866-40F9-9362-2776CB4A7B5B}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{E33D031D-7866-40F9-9362-2776CB4A7B5B}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
| 	EndGlobalSection | ||||
| EndGlobal | ||||
|  | @ -0,0 +1,255 @@ | |||
|  | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using CRC32; | ||||
| 
 | ||||
| namespace GarrysMod.AddonCreator | ||||
| { | ||||
|     public class Addon | ||||
|     { | ||||
|         private static readonly byte[] FormatIdent = Encoding.ASCII.GetBytes("GMAD"); | ||||
|         private const byte FormatVersion = 3; | ||||
|         private const uint AppID = 4000; | ||||
|         private const uint CompressionSignature = 0xbeefcace; | ||||
| 
 | ||||
|         public static void CreateFromFolder() | ||||
|         { | ||||
|              | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Imports a gmod addon into this instance. | ||||
|         /// </summary> | ||||
|         /// <param name="path">Path to a gmod addon file.</param> | ||||
|         /// <param name="withMetadata">Import all metadata (title, description, creator, timestamp, etc.) as well?</param> | ||||
|         public void Import(string path, bool withMetadata = true) | ||||
|         { | ||||
|             using (var stream = File.OpenRead(path)) | ||||
|             { | ||||
|                 var sr = new BinaryReader(stream, Encoding.GetEncoding("windows-1252")); | ||||
| 
 | ||||
|                 // Check format header | ||||
|                 if (!sr.ReadBytes(4).SequenceEqual(FormatIdent) | ||||
|                     || sr.ReadByte() != FormatVersion) | ||||
|                     throw new FormatException(); | ||||
| 
 | ||||
|                 // Check addon's CRC32 hash | ||||
|                 { | ||||
|                     var baseAddon = new byte[stream.Length - sizeof (int)]; | ||||
|                     var oldpos = stream.Position; | ||||
|                     stream.Position = 0; | ||||
|                     stream.Read(baseAddon, 0, baseAddon.Length); | ||||
|                     var baseAddonHash = sr.ReadInt32(); | ||||
|                     if (ParallelCRC.Compute(baseAddon) != baseAddonHash) | ||||
|                     { | ||||
|                         throw new IOException("Data corrupted (calculated hash mismatching hash in addon file)"); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 // Import metadata | ||||
|                 var newSteamID = sr.ReadUInt64(); | ||||
|                 var newBuildTimestamp = sr.ReadUInt64(); | ||||
|                 var newRequiredContentLen = sr.ReadByte(); | ||||
|                 for (var b = 0; b < newRequiredContentLen; b++) | ||||
|                 { | ||||
|                     var value = sr.ReadString(true); | ||||
|                     if (withMetadata && !RequiredContent.Contains(value)) | ||||
|                         RequiredContent.Add(value); | ||||
|                 } | ||||
|                 if (withMetadata) | ||||
|                 { | ||||
|                     SteamID = newSteamID; | ||||
|                     BuildTimestamp = newBuildTimestamp; | ||||
|                 } | ||||
| 
 | ||||
|                 // file list | ||||
|                 var newFilesList = new Dictionary<string, Tuple<long, int>>(); | ||||
|                 do | ||||
|                 { | ||||
|                     var fileId = sr.ReadUInt32(); | ||||
|                     if (fileId == 0) | ||||
|                         break; // end of list | ||||
| 
 | ||||
|                     // key, size, hash | ||||
|                     var filePath = sr.ReadString(true); | ||||
|                     var fileSize = sr.ReadInt64(); | ||||
|                     var fileHash = sr.ReadInt32(); | ||||
| 
 | ||||
|                     // avoid duplicates | ||||
|                     if (newFilesList.ContainsKey(filePath)) | ||||
|                     { | ||||
|                         throw new IOException("Found duplicate file path in addon file. Contact the addon creator and tell him to build a new proper addon file."); | ||||
|                     } | ||||
| 
 | ||||
|                     newFilesList.Add(filePath, new Tuple<long, int>(fileSize, fileHash)); | ||||
|                 } while (true); | ||||
| 
 | ||||
|                 foreach (var file in newFilesList) | ||||
|                 { | ||||
|                     var filePath = file.Key; | ||||
|                     var fileSize = file.Value.Item1; | ||||
|                     var fileHash = file.Value.Item2; | ||||
|                     var fileContent = new byte[fileSize]; | ||||
|                     | ||||
|                     // long-compatible file reading | ||||
|                     for (long i = 0; i < fileSize; i += int.MaxValue) | ||||
|                     { | ||||
|                         var tempContent = sr.ReadBytes((int)Math.Min(int.MaxValue, fileSize)); | ||||
|                         tempContent.CopyTo(fileContent, i); | ||||
|                     } | ||||
| 
 | ||||
|                     // CRC check for this file | ||||
|                     var fileCalcHash = ParallelCRC.Compute(fileContent); | ||||
|                     if (fileCalcHash != fileHash) | ||||
|                     { | ||||
|                         throw new IOException("File " + filePath + " in addon file is corrupted (hash mismatch)"); | ||||
|                     } | ||||
| 
 | ||||
|                     Files.Add(filePath, new SegmentedAddonFileInfo(stream, sr.BaseStream.Position, fileSize, fileHash)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Returns the timestamp of when the addon was built. This data is retrieved from full imports and for new (unsaved) addons this is 0. | ||||
|         /// </summary> | ||||
|         public ulong BuildTimestamp { get; private set; } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Exports this addon into a GMA file. | ||||
|         /// </summary> | ||||
|         /// <param name="path">The output file path, should be pointing to a writable location ending with ".gma".</param> | ||||
|         public void Export(string path) | ||||
|         { | ||||
|             // TODO: Enforce .gma file extension | ||||
| 
 | ||||
|             // Checking for existing addon.json | ||||
|             if (!Files.ContainsKey("addon.json")) | ||||
|             { | ||||
|                 throw new FileNotFoundException("Addon building requires a valid addon.json file."); | ||||
|             } | ||||
| 
 | ||||
|             // TODO: Check addon.json for errors | ||||
|             // TODO: Ignore or remove files marked to be ignored in addon.json | ||||
|             // TODO: Sort in alphabetic order | ||||
|             // TODO: Filter files by general whitelist | ||||
| 
 | ||||
|             using (var stream = new MemoryStream()) | ||||
|             { | ||||
|                 // TODO: Standardized encoding - Garry should use standardized encoding, currently he uses Encoding.Default which is applocale-dependent... | ||||
|                 var sw = new BinaryWriter(stream, Encoding.GetEncoding("windows-1252")); | ||||
| 
 | ||||
|                 // Format header | ||||
|                 sw.Write(FormatIdent); | ||||
|                 sw.Write(FormatVersion); | ||||
| 
 | ||||
|                 // Creator steam ID | ||||
|                 sw.Write(SteamID); | ||||
| 
 | ||||
|                 // Build timestamp | ||||
|                 sw.Write(BuildTimestamp = (ulong)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds); | ||||
| 
 | ||||
|                 // Required content | ||||
|                 if (RequiredContent.Count > byte.MaxValue) | ||||
|                 { | ||||
|                     throw new IndexOutOfRangeException("Required content count must not exceed " + byte.MaxValue + " entries."); | ||||
|                 } | ||||
|                 sw.Write((byte)RequiredContent.Count); | ||||
|                 foreach (string content in RequiredContent) | ||||
|                 { | ||||
|                     sw.Write(content, true); | ||||
|                 } | ||||
| 
 | ||||
|                 // Metadata | ||||
|                 sw.Write(Title, true); | ||||
|                 sw.Write(Description, true); | ||||
|                 sw.Write(Author, true); | ||||
|                 sw.Write(Version); | ||||
| 
 | ||||
|                 // File list | ||||
|                 if (Files.Count > uint.MaxValue) | ||||
|                 { | ||||
|                     throw new IndexOutOfRangeException("Number of addon files must not exceed " + uint.MaxValue + " elements."); | ||||
|                 } | ||||
|                 uint fileNum = 0; | ||||
|                 foreach (var file in Files) | ||||
|                 { | ||||
|                     fileNum++; | ||||
|                     sw.Write(fileNum); | ||||
|                     sw.Write(file.Key.ToLower(), true); // Path | ||||
|                     sw.Write(file.Value.Size); | ||||
|                     sw.Write(file.Value.Crc32Hash); | ||||
|                 } | ||||
|                 sw.Write((uint)0); // End of file list | ||||
| 
 | ||||
|                 // File contents | ||||
|                 foreach (var file in Files) | ||||
|                 { | ||||
|                     if (file.Value.Size == 0) | ||||
|                         continue; | ||||
| 
 | ||||
|                     sw.Write(file.Value.GetContents()); | ||||
|                 } | ||||
| 
 | ||||
|                 // Addon CRC | ||||
|                 var addonHash = ParallelCRC.Compute(stream.ToArray()); | ||||
|                 sw.Write(addonHash); | ||||
| 
 | ||||
|                 using (var outfile = File.Create(path)) | ||||
|                 { | ||||
|                     stream.CopyTo(outfile); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The name of this addon. | ||||
|         /// </summary> | ||||
|         public string Title { get; set; } | ||||
|          | ||||
|         /// <summary> | ||||
|         /// The author of this addon. | ||||
|         /// </summary> | ||||
|         public string Author { get; set; } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// A description of this addon. | ||||
|         /// </summary> | ||||
|         public string Description { get; set; } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// This addon's version. | ||||
|         /// </summary> | ||||
|         public int Version { get; set; } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// The files to include in the addon. | ||||
|         /// </summary> | ||||
|         public Dictionary<string, AddonFileInfo> Files { get; private set; } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Currently unused. | ||||
|         /// </summary> | ||||
|         public ulong SteamID { get; set; } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Content that needs to exist in order to run this addon. | ||||
|         /// </summary> | ||||
|         public List<string> RequiredContent { get; private set; }  | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Initializes a new instance of <see cref="Addon"/> | ||||
|         /// </summary> | ||||
|         public Addon() | ||||
|         { | ||||
|             Files = new Dictionary<string, AddonFileInfo>(); | ||||
|             RequiredContent = new List<string>(); | ||||
|             Version = 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // TODO: Newtonsoft.Json reference | ||||
| } | ||||
|  | @ -0,0 +1,16 @@ | |||
| using CRC32; | ||||
| 
 | ||||
| namespace GarrysMod.AddonCreator | ||||
| { | ||||
|     public abstract class AddonFileInfo | ||||
|     { | ||||
|         private long? _size; | ||||
|         private int? _hash; | ||||
| 
 | ||||
|         public virtual long Size { get { return _size.HasValue ? _size.Value : (_size = GetContents().Length).Value; } } | ||||
| 
 | ||||
|         public virtual int Crc32Hash { get { return _hash.HasValue ? _hash.Value : (_hash = ParallelCRC.Compute(GetContents())).Value; } } | ||||
| 
 | ||||
|         public abstract byte[] GetContents(); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,37 @@ | |||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| 
 | ||||
| namespace GarrysMod.AddonCreator | ||||
| { | ||||
|     static class BinaryCodecExt | ||||
|     { | ||||
|         public static string ReadString(this BinaryReader br, bool nullTerminated) | ||||
|         { | ||||
|             if (!nullTerminated) | ||||
|                 return br.ReadString(); | ||||
| 
 | ||||
|             var sb = new StringBuilder(); | ||||
|             do | ||||
|             { | ||||
|                 var c = br.ReadChar(); | ||||
|                 if (c == 0) | ||||
|                     break; | ||||
|                 sb.Append(c); | ||||
|             } while (true); | ||||
|             return sb.ToString(); | ||||
|         } | ||||
| 
 | ||||
|         public static void Write(this BinaryWriter bw, string value, bool nullTerminated) | ||||
|         { | ||||
|             if (nullTerminated) | ||||
|             { | ||||
|                 value += "\0"; | ||||
|             } | ||||
| 
 | ||||
|             bw.Write(value); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,71 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||||
|   <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | ||||
|   <PropertyGroup> | ||||
|     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||||
|     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||||
|     <ProjectGuid>{E33D031D-7866-40F9-9362-2776CB4A7B5B}</ProjectGuid> | ||||
|     <OutputType>Exe</OutputType> | ||||
|     <AppDesignerFolder>Properties</AppDesignerFolder> | ||||
|     <RootNamespace>GarrysMod.AddonCreator</RootNamespace> | ||||
|     <AssemblyName>GarrysMod.AddonCreator</AssemblyName> | ||||
|     <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> | ||||
|     <FileAlignment>512</FileAlignment> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||||
|     <PlatformTarget>AnyCPU</PlatformTarget> | ||||
|     <DebugSymbols>true</DebugSymbols> | ||||
|     <DebugType>full</DebugType> | ||||
|     <Optimize>false</Optimize> | ||||
|     <OutputPath>bin\Debug\</OutputPath> | ||||
|     <DefineConstants>DEBUG;TRACE</DefineConstants> | ||||
|     <ErrorReport>prompt</ErrorReport> | ||||
|     <WarningLevel>4</WarningLevel> | ||||
|   </PropertyGroup> | ||||
|   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||||
|     <PlatformTarget>AnyCPU</PlatformTarget> | ||||
|     <DebugType>pdbonly</DebugType> | ||||
|     <Optimize>true</Optimize> | ||||
|     <OutputPath>bin\Release\</OutputPath> | ||||
|     <DefineConstants>TRACE</DefineConstants> | ||||
|     <ErrorReport>prompt</ErrorReport> | ||||
|     <WarningLevel>4</WarningLevel> | ||||
|   </PropertyGroup> | ||||
|   <ItemGroup> | ||||
|     <Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> | ||||
|       <SpecificVersion>False</SpecificVersion> | ||||
|       <HintPath>..\packages\Newtonsoft.Json.6.0.5\lib\net40\Newtonsoft.Json.dll</HintPath> | ||||
|     </Reference> | ||||
|     <Reference Include="System" /> | ||||
|     <Reference Include="System.Core" /> | ||||
|     <Reference Include="System.Xml.Linq" /> | ||||
|     <Reference Include="System.Data.DataSetExtensions" /> | ||||
|     <Reference Include="Microsoft.CSharp" /> | ||||
|     <Reference Include="System.Data" /> | ||||
|     <Reference Include="System.Xml" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Compile Include="Addon.cs" /> | ||||
|     <Compile Include="AddonFileInfo.cs" /> | ||||
|     <Compile Include="BinaryCodecExt.cs" /> | ||||
|     <Compile Include="JsonAddonFileInfo.cs" /> | ||||
|     <Compile Include="OptimizedCRC.cs" /> | ||||
|     <Compile Include="ParallelCRC.cs" /> | ||||
|     <Compile Include="PhysicalAddonFileInfo.cs" /> | ||||
|     <Compile Include="Program.cs" /> | ||||
|     <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
|     <Compile Include="SegmentedAddonFileInfo.cs" /> | ||||
|     <Compile Include="TraditionalCRC.cs" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <None Include="packages.config" /> | ||||
|   </ItemGroup> | ||||
|   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||||
|   <!-- To modify your build process, add your task inside one of the targets below and uncomment it.  | ||||
|        Other similar extension points exist, see Microsoft.Common.targets. | ||||
|   <Target Name="BeforeBuild"> | ||||
|   </Target> | ||||
|   <Target Name="AfterBuild"> | ||||
|   </Target> | ||||
|   --> | ||||
| </Project> | ||||
|  | @ -0,0 +1,20 @@ | |||
| using System.Text; | ||||
| using Newtonsoft.Json; | ||||
| 
 | ||||
| namespace GarrysMod.AddonCreator | ||||
| { | ||||
|     public class JsonAddonFileInfo : AddonFileInfo | ||||
|     { | ||||
|         private readonly byte[] _serializedJson; | ||||
| 
 | ||||
|         public JsonAddonFileInfo(object obj) | ||||
|         { | ||||
|             _serializedJson = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(obj)); | ||||
|         } | ||||
| 
 | ||||
|         public override byte[] GetContents() | ||||
|         { | ||||
|             return _serializedJson; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,121 @@ | |||
| using System; | ||||
| using System.IO; | ||||
| 
 | ||||
| namespace CRC32 | ||||
| { | ||||
|     public class OptimizedCRC | ||||
|     { | ||||
|         private const uint kCrcPoly = 0xEDB88320; | ||||
|         private const uint kInitial = 0xFFFFFFFF; | ||||
|         private static readonly uint[] Table; | ||||
|         private const uint CRC_NUM_TABLES = 8; | ||||
| 
 | ||||
|         static OptimizedCRC() | ||||
|         { | ||||
|             unchecked | ||||
|             { | ||||
|                 Table = new uint[256 * CRC_NUM_TABLES]; | ||||
|                 uint i; | ||||
|                 for (i = 0; i < 256; i++) | ||||
|                 { | ||||
|                     uint r = i; | ||||
|                     for (int j = 0; j < 8; j++) | ||||
|                         r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); | ||||
|                     Table[i] = r; | ||||
|                 } | ||||
|                 for (; i < 256 * CRC_NUM_TABLES; i++) | ||||
|                 { | ||||
|                     uint r = Table[i - 256]; | ||||
|                     Table[i] = Table[r & 0xFF] ^ (r >> 8); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private uint value; | ||||
| 
 | ||||
|         public OptimizedCRC() | ||||
|         { | ||||
|             Init(); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Reset CRC | ||||
|         /// </summary> | ||||
|         public void Init() | ||||
|         { | ||||
|             value = kInitial; | ||||
|         } | ||||
| 
 | ||||
|         public int Value | ||||
|         { | ||||
|             get { return (int)~value; } | ||||
|         } | ||||
| 
 | ||||
|         public void UpdateByte(byte b) | ||||
|         { | ||||
|             value = (value >> 8) ^ Table[(byte)value ^ b]; | ||||
|         } | ||||
| 
 | ||||
|         public void Update(byte[] data, int offset, int count) | ||||
|         { | ||||
|             new ArraySegment<byte>(data, offset, count);     // check arguments | ||||
|             if (count == 0) return; | ||||
| 
 | ||||
|             var table = OptimizedCRC.Table;        // important for performance! | ||||
| 
 | ||||
|             uint crc = value; | ||||
| 
 | ||||
|             for (; (offset & 7) != 0 && count != 0; count--) | ||||
|                 crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]]; | ||||
| 
 | ||||
|             if (count >= 8) | ||||
|             { | ||||
|                 /* | ||||
|                  * Idea from 7-zip project sources (http://7-zip.org/sdk.html) | ||||
|                  */ | ||||
| 
 | ||||
|                 int to = (count - 8) & ~7; | ||||
|                 count -= to; | ||||
|                 to += offset; | ||||
| 
 | ||||
|                 while (offset != to) | ||||
|                 { | ||||
|                     crc ^= (uint)(data[offset] + (data[offset + 1] << 8) + (data[offset + 2] << 16) + (data[offset + 3] << 24)); | ||||
|                     uint high = (uint)(data[offset + 4] + (data[offset + 5] << 8) + (data[offset + 6] << 16) + (data[offset + 7] << 24)); | ||||
|                     offset += 8; | ||||
| 
 | ||||
|                     crc = table[(byte)crc + 0x700] | ||||
|                         ^ table[(byte)(crc >>= 8) + 0x600] | ||||
|                         ^ table[(byte)(crc >>= 8) + 0x500] | ||||
|                         ^ table[/*(byte)*/(crc >> 8) + 0x400] | ||||
|                         ^ table[(byte)(high) + 0x300] | ||||
|                         ^ table[(byte)(high >>= 8) + 0x200] | ||||
|                         ^ table[(byte)(high >>= 8) + 0x100] | ||||
|                         ^ table[/*(byte)*/(high >> 8) + 0x000]; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             while (count-- != 0) | ||||
|                 crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]]; | ||||
| 
 | ||||
|             value = crc; | ||||
|         } | ||||
| 
 | ||||
|         static public int Compute(byte[] data, int offset, int size) | ||||
|         { | ||||
|             var crc = new OptimizedCRC(); | ||||
|             crc.Update(data, offset, size); | ||||
|             return crc.Value; | ||||
|         } | ||||
| 
 | ||||
|         static public int Compute(byte[] data) | ||||
|         { | ||||
|             return Compute(data, 0, data.Length); | ||||
|         } | ||||
| 
 | ||||
|         static public int Compute(ArraySegment<byte> block) | ||||
|         { | ||||
|             return Compute(block.Array, block.Offset, block.Count); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,305 @@ | |||
| using System; | ||||
| using System.IO; | ||||
| using System.Threading; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace CRC32 | ||||
| { | ||||
|     public class ParallelCRC | ||||
|     { | ||||
|         private const uint kCrcPoly = 0xEDB88320; | ||||
|         private const uint kInitial = 0xFFFFFFFF; | ||||
|         private static readonly uint[] Table; | ||||
|         private const int CRC_NUM_TABLES = 8; | ||||
| 
 | ||||
|         private const int ThreadCost = 256 << 10; | ||||
|         private static int ThreadCount = Environment.ProcessorCount; | ||||
| 
 | ||||
| 
 | ||||
|         static ParallelCRC() | ||||
|         { | ||||
|             unchecked | ||||
|             { | ||||
|                 Table = new uint[256 * CRC_NUM_TABLES]; | ||||
|                 int i; | ||||
|                 for (i = 0; i < 256; i++) | ||||
|                 { | ||||
|                     uint r = (uint)i; | ||||
|                     for (int j = 0; j < 8; j++) | ||||
|                         r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); | ||||
|                     Table[i] = r; | ||||
|                 } | ||||
|                 for (; i < 256 * CRC_NUM_TABLES; i++) | ||||
|                 { | ||||
|                     uint r = Table[i - 256]; | ||||
|                     Table[i] = Table[r & 0xFF] ^ (r >> 8); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private uint value; | ||||
| 
 | ||||
|         public ParallelCRC() | ||||
|         { | ||||
|             Init(); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Reset CRC | ||||
|         /// </summary> | ||||
|         public void Init() | ||||
|         { | ||||
|             value = kInitial; | ||||
|         } | ||||
| 
 | ||||
|         public int Value | ||||
|         { | ||||
|             get { return (int)~value; } | ||||
|         } | ||||
| 
 | ||||
|         public void UpdateByte(byte b) | ||||
|         { | ||||
|             value = (value >> 8) ^ Table[(byte)value ^ b]; | ||||
|         } | ||||
| 
 | ||||
|         public void Update(byte[] data, int offset, int count) | ||||
|         { | ||||
|             new ArraySegment<byte>(data, offset, count);     // check arguments | ||||
| 
 | ||||
|             if (count <= ThreadCost || ThreadCount <= 1) | ||||
|             { | ||||
|                 value = ProcessBlock(value, data, offset, count); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             // choose optimal number of threads to use | ||||
| 
 | ||||
|             int threadCount = ThreadCount; | ||||
|         L0: | ||||
|             int bytesPerThread = (count + threadCount - 1) / threadCount; | ||||
|             if (bytesPerThread < ThreadCost >> 1) | ||||
|             { | ||||
|                 threadCount--; | ||||
|                 goto L0; | ||||
|             } | ||||
| 
 | ||||
|             // threadCount >= 2 | ||||
| 
 | ||||
|             Job lastJob = null; | ||||
|             while (count > bytesPerThread) | ||||
|             { | ||||
|                 var job = new Job(new ArraySegment<byte>(data, offset, bytesPerThread), this, lastJob); | ||||
|                 ThreadPool.QueueUserWorkItem(job.Do); | ||||
|                 offset += bytesPerThread; | ||||
|                 count -= bytesPerThread; | ||||
|                 lastJob = job; | ||||
|             } | ||||
| 
 | ||||
|             // lastJob != null | ||||
|             var lastBlockCRC = ProcessBlock(kInitial, data, offset, count); | ||||
|             lastJob.WaitAndDispose(); | ||||
|             value = Combine(value, lastBlockCRC, count); | ||||
|         } | ||||
| 
 | ||||
|         private static uint ProcessBlock(uint crc, byte[] data, int offset, int count) | ||||
|         { | ||||
|             /* | ||||
|              * A copy of Optimized implementation. | ||||
|              */ | ||||
| 
 | ||||
|             if (count < 0) throw new ArgumentOutOfRangeException("count"); | ||||
|             if (count == 0) return crc; | ||||
| 
 | ||||
|             var table = ParallelCRC.Table; | ||||
| 
 | ||||
|             for (; (offset & 7) != 0 && count != 0; count--) | ||||
|                 crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]]; | ||||
| 
 | ||||
|             if (count >= 8) | ||||
|             { | ||||
|                 int to = (count - 8) & ~7; | ||||
|                 count -= to; | ||||
|                 to += offset; | ||||
| 
 | ||||
|                 while (offset != to) | ||||
|                 { | ||||
|                     crc ^= (uint)(data[offset] + (data[offset + 1] << 8) + (data[offset + 2] << 16) + (data[offset + 3] << 24)); | ||||
|                     uint high = (uint)(data[offset + 4] + (data[offset + 5] << 8) + (data[offset + 6] << 16) + (data[offset + 7] << 24)); | ||||
|                     offset += 8; | ||||
| 
 | ||||
|                     crc = table[(byte)crc + 0x700] | ||||
|                         ^ table[(byte)(crc >>= 8) + 0x600] | ||||
|                         ^ table[(byte)(crc >>= 8) + 0x500] | ||||
|                         ^ table[/*(byte)*/(crc >> 8) + 0x400] | ||||
|                         ^ table[(byte)(high) + 0x300] | ||||
|                         ^ table[(byte)(high >>= 8) + 0x200] | ||||
|                         ^ table[(byte)(high >>= 8) + 0x100] | ||||
|                         ^ table[/*(byte)*/(high >> 8) + 0x000]; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             while (count-- != 0) | ||||
|                 crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]]; | ||||
| 
 | ||||
|             return crc; | ||||
|         } | ||||
| 
 | ||||
|         static public int Compute(byte[] data, int offset, int count) | ||||
|         { | ||||
|             var crc = new ParallelCRC(); | ||||
|             crc.Update(data, offset, count); | ||||
|             return crc.Value; | ||||
|         } | ||||
| 
 | ||||
|         static public int Compute(byte[] data) | ||||
|         { | ||||
|             return Compute(data, 0, data.Length); | ||||
|         } | ||||
| 
 | ||||
|         static public int Compute(ArraySegment<byte> block) | ||||
|         { | ||||
|             return Compute(block.Array, block.Offset, block.Count); | ||||
|         } | ||||
| 
 | ||||
|         #region Combining | ||||
| 
 | ||||
|         /* | ||||
|          * CRC values combining algorithm. | ||||
|          * Taken from DotNetZip project sources (http://dotnetzip.codeplex.com/) | ||||
|          */ | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// This function is thread-safe! | ||||
|         /// </summary> | ||||
|         private static uint Combine(uint crc1, uint crc2, int length2) | ||||
|         { | ||||
|             if (length2 <= 0) return crc1; | ||||
|             if (crc1 == kInitial) return crc2; | ||||
| 
 | ||||
|             if (even_cache == null) | ||||
|             { | ||||
|                 Prepare_even_odd_Cache(); | ||||
|             } | ||||
| 
 | ||||
|             uint[] even = CopyArray(even_cache); | ||||
|             uint[] odd = CopyArray(odd_cache); | ||||
| 
 | ||||
|             crc1 = ~crc1; | ||||
|             crc2 = ~crc2; | ||||
| 
 | ||||
|             uint len2 = (uint)length2; | ||||
| 
 | ||||
|             // apply len2 zeros to crc1 (first square will put the operator for one | ||||
|             // zero byte, eight zero bits, in even) | ||||
|             do | ||||
|             { | ||||
|                 // apply zeros operator for this bit of len2 | ||||
|                 gf2_matrix_square(even, odd); | ||||
| 
 | ||||
|                 if ((len2 & 1) != 0) crc1 = gf2_matrix_times(even, crc1); | ||||
|                 len2 >>= 1; | ||||
| 
 | ||||
|                 if (len2 == 0) break; | ||||
| 
 | ||||
|                 // another iteration of the loop with odd and even swapped | ||||
|                 gf2_matrix_square(odd, even); | ||||
|                 if ((len2 & 1) != 0) crc1 = gf2_matrix_times(odd, crc1); | ||||
|                 len2 >>= 1; | ||||
|             } while (len2 != 0); | ||||
| 
 | ||||
|             crc1 ^= crc2; | ||||
|             return ~crc1; | ||||
|         } | ||||
| 
 | ||||
|         private static uint[] even_cache = null; | ||||
|         private static uint[] odd_cache; | ||||
| 
 | ||||
|         private static void Prepare_even_odd_Cache() | ||||
|         { | ||||
|             var even = new uint[32];     // even-power-of-two zeros operator | ||||
|             var odd = new uint[32];      // odd-power-of-two zeros operator | ||||
| 
 | ||||
|             // put operator for one zero bit in odd | ||||
|             odd[0] = kCrcPoly;  // the CRC-32 polynomial | ||||
|             for (int i = 1; i < 32; i++) odd[i] = 1U << (i - 1); | ||||
| 
 | ||||
|             // put operator for two zero bits in even | ||||
|             gf2_matrix_square(even, odd); | ||||
| 
 | ||||
|             // put operator for four zero bits in odd | ||||
|             gf2_matrix_square(odd, even); | ||||
| 
 | ||||
|             odd_cache = odd; | ||||
|             even_cache = even; | ||||
|         } | ||||
| 
 | ||||
|         /// <param name="matrix">will not be modified</param> | ||||
|         private static uint gf2_matrix_times(uint[] matrix, uint vec) | ||||
|         { | ||||
|             uint sum = 0; | ||||
|             int i = 0; | ||||
|             while (vec != 0) | ||||
|             { | ||||
|                 if ((vec & 1) != 0) sum ^= matrix[i]; | ||||
|                 vec >>= 1; | ||||
|                 i++; | ||||
|             } | ||||
|             return sum; | ||||
|         } | ||||
| 
 | ||||
|         /// <param name="square">this array will be modified!</param> | ||||
|         /// <param name="mat">will not be modified</param> | ||||
|         private static void gf2_matrix_square(uint[] square, uint[] mat) | ||||
|         { | ||||
|             for (int i = 0; i < 32; i++) | ||||
|                 square[i] = gf2_matrix_times(mat, mat[i]); | ||||
|         } | ||||
| 
 | ||||
|         private static uint[] CopyArray(uint[] a) | ||||
|         { | ||||
|             var b = new uint[a.Length]; | ||||
|             Buffer.BlockCopy(a, 0, b, 0, a.Length * sizeof(uint)); | ||||
|             return b; | ||||
|         } | ||||
| 
 | ||||
|         #endregion Combining | ||||
| 
 | ||||
|         class Job | ||||
|         { | ||||
|             private ArraySegment<byte> data; | ||||
|             private Job waitForJob; | ||||
|             private ParallelCRC accumulator; | ||||
| 
 | ||||
|             private ManualResetEventSlim finished; | ||||
| 
 | ||||
|             public Job(ArraySegment<byte> data, ParallelCRC accumulator, Job waitForJob) | ||||
|             { | ||||
|                 this.data = data; | ||||
|                 this.accumulator = accumulator; | ||||
|                 this.waitForJob = waitForJob; | ||||
|                 this.finished = new ManualResetEventSlim(false); | ||||
|             } | ||||
| 
 | ||||
|             public void Do(object arg) | ||||
|             { | ||||
|                 var crc = ProcessBlock(kInitial, data.Array, data.Offset, data.Count); | ||||
|                 if (waitForJob != null) waitForJob.WaitAndDispose(); | ||||
|                 accumulator.value = Combine(accumulator.value, crc, data.Count); | ||||
|                 finished.Set(); | ||||
|             } | ||||
| 
 | ||||
|             public void WaitAndDispose() | ||||
|             { | ||||
|                 finished.Wait(); | ||||
|                 Dispose(); | ||||
|             } | ||||
| 
 | ||||
|             public void Dispose() | ||||
|             { | ||||
|                 if (finished != null) finished.Dispose(); | ||||
|                 finished = null; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,24 @@ | |||
| using System.IO; | ||||
| 
 | ||||
| namespace GarrysMod.AddonCreator | ||||
| { | ||||
|     public class PhysicalAddonFileInfo : AddonFileInfo | ||||
|     { | ||||
|         public PhysicalAddonFileInfo(string path) | ||||
|         { | ||||
|             _fi = new FileInfo(path); | ||||
|         } | ||||
| 
 | ||||
|         private FileInfo _fi; | ||||
| 
 | ||||
|         public override long Size | ||||
|         { | ||||
|             get { return _fi.Length; } | ||||
|         } | ||||
| 
 | ||||
|         public override byte[] GetContents() | ||||
|         { | ||||
|             return File.ReadAllBytes(_fi.FullName); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,116 @@ | |||
| using System; | ||||
| using System.IO; | ||||
| using System.Linq; | ||||
| 
 | ||||
| namespace GarrysMod.AddonCreator | ||||
| { | ||||
|     internal static class Program | ||||
|     { | ||||
|         private static void Main(string[] args) | ||||
|         { | ||||
|             switch (args[0]) | ||||
|             { | ||||
|                 case "create": | ||||
|                 { | ||||
|                     var folder = new DirectoryInfo(args[1]); | ||||
|                     var output = args[2]; | ||||
|                     var addon = new Addon(); | ||||
| 
 | ||||
|                     // recursively add files | ||||
|                     foreach (var file in folder.EnumerateFiles("*", SearchOption.AllDirectories)) | ||||
|                     { | ||||
|                         var relpath = MakeRelativePath(folder.FullName, file.FullName).Replace(Path.DirectorySeparatorChar, '/'); | ||||
|                         Console.WriteLine("Adding: {0}", relpath); | ||||
| 
 | ||||
|                         addon.Files.Add(relpath, new PhysicalAddonFileInfo(file.FullName)); | ||||
|                     } | ||||
| 
 | ||||
|                     // create addon | ||||
|                     Console.WriteLine("Exporting addon..."); | ||||
|                     addon.Export(output); | ||||
| 
 | ||||
|                     Console.WriteLine("Done."); | ||||
|                     break; | ||||
|                 } | ||||
|                 case "extract": | ||||
|                 { | ||||
|                     var gma = args[1]; | ||||
|                     var folder = new DirectoryInfo(args[2]); | ||||
|                     var addon = new Addon(); | ||||
|                     addon.Import(gma); | ||||
| 
 | ||||
|                     Console.WriteLine("Loaded addon {0} by {1}, Version {2}", addon.Title, addon.Author, addon.Version); | ||||
| 
 | ||||
|                     // extract files | ||||
|                     foreach (var file in addon.Files) | ||||
|                     { | ||||
|                         var relpath = file.Key; | ||||
|                         var targetFile = new FileInfo(Path.Combine(folder.FullName, relpath.Replace('/', Path.DirectorySeparatorChar))); | ||||
| 
 | ||||
|                         Console.WriteLine("Extracting: {0}", relpath); | ||||
| 
 | ||||
|                         // create directory | ||||
|                         var dir = targetFile.Directory; | ||||
|                         if (dir == null) | ||||
|                             continue; // I still need to think about the weird logic here | ||||
|                         dir.Create(); | ||||
| 
 | ||||
|                         // create file | ||||
|                         using (var fs = targetFile.Create()) | ||||
|                         { | ||||
|                             var buffer = file.Value.GetContents(); | ||||
| 
 | ||||
|                             // long-compatible copy algorithm | ||||
|                             for (long i = 0; i < buffer.LongLength; i += int.MaxValue) | ||||
|                             { | ||||
|                                 var toWrite = (int)Math.Min(int.MaxValue, buffer.LongLength - i); | ||||
|                                 var toWriteBuf = buffer.AsEnumerable(); | ||||
|                                 for (long j = 0; j < i; j += int.MaxValue) | ||||
|                                 { | ||||
|                                     toWriteBuf = toWriteBuf.Skip(int.MaxValue); | ||||
|                                 } | ||||
|                                 fs.Write(toWriteBuf.ToArray(), 0, toWrite); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     Console.WriteLine("Done."); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         ///     Creates a relative path from one file or folder to another. | ||||
|         /// </summary> | ||||
|         /// <param name="fromPath">Contains the directory that defines the start of the relative path.</param> | ||||
|         /// <param name="toPath">Contains the path that defines the endpoint of the relative path.</param> | ||||
|         /// <returns>The relative path from the start directory to the end path.</returns> | ||||
|         /// <exception cref="ArgumentNullException"></exception> | ||||
|         /// <exception cref="UriFormatException"></exception> | ||||
|         /// <exception cref="InvalidOperationException"></exception> | ||||
|         private static String MakeRelativePath(String fromPath, String toPath) | ||||
|         { | ||||
|             if (String.IsNullOrEmpty(fromPath)) throw new ArgumentNullException("fromPath"); | ||||
|             if (String.IsNullOrEmpty(toPath)) throw new ArgumentNullException("toPath"); | ||||
| 
 | ||||
|             var fromUri = new Uri(fromPath); | ||||
|             var toUri = new Uri(toPath); | ||||
| 
 | ||||
|             if (fromUri.Scheme != toUri.Scheme) | ||||
|             { | ||||
|                 return toPath; | ||||
|             } // path can't be made relative. | ||||
| 
 | ||||
|             Uri relativeUri = fromUri.MakeRelativeUri(toUri); | ||||
|             String relativePath = Uri.UnescapeDataString(relativeUri.ToString()); | ||||
| 
 | ||||
|             if (toUri.Scheme.ToUpperInvariant() == "FILE") | ||||
|             { | ||||
|                 relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); | ||||
|             } | ||||
| 
 | ||||
|             return relativePath; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,36 @@ | |||
| using System.Reflection; | ||||
| using System.Runtime.CompilerServices; | ||||
| using System.Runtime.InteropServices; | ||||
| 
 | ||||
| // Allgemeine Informationen über eine Assembly werden über die folgenden  | ||||
| // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, | ||||
| // die mit einer Assembly verknüpft sind. | ||||
| [assembly: AssemblyTitle("GarrysMod.AddonCreator")] | ||||
| [assembly: AssemblyDescription("")] | ||||
| [assembly: AssemblyConfiguration("")] | ||||
| [assembly: AssemblyCompany("Hewlett-Packard")] | ||||
| [assembly: AssemblyProduct("GarrysMod.AddonCreator")] | ||||
| [assembly: AssemblyCopyright("Copyright © Hewlett-Packard 2014")] | ||||
| [assembly: AssemblyTrademark("")] | ||||
| [assembly: AssemblyCulture("")] | ||||
| 
 | ||||
| // Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar  | ||||
| // für COM-Komponenten.  Wenn Sie auf einen Typ in dieser Assembly von  | ||||
| // COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. | ||||
| [assembly: ComVisible(false)] | ||||
| 
 | ||||
| // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird | ||||
| [assembly: Guid("bb585862-950d-415b-b518-eb9c7e3d50f3")] | ||||
| 
 | ||||
| // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: | ||||
| // | ||||
| //      Hauptversion | ||||
| //      Nebenversion  | ||||
| //      Buildnummer | ||||
| //      Revision | ||||
| // | ||||
| // Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern  | ||||
| // übernehmen, indem Sie "*" eingeben: | ||||
| // [assembly: AssemblyVersion("1.0.*")] | ||||
| [assembly: AssemblyVersion("1.0.0.0")] | ||||
| [assembly: AssemblyFileVersion("1.0.0.0")] | ||||
|  | @ -0,0 +1,40 @@ | |||
| using System; | ||||
| using System.IO; | ||||
| 
 | ||||
| namespace GarrysMod.AddonCreator | ||||
| { | ||||
|     public class SegmentedAddonFileInfo : AddonFileInfo | ||||
|     { | ||||
|         private Stream _stream; | ||||
|         private long _pos; | ||||
|         private long _len; | ||||
|         private int _hash; | ||||
| 
 | ||||
|         public SegmentedAddonFileInfo(Stream stream, long pos, long len, int fileHash) | ||||
|         { | ||||
|             _stream = stream; | ||||
|             _pos = pos; | ||||
|             _len = len; | ||||
|             _hash = fileHash; | ||||
|         } | ||||
| 
 | ||||
|         public override byte[] GetContents() | ||||
|         { | ||||
|             lock (_stream) | ||||
|             { | ||||
|                 var output = new byte[_len]; | ||||
|                 var oldpos = _stream.Position; | ||||
|                 _stream.Position = _pos; | ||||
|                 for (long i = 0; i < _len; i += int.MaxValue) // for loop for supporting long file sizes | ||||
|                 { | ||||
|                     var toRead = (int) Math.Min(int.MaxValue, _len); | ||||
|                     var buffer = new byte[toRead]; | ||||
|                     var readReal = _stream.Read(buffer, 0, toRead); | ||||
|                     i -= (toRead - readReal); // make absolutely sure everything gets read | ||||
|                     buffer.CopyTo(output, i); | ||||
|                 } | ||||
|                 return output; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,76 @@ | |||
| using System; | ||||
| using System.IO; | ||||
| 
 | ||||
| namespace CRC32 | ||||
| { | ||||
|     public class TraditionalCRC | ||||
|     { | ||||
|         private const uint kCrcPoly = 0xEDB88320; | ||||
|         private const uint kInitial = 0xFFFFFFFF; | ||||
|         private static readonly uint[] Table; | ||||
| 
 | ||||
|         static TraditionalCRC() | ||||
|         { | ||||
|             unchecked | ||||
|             { | ||||
|                 Table = new uint[256]; | ||||
|                 for (uint i = 0; i < 256; i++) | ||||
|                 { | ||||
|                     uint r = i; | ||||
|                     for (int j = 0; j < 8; j++) | ||||
|                         r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); | ||||
|                     Table[i] = r; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private uint value; | ||||
| 
 | ||||
|         public TraditionalCRC() | ||||
|         { | ||||
|             Init(); | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Reset CRC | ||||
|         /// </summary> | ||||
|         public void Init() | ||||
|         { | ||||
|             value = kInitial; | ||||
|         } | ||||
| 
 | ||||
|         public int Value | ||||
|         { | ||||
|             get { return (int)~value; } | ||||
|         } | ||||
| 
 | ||||
|         public void UpdateByte(byte b) | ||||
|         { | ||||
|             value = (value >> 8) ^ Table[(byte)value ^ b]; | ||||
|         } | ||||
| 
 | ||||
|         public void Update(byte[] data, int offset, int count) | ||||
|         { | ||||
|             if (count < 0) throw new ArgumentOutOfRangeException("count"); | ||||
|             while (count-- != 0) | ||||
|                 value = (value >> 8) ^ Table[(byte)value ^ data[offset++]]; | ||||
|         } | ||||
| 
 | ||||
|         static public int Compute(byte[] data, int offset, int count) | ||||
|         { | ||||
|             var crc = new TraditionalCRC(); | ||||
|             crc.Update(data, offset, count); | ||||
|             return crc.Value; | ||||
|         } | ||||
| 
 | ||||
|         static public int Compute(byte[] data) | ||||
|         { | ||||
|             return Compute(data, 0, data.Length); | ||||
|         } | ||||
| 
 | ||||
|         static public int Compute(ArraySegment<byte> block) | ||||
|         { | ||||
|             return Compute(block.Array, block.Offset, block.Count); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,4 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <packages> | ||||
|   <package id="Newtonsoft.Json" version="6.0.5" targetFramework="net40" /> | ||||
| </packages> | ||||
		Loading…
	
		Reference in New Issue