Overwrite

Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
Unstable Kitsune
2023-11-28 23:20:26 -05:00
parent 3cd54811de
commit b918192e4e
11608 changed files with 2644205 additions and 47 deletions

902
Ultima/TileMatrix.cs Normal file
View File

@@ -0,0 +1,902 @@
#region References
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
#endregion
namespace Ultima
{
public sealed class TileMatrix
{
private readonly HuedTile[][][][][] m_StaticTiles;
private readonly Tile[][][] m_LandTiles;
private bool[][] m_RemovedStaticBlock;
private List<StaticTile>[][] m_StaticTiles_ToAdd;
public static Tile[] InvalidLandBlock { get; private set; }
public static HuedTile[][][] EmptyStaticBlock { get; private set; }
private FileStream m_Map;
private BinaryReader m_UOPReader;
private FileStream m_Statics;
private Entry3D[] m_StaticIndex;
public Entry3D[] StaticIndex
{
get
{
if (!StaticIndexInit)
{
InitStatics();
}
return m_StaticIndex;
}
}
public bool StaticIndexInit;
public TileMatrixPatch Patch { get; private set; }
public int BlockWidth { get; private set; }
public int BlockHeight { get; private set; }
public int Width { get; private set; }
public int Height { get; private set; }
private readonly string mapPath;
private readonly string indexPath;
private readonly string staticsPath;
public void CloseStreams()
{
if (m_Map != null)
{
m_Map.Close();
}
if (m_UOPReader != null)
{
m_UOPReader.Close();
}
if (m_Statics != null)
{
m_Statics.Close();
}
}
public TileMatrix(int fileIndex, int mapID, int width, int height, string path)
{
Width = width;
Height = height;
BlockWidth = width >> 3;
BlockHeight = height >> 3;
if (path == null)
{
mapPath = Files.GetFilePath("map{0}.mul", fileIndex);
if (String.IsNullOrEmpty(mapPath) || !File.Exists(mapPath))
{
mapPath = Files.GetFilePath("map{0}LegacyMUL.uop", fileIndex);
}
if (mapPath != null && mapPath.EndsWith(".uop"))
{
IsUOPFormat = true;
}
}
else
{
mapPath = Path.Combine(path, String.Format("map{0}.mul", fileIndex));
if (!File.Exists(mapPath))
{
mapPath = Path.Combine(path, String.Format("map{0}LegacyMUL.uop", fileIndex));
}
if (!File.Exists(mapPath))
{
mapPath = null;
}
else if (mapPath != null && mapPath.EndsWith(".uop"))
{
IsUOPFormat = true;
}
}
if (path == null)
{
indexPath = Files.GetFilePath("staidx{0}.mul", fileIndex);
}
else
{
indexPath = Path.Combine(path, String.Format("staidx{0}.mul", fileIndex));
if (!File.Exists(indexPath))
{
indexPath = null;
}
}
if (path == null)
{
staticsPath = Files.GetFilePath("statics{0}.mul", fileIndex);
}
else
{
staticsPath = Path.Combine(path, String.Format("statics{0}.mul", fileIndex));
if (!File.Exists(staticsPath))
{
staticsPath = null;
}
}
EmptyStaticBlock = new HuedTile[8][][];
for (int i = 0; i < 8; ++i)
{
EmptyStaticBlock[i] = new HuedTile[8][];
for (int j = 0; j < 8; ++j)
{
EmptyStaticBlock[i][j] = new HuedTile[0];
}
}
InvalidLandBlock = new Tile[196];
m_LandTiles = new Tile[BlockWidth][][];
m_StaticTiles = new HuedTile[BlockWidth][][][][];
Patch = new TileMatrixPatch(this, mapID, path);
}
public void SetStaticBlock(int x, int y, HuedTile[][][] value)
{
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
{
return;
}
if (m_StaticTiles[x] == null)
{
m_StaticTiles[x] = new HuedTile[BlockHeight][][][];
}
m_StaticTiles[x][y] = value;
}
public HuedTile[][][] GetStaticBlock(int x, int y)
{
return GetStaticBlock(x, y, true);
}
public HuedTile[][][] GetStaticBlock(int x, int y, bool patch)
{
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
{
return EmptyStaticBlock;
}
if (m_StaticTiles[x] == null)
{
m_StaticTiles[x] = new HuedTile[BlockHeight][][][];
}
HuedTile[][][] tiles = m_StaticTiles[x][y];
if (tiles == null)
{
tiles = m_StaticTiles[x][y] = ReadStaticBlock(x, y);
}
if ((Map.UseDiff) && (patch))
{
if (Patch.StaticBlocksCount > 0)
{
if (Patch.StaticBlocks[x] != null)
{
if (Patch.StaticBlocks[x][y] != null)
{
tiles = Patch.StaticBlocks[x][y];
}
}
}
}
return tiles;
}
public HuedTile[] GetStaticTiles(int x, int y, bool patch)
{
return GetStaticBlock(x >> 3, y >> 3, patch)[x & 0x7][y & 0x7];
}
public HuedTile[] GetStaticTiles(int x, int y)
{
return GetStaticBlock(x >> 3, y >> 3)[x & 0x7][y & 0x7];
}
public void SetLandBlock(int x, int y, Tile[] value)
{
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
{
return;
}
if (m_LandTiles[x] == null)
{
m_LandTiles[x] = new Tile[BlockHeight][];
}
m_LandTiles[x][y] = value;
}
public Tile[] GetLandBlock(int x, int y)
{
return GetLandBlock(x, y, true);
}
public Tile[] GetLandBlock(int x, int y, bool patch)
{
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
{
return InvalidLandBlock;
}
if (m_LandTiles[x] == null)
{
m_LandTiles[x] = new Tile[BlockHeight][];
}
Tile[] tiles = m_LandTiles[x][y];
if (tiles == null)
{
tiles = m_LandTiles[x][y] = ReadLandBlock(x, y);
}
if ((Map.UseDiff) && (patch))
{
if (Patch.LandBlocksCount > 0)
{
if (Patch.LandBlocks[x] != null)
{
if (Patch.LandBlocks[x][y] != null)
{
tiles = Patch.LandBlocks[x][y];
}
}
}
}
return tiles;
}
public Tile GetLandTile(int x, int y, bool patch)
{
return GetLandBlock(x >> 3, y >> 3, patch)[((y & 0x7) << 3) + (x & 0x7)];
}
public Tile GetLandTile(int x, int y)
{
return GetLandBlock(x >> 3, y >> 3)[((y & 0x7) << 3) + (x & 0x7)];
}
private void InitStatics()
{
m_StaticIndex = new Entry3D[BlockHeight * BlockWidth];
if (indexPath == null)
{
return;
}
using (var index = new FileStream(indexPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
m_Statics = new FileStream(staticsPath, FileMode.Open, FileAccess.Read, FileShare.Read);
var count = (int)(index.Length / 12);
GCHandle gc = GCHandle.Alloc(m_StaticIndex, GCHandleType.Pinned);
var buffer = new byte[index.Length];
index.Read(buffer, 0, (int)index.Length);
Marshal.Copy(buffer, 0, gc.AddrOfPinnedObject(), (int)Math.Min(index.Length, BlockHeight * BlockWidth * 12));
gc.Free();
for (var i = (int)Math.Min(index.Length, BlockHeight * BlockWidth); i < BlockHeight * BlockWidth; ++i)
{
m_StaticIndex[i].lookup = -1;
m_StaticIndex[i].length = -1;
m_StaticIndex[i].extra = -1;
}
StaticIndexInit = true;
}
}
private static HuedTileList[][] m_Lists;
private static byte[] m_Buffer;
private unsafe HuedTile[][][] ReadStaticBlock(int x, int y)
{
try
{
if (!StaticIndexInit)
{
InitStatics();
}
if (m_Statics == null || !m_Statics.CanRead || !m_Statics.CanSeek)
{
if (staticsPath == null)
{
m_Statics = null;
}
else
{
m_Statics = new FileStream(staticsPath, FileMode.Open, FileAccess.Read, FileShare.Read);
}
}
if (m_Statics == null)
{
return EmptyStaticBlock;
}
int lookup = m_StaticIndex[(x * BlockHeight) + y].lookup;
int length = m_StaticIndex[(x * BlockHeight) + y].length;
if (lookup < 0 || length <= 0)
{
return EmptyStaticBlock;
}
else
{
int count = length / 7;
m_Statics.Seek(lookup, SeekOrigin.Begin);
if (m_Buffer == null || m_Buffer.Length < length)
{
m_Buffer = new byte[length];
}
GCHandle gc = GCHandle.Alloc(m_Buffer, GCHandleType.Pinned);
try
{
m_Statics.Read(m_Buffer, 0, length);
if (m_Lists == null)
{
m_Lists = new HuedTileList[8][];
for (int i = 0; i < 8; ++i)
{
m_Lists[i] = new HuedTileList[8];
for (int j = 0; j < 8; ++j)
{
m_Lists[i][j] = new HuedTileList();
}
}
}
HuedTileList[][] lists = m_Lists;
for (int i = 0; i < count; ++i)
{
var ptr = new IntPtr((long)gc.AddrOfPinnedObject() + i * sizeof(StaticTile));
var cur = (StaticTile)Marshal.PtrToStructure(ptr, typeof(StaticTile));
lists[cur.m_X & 0x7][cur.m_Y & 0x7].Add(Art.GetLegalItemID(cur.m_ID), cur.m_Hue, cur.m_Z);
}
var tiles = new HuedTile[8][][];
for (int i = 0; i < 8; ++i)
{
tiles[i] = new HuedTile[8][];
for (int j = 0; j < 8; ++j)
{
tiles[i][j] = lists[i][j].ToArray();
}
}
return tiles;
}
finally
{
gc.Free();
}
}
}
finally
{
//if (m_Statics != null)
// m_Statics.Close();
}
}
/* UOP map files support code, written by Wyatt (c) www.ruosi.org
* It's not possible if some entry has unknown hash. Throwed exception
* means that EA changed maps UOPs again.
*/
#region UOP
public bool IsUOPFormat { get; set; }
public bool IsUOPAlreadyRead { get; set; }
private struct UOPFile
{
public readonly long Offset;
public readonly int Length;
public UOPFile(long offset, int length)
{
Offset = offset;
Length = length;
}
}
private UOPFile[] UOPFiles { get; set; }
private long UOPLength { get { return m_Map.Length; } }
private void ReadUOPFiles(string pattern)
{
m_UOPReader = new BinaryReader(m_Map);
m_UOPReader.BaseStream.Seek(0, SeekOrigin.Begin);
if (m_UOPReader.ReadInt32() != 0x50594D)
{
throw new ArgumentException("Bad UOP file.");
}
m_UOPReader.ReadInt64(); // version + signature
long nextBlock = m_UOPReader.ReadInt64();
m_UOPReader.ReadInt32(); // block capacity
int count = m_UOPReader.ReadInt32();
UOPFiles = new UOPFile[count];
var hashes = new Dictionary<ulong, int>();
for (int i = 0; i < count; i++)
{
string file = string.Format("build/{0}/{1:D8}.dat", pattern, i);
ulong hash = FileIndex.HashFileName(file);
if (!hashes.ContainsKey(hash))
{
hashes.Add(hash, i);
}
}
m_UOPReader.BaseStream.Seek(nextBlock, SeekOrigin.Begin);
do
{
int filesCount = m_UOPReader.ReadInt32();
nextBlock = m_UOPReader.ReadInt64();
for (int i = 0; i < filesCount; i++)
{
long offset = m_UOPReader.ReadInt64();
int headerLength = m_UOPReader.ReadInt32();
int compressedLength = m_UOPReader.ReadInt32();
int decompressedLength = m_UOPReader.ReadInt32();
ulong hash = m_UOPReader.ReadUInt64();
m_UOPReader.ReadUInt32(); // Adler32
short flag = m_UOPReader.ReadInt16();
int length = flag == 1 ? compressedLength : decompressedLength;
if (offset == 0)
{
continue;
}
int idx;
if (hashes.TryGetValue(hash, out idx))
{
if (idx < 0 || idx > UOPFiles.Length)
{
throw new IndexOutOfRangeException("hashes dictionary and files collection have different count of entries!");
}
UOPFiles[idx] = new UOPFile(offset + headerLength, length);
}
else
{
throw new ArgumentException(
string.Format("File with hash 0x{0:X8} was not found in hashes dictionary! EA Mythic changed UOP format!", hash));
}
}
}
while (m_UOPReader.BaseStream.Seek(nextBlock, SeekOrigin.Begin) != 0);
}
private long CalculateOffsetFromUOP(long offset)
{
long pos = 0;
foreach (UOPFile t in UOPFiles)
{
long currPos = pos + t.Length;
if (offset < currPos)
{
return t.Offset + (offset - pos);
}
pos = currPos;
}
return UOPLength;
}
#endregion
private Tile[] ReadLandBlock(int x, int y)
{
if (m_Map == null || !m_Map.CanRead || !m_Map.CanSeek)
{
if (mapPath == null)
{
m_Map = null;
}
else
{
m_Map = new FileStream(mapPath, FileMode.Open, FileAccess.Read, FileShare.Read);
}
if (IsUOPFormat && mapPath != null && !IsUOPAlreadyRead)
{
var fi = new FileInfo(mapPath);
string uopPattern = fi.Name.Replace(fi.Extension, "").ToLowerInvariant();
ReadUOPFiles(uopPattern);
IsUOPAlreadyRead = true;
}
}
var tiles = new Tile[64];
if (m_Map != null)
{
long offset = ((x * BlockHeight) + y) * 196 + 4;
if (IsUOPFormat)
{
offset = CalculateOffsetFromUOP(offset);
}
m_Map.Seek(offset, SeekOrigin.Begin);
GCHandle gc = GCHandle.Alloc(tiles, GCHandleType.Pinned);
try
{
if (m_Buffer == null || m_Buffer.Length < 192)
{
m_Buffer = new byte[192];
}
m_Map.Read(m_Buffer, 0, 192);
Marshal.Copy(m_Buffer, 0, gc.AddrOfPinnedObject(), 192);
}
finally
{
gc.Free();
}
//m_Map.Close();
}
return tiles;
}
public void RemoveStaticBlock(int blockx, int blocky)
{
if (m_RemovedStaticBlock == null)
{
m_RemovedStaticBlock = new bool[BlockWidth][];
}
if (m_RemovedStaticBlock[blockx] == null)
{
m_RemovedStaticBlock[blockx] = new bool[BlockHeight];
}
m_RemovedStaticBlock[blockx][blocky] = true;
if (m_StaticTiles[blockx] == null)
{
m_StaticTiles[blockx] = new HuedTile[BlockHeight][][][];
}
m_StaticTiles[blockx][blocky] = EmptyStaticBlock;
}
public bool IsStaticBlockRemoved(int blockx, int blocky)
{
if (m_RemovedStaticBlock == null)
{
return false;
}
if (m_RemovedStaticBlock[blockx] == null)
{
return false;
}
return m_RemovedStaticBlock[blockx][blocky];
}
public bool PendingStatic(int blockx, int blocky)
{
if (m_StaticTiles_ToAdd == null)
{
return false;
}
if (m_StaticTiles_ToAdd[blocky] == null)
{
return false;
}
if (m_StaticTiles_ToAdd[blocky][blockx] == null)
{
return false;
}
return true;
}
public void AddPendingStatic(int blockx, int blocky, StaticTile toadd)
{
if (m_StaticTiles_ToAdd == null)
{
m_StaticTiles_ToAdd = new List<StaticTile>[BlockHeight][];
}
if (m_StaticTiles_ToAdd[blocky] == null)
{
m_StaticTiles_ToAdd[blocky] = new List<StaticTile>[BlockWidth];
}
if (m_StaticTiles_ToAdd[blocky][blockx] == null)
{
m_StaticTiles_ToAdd[blocky][blockx] = new List<StaticTile>();
}
m_StaticTiles_ToAdd[blocky][blockx].Add(toadd);
}
public StaticTile[] GetPendingStatics(int blockx, int blocky)
{
if (m_StaticTiles_ToAdd == null)
{
return null;
}
if (m_StaticTiles_ToAdd[blocky] == null)
{
return null;
}
if (m_StaticTiles_ToAdd[blocky][blockx] == null)
{
return null;
}
return m_StaticTiles_ToAdd[blocky][blockx].ToArray();
}
public void Dispose()
{
if (m_Map != null)
{
m_Map.Close();
}
if (m_UOPReader != null)
{
m_UOPReader.Close();
}
if (m_Statics != null)
{
m_Statics.Close();
}
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct StaticTile
{
public ushort m_ID;
public byte m_X;
public byte m_Y;
public sbyte m_Z;
public short m_Hue;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct HuedTile
{
internal sbyte m_Z;
internal ushort m_ID;
internal int m_Hue;
public ushort ID { get { return m_ID; } set { m_ID = value; } }
public int Hue { get { return m_Hue; } set { m_Hue = value; } }
public int Z { get { return m_Z; } set { m_Z = (sbyte)value; } }
public HuedTile(ushort id, short hue, sbyte z)
{
m_ID = id;
m_Hue = hue;
m_Z = z;
}
public void Set(ushort id, short hue, sbyte z)
{
m_ID = id;
m_Hue = hue;
m_Z = z;
}
}
public struct MTile : IComparable
{
internal ushort m_ID;
internal sbyte m_Z;
internal TileFlag m_Flag;
internal int m_Solver;
public ushort ID { get { return m_ID; } }
public int Z { get { return m_Z; } set { m_Z = (sbyte)value; } }
public TileFlag Flag { get { return m_Flag; } set { m_Flag = value; } }
public int Solver { get { return m_Solver; } set { m_Solver = value; } }
public MTile(ushort id, sbyte z)
{
m_ID = Art.GetLegalItemID(id);
m_Z = z;
m_Flag = TileFlag.Background;
m_Solver = 0;
}
public MTile(ushort id, sbyte z, TileFlag flag)
{
m_ID = Art.GetLegalItemID(id);
m_Z = z;
m_Flag = flag;
m_Solver = 0;
}
public void Set(ushort id, sbyte z)
{
m_ID = Art.GetLegalItemID(id);
m_Z = z;
}
public void Set(ushort id, sbyte z, TileFlag flag)
{
m_ID = Art.GetLegalItemID(id);
m_Z = z;
m_Flag = flag;
}
public int CompareTo(object x)
{
if (x == null)
{
return 1;
}
if (!(x is MTile))
{
throw new ArgumentNullException();
}
var a = (MTile)x;
ItemData ourData = TileData.ItemTable[m_ID];
ItemData theirData = TileData.ItemTable[a.ID];
int ourTreshold = 0;
if (ourData.Height > 0)
{
++ourTreshold;
}
if (!ourData.Background)
{
++ourTreshold;
}
int ourZ = Z;
int theirTreshold = 0;
if (theirData.Height > 0)
{
++theirTreshold;
}
if (!theirData.Background)
{
++theirTreshold;
}
int theirZ = a.Z;
ourZ += ourTreshold;
theirZ += theirTreshold;
int res = ourZ - theirZ;
if (res == 0)
{
res = ourTreshold - theirTreshold;
}
if (res == 0)
{
res = m_Solver - a.Solver;
}
return res;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Tile : IComparable
{
internal ushort m_ID;
internal sbyte m_Z;
public ushort ID { get { return m_ID; } }
public int Z { get { return m_Z; } set { m_Z = (sbyte)value; } }
public Tile(ushort id, sbyte z)
{
m_ID = id;
m_Z = z;
}
public Tile(ushort id, sbyte z, sbyte flag)
{
m_ID = id;
m_Z = z;
}
public void Set(ushort id, sbyte z)
{
m_ID = id;
m_Z = z;
}
public void Set(ushort id, sbyte z, sbyte flag)
{
m_ID = id;
m_Z = z;
}
public int CompareTo(object x)
{
if (x == null)
{
return 1;
}
if (!(x is Tile))
{
throw new ArgumentNullException();
}
var a = (Tile)x;
if (m_Z > a.m_Z)
{
return 1;
}
else if (a.m_Z > m_Z)
{
return -1;
}
ItemData ourData = TileData.ItemTable[m_ID];
ItemData theirData = TileData.ItemTable[a.m_ID];
if (ourData.Height > theirData.Height)
{
return 1;
}
else if (theirData.Height > ourData.Height)
{
return -1;
}
if (ourData.Background && !theirData.Background)
{
return -1;
}
else if (theirData.Background && !ourData.Background)
{
return 1;
}
return 0;
}
}
}