Files
abysmal-isle/Server/MultiData.cs
Unstable Kitsune b918192e4e Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
2023-11-28 23:20:26 -05:00

997 lines
27 KiB
C#

#region References
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Server;
using Server.Network;
#endregion
namespace Server
{
public static class MultiData
{
public static Dictionary<int, MultiComponentList> Components { get { return m_Components; } }
private static readonly Dictionary<int, MultiComponentList> m_Components;
private static readonly BinaryReader m_IndexReader;
private static readonly BinaryReader m_StreamReader;
public static bool UsingUOPFormat { get; private set; }
public static MultiComponentList GetComponents(int multiID)
{
MultiComponentList mcl;
multiID &= 0x3FFF; // The value of the actual multi is shifted by 0x4000, so this is left alone.
if (m_Components.ContainsKey(multiID))
{
mcl = m_Components[multiID];
}
else if (!UsingUOPFormat)
{
m_Components[multiID] = mcl = Load(multiID);
}
else
{
mcl = MultiComponentList.Empty;
}
return mcl;
}
public static MultiComponentList Load(int multiID)
{
try
{
m_IndexReader.BaseStream.Seek(multiID * 12, SeekOrigin.Begin);
int lookup = m_IndexReader.ReadInt32();
int length = m_IndexReader.ReadInt32();
if (lookup < 0 || length <= 0)
{
return MultiComponentList.Empty;
}
m_StreamReader.BaseStream.Seek(lookup, SeekOrigin.Begin);
return new MultiComponentList(m_StreamReader, length / (MultiComponentList.PostHSFormat ? 16 : 12));
}
catch
{
return MultiComponentList.Empty;
}
}
public static void UOPLoad(string path)
{
var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
BinaryReader streamReader = new BinaryReader(stream);
// Head Information Start
if (streamReader.ReadInt32() != 0x0050594D) // Not a UOP Files
return;
if (streamReader.ReadInt32() > 5) // Bad Version
return;
// Multi ID List Array Start
var chunkIds = new Dictionary<ulong, int>();
var chunkIds2 = new Dictionary<ulong, int>();
UOPHash.BuildChunkIDs(ref chunkIds, ref chunkIds2);
// Multi ID List Array End
streamReader.ReadUInt32(); // format timestamp? 0xFD23EC43
long startAddress = streamReader.ReadInt64();
int blockSize = streamReader.ReadInt32(); // files in each block
int totalSize = streamReader.ReadInt32(); // Total File Count
stream.Seek(startAddress, SeekOrigin.Begin); // Head Information End
long nextBlock;
do
{
int blockFileCount = streamReader.ReadInt32();
nextBlock = streamReader.ReadInt64();
int index = 0;
do
{
long offset = streamReader.ReadInt64();
int headerSize = streamReader.ReadInt32(); // header length
int compressedSize = streamReader.ReadInt32(); // compressed size
int decompressedSize = streamReader.ReadInt32(); // decompressed size
ulong filehash = streamReader.ReadUInt64(); // filename hash (HashLittle2)
uint datablockhash = streamReader.ReadUInt32(); // data hash (Adler32)
short flag = streamReader.ReadInt16(); // compression method (0 = none, 1 = zlib)
index++;
if (offset == 0 || decompressedSize == 0 || filehash == 0x126D1E99DDEDEE0A) // Exclude housing.bin
continue;
// Multi ID Search Start
int chunkID = -1;
if (!chunkIds.TryGetValue(filehash, out chunkID))
{
int tmpChunkID = 0;
if (chunkIds2.TryGetValue(filehash, out tmpChunkID))
{
chunkID = tmpChunkID;
}
}
// Multi ID Search End
long positionpoint = stream.Position; // save current position
// Decompress Data Start
stream.Seek(offset + headerSize, SeekOrigin.Begin);
byte[] sourceData = new byte[compressedSize];
if (stream.Read(sourceData, 0, compressedSize) != compressedSize)
continue;
byte[] data;
if (flag == 1)
{
byte[] destData = new byte[decompressedSize];
/*ZLibError error = */Compression.Compressor.Decompress(destData, ref decompressedSize, sourceData, compressedSize);
data = destData;
}
else
{
data = sourceData;
}
// End Decompress Data
var tileList = new List<MultiTileEntry>();
using (MemoryStream fs = new MemoryStream(data))
{
using (BinaryReader reader = new BinaryReader(fs))
{
uint a = reader.ReadUInt32();
uint count = reader.ReadUInt32();
for (uint i = 0; i < count; i++)
{
ushort ItemId = reader.ReadUInt16();
short x = reader.ReadInt16();
short y = reader.ReadInt16();
short z = reader.ReadInt16();
ushort flagint = reader.ReadUInt16();
TileFlag flagg;
switch (flagint)
{
default:
case 0: { flagg = TileFlag.Background; break; }
case 1: { flagg = TileFlag.None; break; }
case 257: { flagg = TileFlag.Generic; break; }
}
uint clilocsCount = reader.ReadUInt32();
if (clilocsCount != 0)
{
fs.Seek(fs.Position + (clilocsCount * 4), SeekOrigin.Begin); // binary block bypass
}
tileList.Add(new MultiTileEntry(ItemId, x, y, z, flagg));
}
reader.Close();
}
}
m_Components[chunkID] = new MultiComponentList(tileList);
stream.Seek(positionpoint, SeekOrigin.Begin); // back to position
}
while (index < blockFileCount);
}
while (stream.Seek(nextBlock, SeekOrigin.Begin) != 0);
chunkIds.Clear();
chunkIds2.Clear();
}
static MultiData()
{
m_Components = new Dictionary<int, MultiComponentList>();
string multicollectionPath = Core.FindDataFile("MultiCollection.uop");
if (File.Exists(multicollectionPath))
{
UOPLoad(multicollectionPath);
UsingUOPFormat = true;
}
else
{
string idxPath = Core.FindDataFile("multi.idx");
string mulPath = Core.FindDataFile("multi.mul");
if (File.Exists(idxPath) && File.Exists(mulPath))
{
var idx = new FileStream(idxPath, FileMode.Open, FileAccess.Read, FileShare.Read);
m_IndexReader = new BinaryReader(idx);
var stream = new FileStream(mulPath, FileMode.Open, FileAccess.Read, FileShare.Read);
m_StreamReader = new BinaryReader(stream);
string vdPath = Core.FindDataFile("verdata.mul");
if (File.Exists(vdPath))
{
using (FileStream fs = new FileStream(vdPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
BinaryReader bin = new BinaryReader(fs);
int count = bin.ReadInt32();
for (int i = 0; i < count; ++i)
{
int file = bin.ReadInt32();
int index = bin.ReadInt32();
int lookup = bin.ReadInt32();
int length = bin.ReadInt32();
/*int extra = */
bin.ReadInt32();
if (file == 14 && index >= 0 && lookup >= 0 && length > 0)
{
bin.BaseStream.Seek(lookup, SeekOrigin.Begin);
m_Components[index] = new MultiComponentList(bin, length / 12);
bin.BaseStream.Seek(24 + (i * 20), SeekOrigin.Begin);
}
}
bin.Close();
}
}
else
{
Console.WriteLine("Warning: Multi data files not found!");
}
}
}
}
}
public struct MultiTileEntry
{
public ushort m_ItemID;
public short m_OffsetX, m_OffsetY, m_OffsetZ;
public TileFlag m_Flags;
public MultiTileEntry(ushort itemID, short xOffset, short yOffset, short zOffset, TileFlag flags)
{
m_ItemID = itemID;
m_OffsetX = xOffset;
m_OffsetY = yOffset;
m_OffsetZ = zOffset;
m_Flags = flags;
}
}
public sealed class MultiComponentList
{
public static bool PostHSFormat { get; set; }
private Point2D m_Min, m_Max, m_Center;
private int m_Width, m_Height;
private StaticTile[][][] m_Tiles;
private MultiTileEntry[] m_List;
public static readonly MultiComponentList Empty = new MultiComponentList();
public Point2D Min { get { return m_Min; } }
public Point2D Max { get { return m_Max; } }
public Point2D Center { get { return m_Center; } }
public int Width { get { return m_Width; } }
public int Height { get { return m_Height; } }
public StaticTile[][][] Tiles { get { return m_Tiles; } }
public MultiTileEntry[] List { get { return m_List; } }
public void Add(int itemID, int x, int y, int z)
{
itemID &= TileData.MaxItemValue;
itemID |= 0x10000;
int vx = x + m_Center.m_X;
int vy = y + m_Center.m_Y;
if (vx >= 0 && vx < m_Width && vy >= 0 && vy < m_Height)
{
var oldTiles = m_Tiles[vx][vy];
for (int i = oldTiles.Length - 1; i >= 0; --i)
{
ItemData data = TileData.ItemTable[itemID & TileData.MaxItemValue];
if (oldTiles[i].Z == z && (oldTiles[i].Height > 0 == data.Height > 0))
{
bool newIsRoof = (data.Flags & TileFlag.Roof) != 0;
bool oldIsRoof = (TileData.ItemTable[oldTiles[i].ID & TileData.MaxItemValue].Flags & TileFlag.Roof) != 0;
if (newIsRoof == oldIsRoof)
{
Remove(oldTiles[i].ID, x, y, z);
}
}
}
oldTiles = m_Tiles[vx][vy];
var newTiles = new StaticTile[oldTiles.Length + 1];
for (int i = 0; i < oldTiles.Length; ++i)
{
newTiles[i] = oldTiles[i];
}
newTiles[oldTiles.Length] = new StaticTile((ushort)itemID, (sbyte)z);
m_Tiles[vx][vy] = newTiles;
var oldList = m_List;
var newList = new MultiTileEntry[oldList.Length + 1];
for (int i = 0; i < oldList.Length; ++i)
{
newList[i] = oldList[i];
}
newList[oldList.Length] = new MultiTileEntry((ushort)itemID, (short)x, (short)y, (short)z, TileFlag.Background);
m_List = newList;
if (x < m_Min.m_X)
{
m_Min.m_X = x;
}
if (y < m_Min.m_Y)
{
m_Min.m_Y = y;
}
if (x > m_Max.m_X)
{
m_Max.m_X = x;
}
if (y > m_Max.m_Y)
{
m_Max.m_Y = y;
}
}
}
public void RemoveXYZH(int x, int y, int z, int minHeight)
{
int vx = x + m_Center.m_X;
int vy = y + m_Center.m_Y;
if (vx >= 0 && vx < m_Width && vy >= 0 && vy < m_Height)
{
var oldTiles = m_Tiles[vx][vy];
for (int i = 0; i < oldTiles.Length; ++i)
{
StaticTile tile = oldTiles[i];
if (tile.Z == z && tile.Height >= minHeight)
{
var newTiles = new StaticTile[oldTiles.Length - 1];
for (int j = 0; j < i; ++j)
{
newTiles[j] = oldTiles[j];
}
for (int j = i + 1; j < oldTiles.Length; ++j)
{
newTiles[j - 1] = oldTiles[j];
}
m_Tiles[vx][vy] = newTiles;
break;
}
}
var oldList = m_List;
for (int i = 0; i < oldList.Length; ++i)
{
MultiTileEntry tile = oldList[i];
if (tile.m_OffsetX == (short)x && tile.m_OffsetY == (short)y && tile.m_OffsetZ == (short)z &&
TileData.ItemTable[tile.m_ItemID & TileData.MaxItemValue].Height >= minHeight)
{
var newList = new MultiTileEntry[oldList.Length - 1];
for (int j = 0; j < i; ++j)
{
newList[j] = oldList[j];
}
for (int j = i + 1; j < oldList.Length; ++j)
{
newList[j - 1] = oldList[j];
}
m_List = newList;
break;
}
}
}
}
public void Remove(int itemID, int x, int y, int z)
{
int vx = x + m_Center.m_X;
int vy = y + m_Center.m_Y;
if (vx >= 0 && vx < m_Width && vy >= 0 && vy < m_Height)
{
var oldTiles = m_Tiles[vx][vy];
for (int i = 0; i < oldTiles.Length; ++i)
{
StaticTile tile = oldTiles[i];
if (tile.ID == itemID && tile.Z == z)
{
var newTiles = new StaticTile[oldTiles.Length - 1];
for (int j = 0; j < i; ++j)
{
newTiles[j] = oldTiles[j];
}
for (int j = i + 1; j < oldTiles.Length; ++j)
{
newTiles[j - 1] = oldTiles[j];
}
m_Tiles[vx][vy] = newTiles;
break;
}
}
var oldList = m_List;
for (int i = 0; i < oldList.Length; ++i)
{
MultiTileEntry tile = oldList[i];
if (tile.m_ItemID == itemID && tile.m_OffsetX == (short)x && tile.m_OffsetY == (short)y &&
tile.m_OffsetZ == (short)z)
{
var newList = new MultiTileEntry[oldList.Length - 1];
for (int j = 0; j < i; ++j)
{
newList[j] = oldList[j];
}
for (int j = i + 1; j < oldList.Length; ++j)
{
newList[j - 1] = oldList[j];
}
m_List = newList;
break;
}
}
}
}
public void Resize(int newWidth, int newHeight)
{
int oldWidth = m_Width, oldHeight = m_Height;
var oldTiles = m_Tiles;
int totalLength = 0;
var newTiles = new StaticTile[newWidth][][];
for (int x = 0; x < newWidth; ++x)
{
newTiles[x] = new StaticTile[newHeight][];
for (int y = 0; y < newHeight; ++y)
{
if (x < oldWidth && y < oldHeight)
{
newTiles[x][y] = oldTiles[x][y];
}
else
{
newTiles[x][y] = new StaticTile[0];
}
totalLength += newTiles[x][y].Length;
}
}
m_Tiles = newTiles;
m_List = new MultiTileEntry[totalLength];
m_Width = newWidth;
m_Height = newHeight;
m_Min = Point2D.Zero;
m_Max = Point2D.Zero;
int index = 0;
for (int x = 0; x < newWidth; ++x)
{
for (int y = 0; y < newHeight; ++y)
{
var tiles = newTiles[x][y];
foreach (StaticTile tile in tiles)
{
int vx = x - m_Center.X;
int vy = y - m_Center.Y;
if (vx < m_Min.m_X)
{
m_Min.m_X = vx;
}
if (vy < m_Min.m_Y)
{
m_Min.m_Y = vy;
}
if (vx > m_Max.m_X)
{
m_Max.m_X = vx;
}
if (vy > m_Max.m_Y)
{
m_Max.m_Y = vy;
}
m_List[index++] = new MultiTileEntry((ushort)tile.ID, (short)vx, (short)vy, (short)tile.Z, TileFlag.Background);
}
}
}
}
public MultiComponentList(MultiComponentList toCopy)
{
m_Min = toCopy.m_Min;
m_Max = toCopy.m_Max;
m_Center = toCopy.m_Center;
m_Width = toCopy.m_Width;
m_Height = toCopy.m_Height;
m_Tiles = new StaticTile[m_Width][][];
for (int x = 0; x < m_Width; ++x)
{
m_Tiles[x] = new StaticTile[m_Height][];
for (int y = 0; y < m_Height; ++y)
{
m_Tiles[x][y] = new StaticTile[toCopy.m_Tiles[x][y].Length];
for (int i = 0; i < m_Tiles[x][y].Length; ++i)
{
m_Tiles[x][y][i] = toCopy.m_Tiles[x][y][i];
}
}
}
m_List = new MultiTileEntry[toCopy.m_List.Length];
for (int i = 0; i < m_List.Length; ++i)
{
m_List[i] = toCopy.m_List[i];
}
}
public void Serialize(GenericWriter writer)
{
writer.Write(2); // version;
writer.Write(m_Min);
writer.Write(m_Max);
writer.Write(m_Center);
writer.Write(m_Width);
writer.Write(m_Height);
writer.Write(m_List.Length);
foreach (MultiTileEntry ent in m_List)
{
writer.Write(ent.m_ItemID);
writer.Write(ent.m_OffsetX);
writer.Write(ent.m_OffsetY);
writer.Write(ent.m_OffsetZ);
writer.Write((ulong)ent.m_Flags);
}
}
public MultiComponentList(GenericReader reader)
{
int version = reader.ReadInt();
m_Min = reader.ReadPoint2D();
m_Max = reader.ReadPoint2D();
m_Center = reader.ReadPoint2D();
m_Width = reader.ReadInt();
m_Height = reader.ReadInt();
int length = reader.ReadInt();
var allTiles = m_List = new MultiTileEntry[length];
if (version == 0)
{
for (int i = 0; i < length; ++i)
{
int id = reader.ReadShort();
if (id >= 0x4000)
{
id -= 0x4000;
}
allTiles[i].m_ItemID = (ushort)id;
allTiles[i].m_OffsetX = reader.ReadShort();
allTiles[i].m_OffsetY = reader.ReadShort();
allTiles[i].m_OffsetZ = reader.ReadShort();
allTiles[i].m_Flags = (TileFlag)reader.ReadUInt();
}
}
else
{
for (int i = 0; i < length; ++i)
{
allTiles[i].m_ItemID = reader.ReadUShort();
allTiles[i].m_OffsetX = reader.ReadShort();
allTiles[i].m_OffsetY = reader.ReadShort();
allTiles[i].m_OffsetZ = reader.ReadShort();
if (version > 1)
allTiles[i].m_Flags = (TileFlag)reader.ReadULong();
else
allTiles[i].m_Flags = (TileFlag)reader.ReadUInt();
}
}
var tiles = new TileList[m_Width][];
m_Tiles = new StaticTile[m_Width][][];
for (int x = 0; x < m_Width; ++x)
{
tiles[x] = new TileList[m_Height];
m_Tiles[x] = new StaticTile[m_Height][];
for (int y = 0; y < m_Height; ++y)
{
tiles[x][y] = new TileList();
}
}
for (int i = 0; i < allTiles.Length; ++i)
{
if (i == 0 || allTiles[i].m_Flags != 0)
{
int xOffset = allTiles[i].m_OffsetX + m_Center.m_X;
int yOffset = allTiles[i].m_OffsetY + m_Center.m_Y;
int itemID = ((allTiles[i].m_ItemID & TileData.MaxItemValue) | 0x10000);
tiles[xOffset][yOffset].Add((ushort)itemID, (sbyte)allTiles[i].m_OffsetZ);
}
}
for (int x = 0; x < m_Width; ++x)
{
for (int y = 0; y < m_Height; ++y)
{
m_Tiles[x][y] = tiles[x][y].ToArray();
}
}
}
public MultiComponentList(BinaryReader reader, int count)
{
var allTiles = m_List = new MultiTileEntry[count];
for (int i = 0; i < count; ++i)
{
allTiles[i].m_ItemID = reader.ReadUInt16();
allTiles[i].m_OffsetX = reader.ReadInt16();
allTiles[i].m_OffsetY = reader.ReadInt16();
allTiles[i].m_OffsetZ = reader.ReadInt16();
if (PostHSFormat)
allTiles[i].m_Flags = (TileFlag)reader.ReadUInt64();
else
allTiles[i].m_Flags = (TileFlag)reader.ReadUInt32();
MultiTileEntry e = allTiles[i];
if (i == 0 || e.m_Flags != 0)
{
if (e.m_OffsetX < m_Min.m_X)
{
m_Min.m_X = e.m_OffsetX;
}
if (e.m_OffsetY < m_Min.m_Y)
{
m_Min.m_Y = e.m_OffsetY;
}
if (e.m_OffsetX > m_Max.m_X)
{
m_Max.m_X = e.m_OffsetX;
}
if (e.m_OffsetY > m_Max.m_Y)
{
m_Max.m_Y = e.m_OffsetY;
}
}
}
m_Center = new Point2D(-m_Min.m_X, -m_Min.m_Y);
m_Width = (m_Max.m_X - m_Min.m_X) + 1;
m_Height = (m_Max.m_Y - m_Min.m_Y) + 1;
var tiles = new TileList[m_Width][];
m_Tiles = new StaticTile[m_Width][][];
for (int x = 0; x < m_Width; ++x)
{
tiles[x] = new TileList[m_Height];
m_Tiles[x] = new StaticTile[m_Height][];
for (int y = 0; y < m_Height; ++y)
{
tiles[x][y] = new TileList();
}
}
for (int i = 0; i < allTiles.Length; ++i)
{
if (i == 0 || allTiles[i].m_Flags != 0)
{
int xOffset = allTiles[i].m_OffsetX + m_Center.m_X;
int yOffset = allTiles[i].m_OffsetY + m_Center.m_Y;
int itemID = ((allTiles[i].m_ItemID & TileData.MaxItemValue) | 0x10000);
tiles[xOffset][yOffset].Add((ushort)itemID, (sbyte)allTiles[i].m_OffsetZ);
}
}
for (int x = 0; x < m_Width; ++x)
{
for (int y = 0; y < m_Height; ++y)
{
m_Tiles[x][y] = tiles[x][y].ToArray();
}
}
}
public MultiComponentList(List<MultiTileEntry> list)
{
var allTiles = m_List = new MultiTileEntry[list.Count];
for (int i = 0; i < list.Count; ++i)
{
allTiles[i].m_ItemID = list[i].m_ItemID;
allTiles[i].m_OffsetX = list[i].m_OffsetX;
allTiles[i].m_OffsetY = list[i].m_OffsetY;
allTiles[i].m_OffsetZ = list[i].m_OffsetZ;
allTiles[i].m_Flags = list[i].m_Flags;
MultiTileEntry e = allTiles[i];
if (i == 0 || e.m_Flags != 0)
{
if (e.m_OffsetX < m_Min.m_X)
{
m_Min.m_X = e.m_OffsetX;
}
if (e.m_OffsetY < m_Min.m_Y)
{
m_Min.m_Y = e.m_OffsetY;
}
if (e.m_OffsetX > m_Max.m_X)
{
m_Max.m_X = e.m_OffsetX;
}
if (e.m_OffsetY > m_Max.m_Y)
{
m_Max.m_Y = e.m_OffsetY;
}
}
}
m_Center = new Point2D(-m_Min.m_X, -m_Min.m_Y);
m_Width = (m_Max.m_X - m_Min.m_X) + 1;
m_Height = (m_Max.m_Y - m_Min.m_Y) + 1;
var tiles = new TileList[m_Width][];
m_Tiles = new StaticTile[m_Width][][];
for (int x = 0; x < m_Width; ++x)
{
tiles[x] = new TileList[m_Height];
m_Tiles[x] = new StaticTile[m_Height][];
for (int y = 0; y < m_Height; ++y)
{
tiles[x][y] = new TileList();
}
}
for (int i = 0; i < allTiles.Length; ++i)
{
if (i == 0 || allTiles[i].m_Flags != 0)
{
int xOffset = allTiles[i].m_OffsetX + m_Center.m_X;
int yOffset = allTiles[i].m_OffsetY + m_Center.m_Y;
int itemID = ((allTiles[i].m_ItemID & TileData.MaxItemValue) | 0x10000);
tiles[xOffset][yOffset].Add((ushort)itemID, (sbyte)allTiles[i].m_OffsetZ);
}
}
for (int x = 0; x < m_Width; ++x)
{
for (int y = 0; y < m_Height; ++y)
{
m_Tiles[x][y] = tiles[x][y].ToArray();
}
}
}
private MultiComponentList()
{
m_Tiles = new StaticTile[0][][];
m_List = new MultiTileEntry[0];
}
}
public class UOPHash
{
public static void BuildChunkIDs(ref Dictionary<ulong, int> chunkIds, ref Dictionary<ulong, int> chunkIds2)
{
int maxId;
string[] formats = GetHashFormat(0, out maxId);
for (int i = 0; i < maxId; ++i)
{
chunkIds[HashLittle2(String.Format(formats[0], i))] = i;
}
if (formats[1] != "")
{
for (int i = 0; i < maxId; ++i)
chunkIds2[HashLittle2(String.Format(formats[1], i))] = i;
}
}
private static string[] GetHashFormat(int typeIndex, out int maxId)
{
/*
* MaxID is only used for constructing a lookup table.
* Decrease to save some possibly unneeded computation.
*/
maxId = 0x10000;
return new string[] { "build/multicollection/{0:000000}.bin", "" };
}
private static ulong HashLittle2(string s)
{
int length = s.Length;
uint a, b, c;
a = b = c = 0xDEADBEEF + (uint)length;
int k = 0;
while (length > 12)
{
a += s[k];
a += ((uint)s[k + 1]) << 8;
a += ((uint)s[k + 2]) << 16;
a += ((uint)s[k + 3]) << 24;
b += s[k + 4];
b += ((uint)s[k + 5]) << 8;
b += ((uint)s[k + 6]) << 16;
b += ((uint)s[k + 7]) << 24;
c += s[k + 8];
c += ((uint)s[k + 9]) << 8;
c += ((uint)s[k + 10]) << 16;
c += ((uint)s[k + 11]) << 24;
a -= c; a ^= ((c << 4) | (c >> 28)); c += b;
b -= a; b ^= ((a << 6) | (a >> 26)); a += c;
c -= b; c ^= ((b << 8) | (b >> 24)); b += a;
a -= c; a ^= ((c << 16) | (c >> 16)); c += b;
b -= a; b ^= ((a << 19) | (a >> 13)); a += c;
c -= b; c ^= ((b << 4) | (b >> 28)); b += a;
length -= 12;
k += 12;
}
if (length != 0)
{
switch (length)
{
case 12: c += ((uint)s[k + 11]) << 24; goto case 11;
case 11: c += ((uint)s[k + 10]) << 16; goto case 10;
case 10: c += ((uint)s[k + 9]) << 8; goto case 9;
case 9: c += s[k + 8]; goto case 8;
case 8: b += ((uint)s[k + 7]) << 24; goto case 7;
case 7: b += ((uint)s[k + 6]) << 16; goto case 6;
case 6: b += ((uint)s[k + 5]) << 8; goto case 5;
case 5: b += s[k + 4]; goto case 4;
case 4: a += ((uint)s[k + 3]) << 24; goto case 3;
case 3: a += ((uint)s[k + 2]) << 16; goto case 2;
case 2: a += ((uint)s[k + 1]) << 8; goto case 1;
case 1: a += s[k]; break;
}
c ^= b; c -= ((b << 14) | (b >> 18));
a ^= c; a -= ((c << 11) | (c >> 21));
b ^= a; b -= ((a << 25) | (a >> 7));
c ^= b; c -= ((b << 16) | (b >> 16));
a ^= c; a -= ((c << 4) | (c >> 28));
b ^= a; b -= ((a << 14) | (a >> 18));
c ^= b; c -= ((b << 24) | (b >> 8));
}
return ((ulong)b << 32) | c;
}
}
}