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