first commit
This commit is contained in:
@@ -0,0 +1,382 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using ClassicAssist.Misc;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public static class Art
|
||||
{
|
||||
private static string _dataPath;
|
||||
private static bool _isUOPFormat;
|
||||
private static Lazy<Dictionary<int, Entry3D>> _lazyIndex;
|
||||
|
||||
public static bool Initialize( string dataPath )
|
||||
{
|
||||
_dataPath = dataPath;
|
||||
|
||||
if ( File.Exists( Path.Combine( dataPath, "artLegacyMUL.uop" ) ) )
|
||||
{
|
||||
_isUOPFormat = true;
|
||||
}
|
||||
|
||||
_lazyIndex = new Lazy<Dictionary<int, Entry3D>>( () => _isUOPFormat ? LoadUOPIndex() : LoadMULIndex() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Dictionary<int, Entry3D> LoadMULIndex()
|
||||
{
|
||||
int entrySize = Marshal.SizeOf( typeof( Entry3D ) );
|
||||
byte[] buffer = new byte[entrySize];
|
||||
GCHandle pinnedBuffer = GCHandle.Alloc( buffer, GCHandleType.Pinned );
|
||||
int index = 0;
|
||||
|
||||
Dictionary<int, Entry3D> indexes = new Dictionary<int, Entry3D>();
|
||||
|
||||
using ( FileStream reader = File.Open( Path.Combine( _dataPath, "artidx.mul" ), FileMode.Open,
|
||||
FileAccess.Read, FileShare.ReadWrite ) )
|
||||
{
|
||||
int bytesRead;
|
||||
|
||||
do
|
||||
{
|
||||
bytesRead = reader.Read( buffer, 0, entrySize );
|
||||
indexes[index++] =
|
||||
(Entry3D) Marshal.PtrToStructure( pinnedBuffer.AddrOfPinnedObject(), typeof( Entry3D ) );
|
||||
}
|
||||
while ( bytesRead > 0 );
|
||||
}
|
||||
|
||||
return indexes;
|
||||
}
|
||||
|
||||
private static Dictionary<int, Entry3D> LoadUOPIndex()
|
||||
{
|
||||
Dictionary<ulong, int> hashes = new Dictionary<ulong, int>();
|
||||
Dictionary<int, Entry3D> indexes = new Dictionary<int, Entry3D>();
|
||||
|
||||
using ( FileStream reader = File.Open( Path.Combine( _dataPath, "artLegacyMUL.uop" ), FileMode.Open,
|
||||
FileAccess.Read, FileShare.ReadWrite ) )
|
||||
{
|
||||
using ( BinaryReader bin = new BinaryReader( reader ) )
|
||||
{
|
||||
reader.Seek( 12, SeekOrigin.Current );
|
||||
int firstAddress = bin.ReadInt32();
|
||||
|
||||
for ( int i = 0; i < 0x10000 /*formatHeader.NumberOfFiles*/; i++ )
|
||||
{
|
||||
string entryName = $"build/artlegacymul/{i:D8}.tga";
|
||||
ulong hash = HashFileName( entryName );
|
||||
|
||||
if ( !hashes.ContainsKey( hash ) )
|
||||
{
|
||||
hashes.Add( hash, i );
|
||||
}
|
||||
}
|
||||
|
||||
long nextAddress = firstAddress;
|
||||
|
||||
do
|
||||
{
|
||||
reader.Seek( nextAddress, SeekOrigin.Begin );
|
||||
UOPBlockHeader blockHeader = reader.ReadStruct<UOPBlockHeader>();
|
||||
|
||||
nextAddress = blockHeader.NextAddress;
|
||||
|
||||
for ( int i = 0; i < blockHeader.NumberOfFiles; i++ )
|
||||
{
|
||||
UOPFileHeader fileHeader = reader.ReadStruct<UOPFileHeader>();
|
||||
|
||||
if ( fileHeader.DataHeaderAddress == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( hashes.ContainsKey( fileHeader.Hash ) )
|
||||
{
|
||||
int index = hashes[fileHeader.Hash];
|
||||
|
||||
indexes[index] = new Entry3D( (int) fileHeader.DataHeaderAddress + fileHeader.Length,
|
||||
fileHeader.IsCompressed == 1
|
||||
? fileHeader.CompressedSize
|
||||
: fileHeader.DecompressedSize, 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
while ( nextAddress > 0 );
|
||||
}
|
||||
}
|
||||
|
||||
return indexes;
|
||||
}
|
||||
|
||||
public static Bitmap GetStatic( int itemID, int hue )
|
||||
{
|
||||
Bitmap bmp = GetStatic( itemID );
|
||||
|
||||
if ( hue == 0 || bmp == null )
|
||||
{
|
||||
return bmp;
|
||||
}
|
||||
|
||||
StaticTile tileData = TileData.GetStaticTile( itemID );
|
||||
|
||||
Hues.ApplyHue( hue, bmp, tileData.Flags.HasFlag( TileFlags.PartialHue ) );
|
||||
|
||||
return bmp;
|
||||
}
|
||||
|
||||
public static unsafe Bitmap GetStatic( int itemId )
|
||||
{
|
||||
itemId += 0x4000;
|
||||
|
||||
string fileName = Path.Combine( _dataPath, "art.mul" );
|
||||
|
||||
if ( _isUOPFormat )
|
||||
{
|
||||
fileName = Path.Combine( _dataPath, "artLegacyMUL.uop" );
|
||||
}
|
||||
|
||||
FileStream artFile = File.Open( fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite );
|
||||
|
||||
Entry3D entry;
|
||||
|
||||
try
|
||||
{
|
||||
entry = _lazyIndex.Value[itemId];
|
||||
}
|
||||
catch ( KeyNotFoundException )
|
||||
{
|
||||
entry = _lazyIndex.Value[0x4000];
|
||||
}
|
||||
|
||||
artFile.Seek( entry.Lookup, SeekOrigin.Begin );
|
||||
|
||||
using ( BinaryReader reader = new BinaryReader( artFile ) )
|
||||
{
|
||||
reader.ReadInt32();
|
||||
|
||||
int width = reader.ReadInt16();
|
||||
int height = reader.ReadInt16();
|
||||
|
||||
if ( width <= 0 || height <= 0 )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int[] lookups = new int[height];
|
||||
|
||||
int start = (int) reader.BaseStream.Position + height * 2;
|
||||
|
||||
for ( int i = 0; i < height; i++ )
|
||||
{
|
||||
lookups[i] = start + reader.ReadUInt16() * 2;
|
||||
}
|
||||
|
||||
Bitmap bmp = new Bitmap( width, height, PixelFormat.Format16bppArgb1555 );
|
||||
|
||||
BitmapData bd = bmp.LockBits( new Rectangle( 0, 0, width, height ), ImageLockMode.WriteOnly,
|
||||
PixelFormat.Format16bppArgb1555 );
|
||||
|
||||
ushort* line = (ushort*) bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
|
||||
int largestX = 0;
|
||||
int smallestX = width;
|
||||
int largestY = 0;
|
||||
int smallestY = height;
|
||||
|
||||
for ( int y = 0; y < height; y++, line += delta )
|
||||
{
|
||||
reader.BaseStream.Seek( lookups[y], SeekOrigin.Begin );
|
||||
|
||||
ushort* cur = line;
|
||||
|
||||
int xOffset, xRun;
|
||||
int x = 0;
|
||||
|
||||
if ( y > largestY )
|
||||
{
|
||||
largestY = y + 1;
|
||||
}
|
||||
|
||||
if ( y < smallestY )
|
||||
{
|
||||
smallestY = y;
|
||||
}
|
||||
|
||||
while ( ( xOffset = reader.ReadUInt16() ) + ( xRun = reader.ReadUInt16() ) != 0 )
|
||||
{
|
||||
cur += xOffset;
|
||||
ushort* end = cur + xRun;
|
||||
x += xOffset;
|
||||
|
||||
if ( x < smallestX )
|
||||
{
|
||||
smallestX = x;
|
||||
}
|
||||
|
||||
while ( cur < end )
|
||||
{
|
||||
*cur++ = (ushort) ( reader.ReadUInt16() ^ 0x8000 );
|
||||
x++;
|
||||
}
|
||||
|
||||
if ( x > largestX )
|
||||
{
|
||||
largestX = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bmp.UnlockBits( bd );
|
||||
|
||||
return bmp;
|
||||
}
|
||||
}
|
||||
|
||||
private static ulong HashFileName( string s )
|
||||
{
|
||||
uint ecx, edx, ebx, esi, edi;
|
||||
uint eax = ecx = edx = ebx = esi = edi = 0;
|
||||
ebx = edi = esi = (uint) s.Length + 0xDEADBEEF;
|
||||
int i = 0;
|
||||
|
||||
for ( i = 0; i + 12 < s.Length; i += 12 )
|
||||
{
|
||||
edi = (uint) ( ( s[i + 7] << 24 ) | ( s[i + 6] << 16 ) | ( s[i + 5] << 8 ) | s[i + 4] ) + edi;
|
||||
esi = (uint) ( ( s[i + 11] << 24 ) | ( s[i + 10] << 16 ) | ( s[i + 9] << 8 ) | s[i + 8] ) + esi;
|
||||
edx = (uint) ( ( s[i + 3] << 24 ) | ( s[i + 2] << 16 ) | ( s[i + 1] << 8 ) | s[i] ) - esi;
|
||||
edx = ( edx + ebx ) ^ ( esi >> 28 ) ^ ( esi << 4 );
|
||||
esi += edi;
|
||||
edi = ( edi - edx ) ^ ( edx >> 26 ) ^ ( edx << 6 );
|
||||
edx += esi;
|
||||
esi = ( esi - edi ) ^ ( edi >> 24 ) ^ ( edi << 8 );
|
||||
edi += edx;
|
||||
ebx = ( edx - esi ) ^ ( esi >> 16 ) ^ ( esi << 16 );
|
||||
esi += edi;
|
||||
edi = ( edi - ebx ) ^ ( ebx >> 13 ) ^ ( ebx << 19 );
|
||||
ebx += esi;
|
||||
esi = ( esi - edi ) ^ ( edi >> 28 ) ^ ( edi << 4 );
|
||||
edi += ebx;
|
||||
}
|
||||
|
||||
if ( s.Length - i > 0 )
|
||||
{
|
||||
switch ( s.Length - i )
|
||||
{
|
||||
case 12:
|
||||
esi += (uint) s[i + 11] << 24;
|
||||
goto case 11;
|
||||
case 11:
|
||||
esi += (uint) s[i + 10] << 16;
|
||||
goto case 10;
|
||||
case 10:
|
||||
esi += (uint) s[i + 9] << 8;
|
||||
goto case 9;
|
||||
case 9:
|
||||
esi += s[i + 8];
|
||||
goto case 8;
|
||||
case 8:
|
||||
edi += (uint) s[i + 7] << 24;
|
||||
goto case 7;
|
||||
case 7:
|
||||
edi += (uint) s[i + 6] << 16;
|
||||
goto case 6;
|
||||
case 6:
|
||||
edi += (uint) s[i + 5] << 8;
|
||||
goto case 5;
|
||||
case 5:
|
||||
edi += s[i + 4];
|
||||
goto case 4;
|
||||
case 4:
|
||||
ebx += (uint) s[i + 3] << 24;
|
||||
goto case 3;
|
||||
case 3:
|
||||
ebx += (uint) s[i + 2] << 16;
|
||||
goto case 2;
|
||||
case 2:
|
||||
ebx += (uint) s[i + 1] << 8;
|
||||
goto case 1;
|
||||
case 1:
|
||||
ebx += s[i];
|
||||
break;
|
||||
}
|
||||
|
||||
esi = ( esi ^ edi ) - ( ( edi >> 18 ) ^ ( edi << 14 ) );
|
||||
ecx = ( esi ^ ebx ) - ( ( esi >> 21 ) ^ ( esi << 11 ) );
|
||||
edi = ( edi ^ ecx ) - ( ( ecx >> 7 ) ^ ( ecx << 25 ) );
|
||||
esi = ( esi ^ edi ) - ( ( edi >> 16 ) ^ ( edi << 16 ) );
|
||||
edx = ( esi ^ ecx ) - ( ( esi >> 28 ) ^ ( esi << 4 ) );
|
||||
edi = ( edi ^ edx ) - ( ( edx >> 18 ) ^ ( edx << 14 ) );
|
||||
eax = ( esi ^ edi ) - ( ( edi >> 8 ) ^ ( edi << 24 ) );
|
||||
return ( (ulong) edi << 32 ) | eax;
|
||||
}
|
||||
|
||||
return ( (ulong) esi << 32 ) | eax;
|
||||
}
|
||||
|
||||
#region Structures
|
||||
|
||||
[StructLayout( LayoutKind.Explicit )]
|
||||
private struct Entry3D
|
||||
{
|
||||
[FieldOffset( 0 )]
|
||||
public readonly int Lookup;
|
||||
|
||||
[FieldOffset( 4 )]
|
||||
public readonly int Length;
|
||||
|
||||
[FieldOffset( 8 )]
|
||||
public readonly int Extra;
|
||||
|
||||
public Entry3D( int lookup, int length, int extra )
|
||||
{
|
||||
Lookup = lookup;
|
||||
Length = length;
|
||||
Extra = extra;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout( LayoutKind.Explicit, Size = 12 )]
|
||||
private struct UOPBlockHeader
|
||||
{
|
||||
[FieldOffset( 0 )]
|
||||
public readonly int NumberOfFiles;
|
||||
|
||||
[FieldOffset( 4 )]
|
||||
public readonly long NextAddress;
|
||||
}
|
||||
|
||||
[StructLayout( LayoutKind.Explicit, Pack = 0, Size = 34 )]
|
||||
private struct UOPFileHeader
|
||||
{
|
||||
[FieldOffset( 0 )]
|
||||
public readonly long DataHeaderAddress;
|
||||
|
||||
[FieldOffset( 8 )]
|
||||
public readonly int Length;
|
||||
|
||||
[FieldOffset( 12 )]
|
||||
public readonly int CompressedSize;
|
||||
|
||||
[FieldOffset( 16 )]
|
||||
public readonly int DecompressedSize;
|
||||
|
||||
[FieldOffset( 20 )]
|
||||
public readonly ulong Hash;
|
||||
|
||||
[FieldOffset( 28 )]
|
||||
public readonly int Unknown;
|
||||
|
||||
[FieldOffset( 32 )]
|
||||
public readonly short IsCompressed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public static class Cliloc
|
||||
{
|
||||
private static readonly Lazy<Dictionary<int, string>> _lazyClilocList =
|
||||
new Lazy<Dictionary<int, string>>( LoadClilocs );
|
||||
|
||||
private static string _dataPath;
|
||||
|
||||
private static Dictionary<int, string> LoadClilocs()
|
||||
{
|
||||
string filename = Path.Combine( _dataPath, "Cliloc.enu" );
|
||||
|
||||
if ( !File.Exists( filename ) )
|
||||
{
|
||||
throw new FileNotFoundException( "File not found.", filename );
|
||||
}
|
||||
|
||||
byte[] fileBytes = File.ReadAllBytes( filename );
|
||||
|
||||
Dictionary<int, string> clilocList = new Dictionary<int, string>( 100000 );
|
||||
|
||||
ushort len;
|
||||
|
||||
for ( int x = 6; x + 7 <= fileBytes.Length; x += 7 + len )
|
||||
{
|
||||
len = BitConverter.ToUInt16( fileBytes, x + 5 );
|
||||
|
||||
if ( x + 7 + len > fileBytes.Length )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
int key = BitConverter.ToInt32( fileBytes, x );
|
||||
string value = Encoding.UTF8.GetString( fileBytes, x + 7, len );
|
||||
clilocList[key] = value;
|
||||
}
|
||||
catch
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return clilocList;
|
||||
}
|
||||
|
||||
public static string GetLocalString( string tokenizedString )
|
||||
{
|
||||
if ( tokenizedString.ToLower().Contains( "http://" ) || tokenizedString.ToLower().Contains( "https://" ) )
|
||||
{
|
||||
return tokenizedString;
|
||||
}
|
||||
|
||||
while ( tokenizedString.Contains( "#" ) )
|
||||
{
|
||||
for ( int x = 0; x < tokenizedString.Length; x++ )
|
||||
{
|
||||
if ( tokenizedString[x] != '#' || x >= tokenizedString.Length - 1 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !char.IsNumber( tokenizedString[x + 1] ) )
|
||||
{
|
||||
return tokenizedString;
|
||||
}
|
||||
|
||||
int y;
|
||||
|
||||
for ( y = x + 1; y < tokenizedString.Length; y++ )
|
||||
{
|
||||
if ( !char.IsNumber( tokenizedString[y] ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string token = tokenizedString.Substring( x, y - x );
|
||||
string tokenNum = tokenizedString.Substring( x + 1, y - x - 1 );
|
||||
|
||||
if ( tokenNum.Length <= 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int propertyNum;
|
||||
|
||||
try
|
||||
{
|
||||
propertyNum = Convert.ToInt32( tokenNum );
|
||||
}
|
||||
catch
|
||||
{
|
||||
return tokenizedString;
|
||||
}
|
||||
|
||||
string property = GetProperty( propertyNum );
|
||||
tokenizedString = tokenizedString.Replace( token, property );
|
||||
}
|
||||
}
|
||||
|
||||
return tokenizedString;
|
||||
}
|
||||
|
||||
public static string GetLocalString( int property, string[] arguments )
|
||||
{
|
||||
string propertyString = GetProperty( property );
|
||||
|
||||
if ( arguments == null )
|
||||
{
|
||||
return propertyString;
|
||||
}
|
||||
|
||||
//foreach (string s in arguments)
|
||||
for ( int x = 0; x < arguments.Length; x++ )
|
||||
{
|
||||
arguments[x] = GetLocalString( arguments[x] );
|
||||
bool found = false;
|
||||
int start = 0;
|
||||
int index = 0;
|
||||
|
||||
foreach ( char c in propertyString )
|
||||
{
|
||||
if ( c == '~' )
|
||||
{
|
||||
if ( found )
|
||||
{
|
||||
string subString = propertyString.Substring( start, index - start + 1 );
|
||||
propertyString = propertyString.Replace( subString, arguments[x] );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
start = index;
|
||||
found = true;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if ( !found )
|
||||
{
|
||||
return propertyString;
|
||||
}
|
||||
}
|
||||
|
||||
return propertyString;
|
||||
}
|
||||
|
||||
public static void Initialize( string dataPath )
|
||||
{
|
||||
_dataPath = dataPath;
|
||||
}
|
||||
|
||||
public static string GetProperty( int property )
|
||||
{
|
||||
return _lazyClilocList.Value.TryGetValue( property, out string propertyString )
|
||||
? propertyString
|
||||
: $"Localized string {property} not found!";
|
||||
}
|
||||
|
||||
public static Dictionary<int, string> GetItems()
|
||||
{
|
||||
return new Dictionary<int, string>( _lazyClilocList.Value );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public enum Direction : byte
|
||||
{
|
||||
North,
|
||||
Northeast,
|
||||
East,
|
||||
Southeast,
|
||||
South,
|
||||
Southwest,
|
||||
West,
|
||||
Northwest,
|
||||
Invalid
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
[Flags]
|
||||
public enum MessageAffixType
|
||||
{
|
||||
Append,
|
||||
Prepend,
|
||||
System
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum HealthbarColour
|
||||
{
|
||||
None = 0x00,
|
||||
Green = 0x01,
|
||||
Yellow = 0x02,
|
||||
Red = 0x04
|
||||
}
|
||||
|
||||
public enum TargetFlags : byte
|
||||
{
|
||||
None,
|
||||
Harmful,
|
||||
Beneficial,
|
||||
Cancel
|
||||
}
|
||||
|
||||
public enum Virtues
|
||||
{
|
||||
None = '0',
|
||||
Honor,
|
||||
Sacrafice,
|
||||
Valor,
|
||||
Compassion,
|
||||
Honesty,
|
||||
Humility,
|
||||
Justice,
|
||||
Spirituality
|
||||
}
|
||||
|
||||
public enum MobileRace : byte
|
||||
{
|
||||
Unknown,
|
||||
Human,
|
||||
Elf,
|
||||
Gargoyle
|
||||
}
|
||||
|
||||
public enum JournalSpeech : byte
|
||||
{
|
||||
Say,
|
||||
System,
|
||||
Emote,
|
||||
Unknown1,
|
||||
Unknown2,
|
||||
Unknown3,
|
||||
Label,
|
||||
Focus,
|
||||
Whisper,
|
||||
Yell,
|
||||
Spell,
|
||||
Unknown4,
|
||||
Unknown5,
|
||||
Guild,
|
||||
Alliance,
|
||||
GM
|
||||
}
|
||||
|
||||
public enum MobileQueryType : byte
|
||||
{
|
||||
StatsRequest = 4,
|
||||
SkillsRequest = 5
|
||||
}
|
||||
|
||||
public enum TargetType : byte
|
||||
{
|
||||
Object,
|
||||
Tile
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum FeatureFlags
|
||||
{
|
||||
None = 0x00000000,
|
||||
T2A = 0x00000001,
|
||||
UOR = 0x00000002,
|
||||
UOTD = 0x00000004,
|
||||
LBR = 0x00000008,
|
||||
AOS = 0x00000010,
|
||||
SixthCharacterSlot = 0x00000020,
|
||||
SE = 0x00000040,
|
||||
ML = 0x00000080,
|
||||
EigthAge = 0x00000100,
|
||||
NinthAge = 0x00000200, /* Crystal/Shadow Custom House Tiles */
|
||||
TenthAge = 0x00000400,
|
||||
IncreasedStorage = 0x00000800, /* Increased Housing/Bank Storage */
|
||||
SeventhCharacterSlot = 0x00001000,
|
||||
RoleplayFaces = 0x00002000,
|
||||
TrialAccount = 0x00004000,
|
||||
LiveAccount = 0x00008000,
|
||||
SA = 0x00010000,
|
||||
HS = 0x00020000,
|
||||
Gothic = 0x00040000,
|
||||
Rustic = 0x00080000,
|
||||
Jungle = 0x00100000,
|
||||
Shadowguard = 0x00200000,
|
||||
TOL = 0x00400000,
|
||||
EJ = 0x00800000
|
||||
}
|
||||
|
||||
public enum Notoriety : byte
|
||||
{
|
||||
Invalid,
|
||||
Innocent,
|
||||
Ally,
|
||||
Attackable,
|
||||
Criminal,
|
||||
Enemy,
|
||||
Murderer,
|
||||
Invulnerable,
|
||||
Unknown
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum MobileStatus : byte
|
||||
{
|
||||
None = 0x00,
|
||||
Frozen = 0x01,
|
||||
Female = 0x02,
|
||||
Flying = 0x04,
|
||||
Invulnerable = 0x08,
|
||||
IgnoreMobiles = 0x10,
|
||||
Unknown = 0x20,
|
||||
WarMode = 0x40,
|
||||
Hidden = 0x80
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Linq;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.UO.Network;
|
||||
using ClassicAssist.UO.Network.Packets;
|
||||
using ClassicAssist.UO.Objects.Gumps;
|
||||
|
||||
namespace ClassicAssist.UO.Data.Gumps
|
||||
{
|
||||
public class BoatGumpExtension : IExtension
|
||||
{
|
||||
public void Initialize()
|
||||
{
|
||||
IncomingPacketHandlers.JournalEntryAddedEvent += OnJournalEntryAddedEvent;
|
||||
}
|
||||
|
||||
private static void OnJournalEntryAddedEvent( JournalEntry je )
|
||||
{
|
||||
switch ( je.Cliloc )
|
||||
{
|
||||
/* You are now piloting this vessel. */
|
||||
case 1116727:
|
||||
new BoatMovementGump().SendGump();
|
||||
break;
|
||||
/* You are no longer piloting this vessel. */
|
||||
case 1149592:
|
||||
Shared.UO.Commands.CloseClientGump( typeof( BoatMovementGump ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BoatMovementGump : Gump
|
||||
{
|
||||
private static BoatSpeed _lastSpeed = BoatSpeed.OneTile;
|
||||
|
||||
public BoatMovementGump() : base( 100, 100 )
|
||||
{
|
||||
Movable = true;
|
||||
Closable = true;
|
||||
Resizable = false;
|
||||
Disposable = false;
|
||||
AddPage( 0 );
|
||||
AddBackground( 0, 0, 150, 175, 9200 );
|
||||
AddButton( 0, 0, 4507, 4507, (int) Direction.Northwest + 1, GumpButtonType.Reply, 0 );
|
||||
AddButton( 50, 0, 4500, 4500, (int) Direction.North + 1, GumpButtonType.Reply, 0 );
|
||||
AddButton( 100, 0, 4501, 4501, (int) Direction.Northeast + 1, GumpButtonType.Reply, 0 );
|
||||
AddButton( 10, 50, 4506, 4506, (int) Direction.West + 1, GumpButtonType.Reply, 0 );
|
||||
AddButton( 90, 50, 4502, 4502, (int) Direction.East + 1, GumpButtonType.Reply, 0 );
|
||||
AddButton( 0, 100, 4505, 4505, (int) Direction.Southwest + 1, GumpButtonType.Reply, 0 );
|
||||
AddButton( 50, 100, 4504, 4504, (int) Direction.South + 1, GumpButtonType.Reply, 0 );
|
||||
AddButton( 100, 100, 4503, 4503, (int) Direction.Southeast + 1, GumpButtonType.Reply, 0 );
|
||||
AddRadio( 10, 150, 208, 209, _lastSpeed == BoatSpeed.Normal, 1 );
|
||||
AddLabel( 40, 150, 0, "Fast" );
|
||||
AddButton( 60, 60, 2151, 2151, 10, GumpButtonType.Reply, 0 );
|
||||
}
|
||||
|
||||
public override void OnResponse( int buttonID, int[] switches )
|
||||
{
|
||||
if ( buttonID == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BoatSpeed speed = BoatSpeed.OneTile;
|
||||
|
||||
if ( switches?.Any( e => e == 1 ) ?? false )
|
||||
{
|
||||
speed = BoatSpeed.Normal;
|
||||
}
|
||||
|
||||
_lastSpeed = speed;
|
||||
|
||||
if ( buttonID < 9 )
|
||||
{
|
||||
Engine.SendPacketToServer( new WheelBoatMoving( (Direction) buttonID - 1, speed ) );
|
||||
}
|
||||
|
||||
if ( buttonID == 10 )
|
||||
{
|
||||
Engine.SendPacketToServer( new WheelBoatMoving( Direction.North, BoatSpeed.Stop ) );
|
||||
}
|
||||
|
||||
new BoatMovementGump().SendGump();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
using System.Threading;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.Helpers;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.UO.Network.PacketFilter;
|
||||
using ClassicAssist.UO.Objects.Gumps;
|
||||
|
||||
namespace ClassicAssist.UO.Gumps
|
||||
{
|
||||
public class ConfirmPromptGump : Gump
|
||||
{
|
||||
public ConfirmPromptGump( string message, bool closable = false ) : base( 500, 500 )
|
||||
{
|
||||
const int width = 300;
|
||||
const int height = 200;
|
||||
|
||||
SetCenterPosition( width, height );
|
||||
Closable = closable;
|
||||
Resizable = false;
|
||||
Disposable = false;
|
||||
Movable = true;
|
||||
AddPage( 0 );
|
||||
AddBackground( 0, 0, width, height, 9200 );
|
||||
AddHtml( 10, 10, width - 20, height - 50, message, true, true );
|
||||
AddButton( width - 140, height - 30, 247, 248, 1, GumpButtonType.Reply, 0 );
|
||||
AddButton( width - 70, height - 30, 241, 242, 2, GumpButtonType.Reply, 0 );
|
||||
}
|
||||
|
||||
public AutoResetEvent AutoResetEvent { get; set; } = new AutoResetEvent( false );
|
||||
|
||||
public bool Result { get; set; }
|
||||
|
||||
public override void OnResponse( int buttonID, int[] switches )
|
||||
{
|
||||
Result = buttonID == 1;
|
||||
AutoResetEvent.Set();
|
||||
}
|
||||
|
||||
public static bool ConfirmPrompt( string message, bool closable = false )
|
||||
{
|
||||
ConfirmPromptGump gump = new ConfirmPromptGump( message, closable );
|
||||
|
||||
PacketFilterInfo pfi = new PacketFilterInfo( 0xB1,
|
||||
new[] { PacketFilterConditions.UIntAtPositionCondition( gump.ID, 7 ) } );
|
||||
|
||||
Engine.AddSendPostFilter( pfi );
|
||||
|
||||
gump.SendGump();
|
||||
|
||||
gump.AutoResetEvent.WaitOne();
|
||||
|
||||
Engine.RemoveSendPostFilter( pfi );
|
||||
|
||||
return gump.Result;
|
||||
}
|
||||
|
||||
private static Point GetWindowSize()
|
||||
{
|
||||
if ( !NativeMethods.GetWindowRect( Engine.WindowHandle, out NativeMethods.RECT rect ) )
|
||||
{
|
||||
return Point.Empty;
|
||||
}
|
||||
|
||||
int windowWidth = rect.Right - rect.Left;
|
||||
int windowHeight = rect.Bottom - rect.Top;
|
||||
|
||||
return new Point( windowWidth, windowHeight );
|
||||
}
|
||||
|
||||
public static Point GetGameWindowCenter()
|
||||
{
|
||||
dynamic currentProfile =
|
||||
Reflection.GetTypePropertyValue<dynamic>( "ClassicUO.Configuration.ProfileManager", "Current", null );
|
||||
|
||||
if ( currentProfile == null )
|
||||
{
|
||||
return Point.Empty;
|
||||
}
|
||||
|
||||
dynamic gameWindowSize =
|
||||
Reflection.GetTypePropertyValue<dynamic>( currentProfile.GetType(), "GameWindowSize", currentProfile );
|
||||
dynamic gameWindowPosition = Reflection.GetTypePropertyValue<dynamic>( currentProfile.GetType(),
|
||||
"GameWindowPosition", currentProfile );
|
||||
|
||||
if ( gameWindowSize == null || gameWindowPosition == null )
|
||||
{
|
||||
return Point.Empty;
|
||||
}
|
||||
|
||||
return new Point( gameWindowPosition.X + ( gameWindowSize.X >> 1 ),
|
||||
gameWindowPosition.Y + ( gameWindowSize.Y >> 1 ) );
|
||||
}
|
||||
|
||||
private void SetCenterPosition( int width, int height )
|
||||
{
|
||||
Point gameCenterPosition = GetGameWindowCenter();
|
||||
|
||||
if ( gameCenterPosition == Point.Empty )
|
||||
{
|
||||
Point size = GetWindowSize();
|
||||
|
||||
X = ( size.X - ( width ) ) >> 1;
|
||||
Y = ( size.Y - ( height ) ) >> 1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
X = gameCenterPosition.X - width / 2;
|
||||
Y = gameCenterPosition.Y - height / 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Macros;
|
||||
using ClassicAssist.UO.Objects.Gumps;
|
||||
|
||||
namespace ClassicAssist.UO.Gumps
|
||||
{
|
||||
public class MacrosGump : RepositionableGump
|
||||
{
|
||||
private static Timer _timer;
|
||||
private static string _lastList;
|
||||
private static int _serial = 0x0fe00000;
|
||||
|
||||
public MacrosGump( string html ) : base( 190, 180, _serial++, (uint) _serial++ )
|
||||
{
|
||||
GumpX = Options.CurrentOptions.MacrosGumpX;
|
||||
GumpY = Options.CurrentOptions.MacrosGumpY;
|
||||
|
||||
Movable = false;
|
||||
Closable = false;
|
||||
Resizable = false;
|
||||
Disposable = false;
|
||||
AddPage( 0 );
|
||||
AddBackground( 0, 0, 190, 180, 3500 );
|
||||
AddHtml( 20, 20, 150, 140, html, false, true );
|
||||
}
|
||||
|
||||
public static void ResendGump( bool force = false )
|
||||
{
|
||||
try
|
||||
{
|
||||
MacroManager _macroManager = MacroManager.GetInstance();
|
||||
|
||||
IEnumerable<MacroEntry> macro = _macroManager.Items.Where( e => e.IsRunning );
|
||||
|
||||
string html = string.Empty;
|
||||
|
||||
foreach ( MacroEntry entry in macro )
|
||||
{
|
||||
if ( entry.IsBackground )
|
||||
{
|
||||
html += $"<BASEFONT COLOR=#000000><I>{entry.Name}</I></BASEFONT>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
html += $"<BASEFONT COLOR=#000000>{entry.Name}</BASEFONT>\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ( html.Equals( _lastList ) && !force )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( Engine.Gumps.GetGumps( out Gump[] gumps ) )
|
||||
{
|
||||
foreach ( Gump macrosGump in gumps.Where( g => g is MacrosGump ) )
|
||||
{
|
||||
Shared.UO.Commands.CloseClientGump( macrosGump.ID );
|
||||
}
|
||||
}
|
||||
|
||||
MacrosGump gump = new MacrosGump( html );
|
||||
gump.SendGump();
|
||||
|
||||
_lastList = html;
|
||||
}
|
||||
catch ( InvalidOperationException e )
|
||||
{
|
||||
Console.WriteLine( e.ToString() );
|
||||
}
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
_timer = new Timer( o => ResendGump(), null, 1000, 250 );
|
||||
}
|
||||
|
||||
public override void SetPosition( int x, int y )
|
||||
{
|
||||
base.SetPosition( x, y );
|
||||
|
||||
Options.CurrentOptions.MacrosGumpX = x;
|
||||
Options.CurrentOptions.MacrosGumpY = y;
|
||||
|
||||
ResendGump( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
#region License
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using ClassicAssist.UO.Data;
|
||||
|
||||
namespace ClassicAssist.UO.Network.Packets
|
||||
{
|
||||
public class Pathfind : BasePacket
|
||||
{
|
||||
public Pathfind(int x, int y, int z)
|
||||
{
|
||||
_writer = new PacketWriter(7);
|
||||
_writer.Write((byte)0x38);
|
||||
_writer.Write((short)x);
|
||||
_writer.Write((short)y);
|
||||
_writer.Write((short)z);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Threading;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.UI.ViewModels;
|
||||
using ClassicAssist.UO.Objects.Gumps;
|
||||
|
||||
namespace ClassicAssist.UO.Gumps
|
||||
{
|
||||
public abstract class RepositionableGump : Gump
|
||||
{
|
||||
private const int REPOSITION_BUTTON_ID = 10;
|
||||
private readonly int _height;
|
||||
private readonly int _width;
|
||||
|
||||
protected RepositionableGump( int width, int height, int serial, uint gumpID ) : base( 0, 0, serial,
|
||||
gumpID )
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
}
|
||||
|
||||
public int GumpX { get; set; } = 100;
|
||||
public int GumpY { get; set; } = 100;
|
||||
|
||||
public override void SendGump()
|
||||
{
|
||||
X = GumpX;
|
||||
Y = GumpY;
|
||||
AddButton( _width - 15, 5, 0x82C, 0x82C, REPOSITION_BUTTON_ID, GumpButtonType.Reply, 0 );
|
||||
|
||||
base.SendGump();
|
||||
}
|
||||
|
||||
public override void OnResponse( int buttonID, int[] switches )
|
||||
{
|
||||
if ( buttonID == REPOSITION_BUTTON_ID )
|
||||
{
|
||||
Engine.Dispatcher.Invoke( () =>
|
||||
{
|
||||
Thread t = new Thread( () =>
|
||||
{
|
||||
SetPosition( GumpX, GumpY );
|
||||
RepositionableGumpViewModel vm = new RepositionableGumpViewModel( this, GumpX, GumpY );
|
||||
//TODO UI
|
||||
//RepositionableGumpWindow window = new RepositionableGumpWindow { DataContext = vm };
|
||||
//WindowInteropHelper helper = new WindowInteropHelper( window ) { Owner = Engine.WindowHandle };
|
||||
//window.ShowInTaskbar = false;
|
||||
//window.ShowDialog();
|
||||
} )
|
||||
{ IsBackground = true };
|
||||
|
||||
t.SetApartmentState( ApartmentState.STA );
|
||||
t.Start();
|
||||
} );
|
||||
}
|
||||
|
||||
base.OnResponse( buttonID, switches );
|
||||
}
|
||||
|
||||
public virtual void SetPosition( int x, int y )
|
||||
{
|
||||
GumpX = x;
|
||||
GumpY = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Misc;
|
||||
using ClassicAssist.UO.Objects.Gumps;
|
||||
|
||||
namespace ClassicAssist.UO.Gumps
|
||||
{
|
||||
public sealed class UpdateMessageGump : Gump
|
||||
{
|
||||
private readonly Version _version;
|
||||
|
||||
public UpdateMessageGump( IntPtr hWnd, string message, Version version ) : base( 500, 250, -1 )
|
||||
{
|
||||
if ( NativeMethods.GetWindowRect( hWnd, out NativeMethods.RECT rect ) )
|
||||
{
|
||||
int windowWidth = rect.Right - rect.Left;
|
||||
int windowHeight = rect.Bottom - rect.Top;
|
||||
|
||||
X = ( windowWidth - 600 ) / 2;
|
||||
Y = ( windowHeight - 400 ) / 2;
|
||||
}
|
||||
|
||||
_version = version;
|
||||
Closable = true;
|
||||
Disposable = false;
|
||||
|
||||
AddPage( 0 );
|
||||
AddBackground( 0, 0, 500, 400, 9270 );
|
||||
AddHtml( 20, 20, 460, 330, message, true, true );
|
||||
AddButton( 420, 360, 247, 248, 0, GumpButtonType.Reply, 0 );
|
||||
}
|
||||
|
||||
public override void OnResponse( int buttonID, int[] switches )
|
||||
{
|
||||
AssistantOptions.UpdateGumpVersion = _version;
|
||||
base.OnResponse( buttonID, switches );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Data.Macros.Commands;
|
||||
using ClassicAssist.UO.Gumps;
|
||||
using ClassicAssist.UO.Objects.Gumps;
|
||||
|
||||
namespace ClassicAssist.UO.Data.Gumps
|
||||
{
|
||||
public class WeaponAbilitiesGump : RepositionableGump
|
||||
{
|
||||
private readonly bool _primaryEnable;
|
||||
private readonly int _primaryId;
|
||||
private readonly bool _secondaryEnable;
|
||||
private readonly int _secondaryId;
|
||||
|
||||
public WeaponAbilitiesGump( int primaryId, bool primaryEnable, int secondaryId, bool secondaryEnable ) : base(
|
||||
90, 40, GumpSerial++, (uint) GumpSerial++ )
|
||||
{
|
||||
if ( Engine.Gumps.GetGumps( out Gump[] gumps ) )
|
||||
{
|
||||
foreach ( Gump gump in gumps )
|
||||
{
|
||||
if ( gump is WeaponAbilitiesGump )
|
||||
{
|
||||
Shared.UO.Commands.CloseClientGump( gump.ID );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GumpX = Options.CurrentOptions.AbilitiesGumpX;
|
||||
GumpY = Options.CurrentOptions.AbilitiesGumpY;
|
||||
|
||||
_primaryId = primaryId;
|
||||
_secondaryId = secondaryId;
|
||||
_primaryEnable = primaryEnable;
|
||||
_secondaryEnable = secondaryEnable;
|
||||
|
||||
Movable = false;
|
||||
Closable = false;
|
||||
AddPage( 0 );
|
||||
AddImage( 0, 0, 0x5200 + ( primaryId - 1 ), primaryEnable ? 37 : 0 );
|
||||
AddImage( 45, 0, 0x5200 + ( secondaryId - 1 ), secondaryEnable ? 37 : 0 );
|
||||
AddButton( 15, 15, 1209, 1210, 1, GumpButtonType.Reply, 0 );
|
||||
AddButton( 60, 15, 1209, 1210, 2, GumpButtonType.Reply, 0 );
|
||||
}
|
||||
|
||||
public static int GumpSerial { get; set; } = 0x0fff0000;
|
||||
|
||||
public override void OnResponse( int buttonID, int[] switches )
|
||||
{
|
||||
switch ( buttonID )
|
||||
{
|
||||
case 1:
|
||||
AbilitiesCommands.SetAbility( "primary" );
|
||||
break;
|
||||
case 2:
|
||||
AbilitiesCommands.SetAbility( "secondary" );
|
||||
break;
|
||||
}
|
||||
|
||||
base.OnResponse( buttonID, switches );
|
||||
}
|
||||
|
||||
private void ResendGump()
|
||||
{
|
||||
WeaponAbilitiesGump gump =
|
||||
new WeaponAbilitiesGump( _primaryId, _primaryEnable, _secondaryId, _secondaryEnable );
|
||||
gump.SendGump();
|
||||
}
|
||||
|
||||
public override void SetPosition( int x, int y )
|
||||
{
|
||||
base.SetPosition( x, y );
|
||||
|
||||
Options.CurrentOptions.AbilitiesGumpX = x;
|
||||
Options.CurrentOptions.AbilitiesGumpY = y;
|
||||
|
||||
ResendGump();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public static class Hues
|
||||
{
|
||||
private const int BlockCount = 375;
|
||||
private static string _dataPath;
|
||||
|
||||
public static Lazy<HueEntry[]> _lazyHueEntries = new Lazy<HueEntry[]>( LoadHueIndex );
|
||||
|
||||
public static bool Initialize( string dataPath )
|
||||
{
|
||||
_dataPath = dataPath;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static HueEntry[] LoadHueIndex()
|
||||
{
|
||||
HueEntry[] entries = new HueEntry[3000];
|
||||
|
||||
if ( !File.Exists( Path.Combine( _dataPath, "hues.mul" ) ) )
|
||||
{
|
||||
throw new FileNotFoundException( "File not found", "hues.mul" );
|
||||
}
|
||||
|
||||
using ( FileStream reader = File.Open( Path.Combine( _dataPath, "hues.mul" ), FileMode.Open,
|
||||
FileAccess.Read, FileShare.ReadWrite ) )
|
||||
{
|
||||
BinaryReader binaryReader = new BinaryReader( reader );
|
||||
int total = 0;
|
||||
|
||||
for ( int i = 0; i < BlockCount; i++ )
|
||||
{
|
||||
binaryReader.ReadInt32();
|
||||
|
||||
for ( int j = 0; j < 8; ++j, ++total )
|
||||
{
|
||||
entries[total] = HueEntry.Read( binaryReader );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
public static unsafe void ApplyHue( int hue, Bitmap bmp, bool onlyHueGrayPixels )
|
||||
{
|
||||
BitmapData bd = bmp.LockBits( new Rectangle( 0, 0, bmp.Width, bmp.Height ), ImageLockMode.ReadWrite,
|
||||
PixelFormat.Format16bppArgb1555 );
|
||||
|
||||
hue = ( hue & 0x3FFF ) - 1;
|
||||
|
||||
int stride = bd.Stride >> 1;
|
||||
int width = bd.Width;
|
||||
int height = bd.Height;
|
||||
int delta = stride - width;
|
||||
|
||||
ushort* pBuffer = (ushort*) bd.Scan0;
|
||||
ushort* pLineEnd = pBuffer + width;
|
||||
ushort* pImageEnd = pBuffer + stride * height;
|
||||
|
||||
if ( onlyHueGrayPixels )
|
||||
{
|
||||
while ( pBuffer < pImageEnd )
|
||||
{
|
||||
while ( pBuffer < pLineEnd )
|
||||
{
|
||||
int c = *pBuffer;
|
||||
|
||||
if ( c != 0 )
|
||||
{
|
||||
int r = ( c >> 10 ) & 0x1F;
|
||||
int g = ( c >> 5 ) & 0x1F;
|
||||
int b = c & 0x1F;
|
||||
|
||||
if ( r == g && r == b )
|
||||
{
|
||||
*pBuffer = (ushort) _lazyHueEntries.Value[hue].Colors[( c >> 10 ) & 0x1F];
|
||||
}
|
||||
}
|
||||
|
||||
++pBuffer;
|
||||
}
|
||||
|
||||
pBuffer += delta;
|
||||
pLineEnd += stride;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while ( pBuffer < pImageEnd )
|
||||
{
|
||||
while ( pBuffer < pLineEnd )
|
||||
{
|
||||
if ( *pBuffer != 0 )
|
||||
{
|
||||
int index = ( *pBuffer >> 10 ) & 0x1F;
|
||||
HueEntry hueEntry = _lazyHueEntries.Value[hue];
|
||||
*pBuffer = (ushort) hueEntry.Colors[index];
|
||||
}
|
||||
|
||||
++pBuffer;
|
||||
}
|
||||
|
||||
pBuffer += delta;
|
||||
pLineEnd += stride;
|
||||
}
|
||||
}
|
||||
|
||||
bmp.UnlockBits( bd );
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout( LayoutKind.Sequential, Pack = 1 )]
|
||||
public struct HueEntry
|
||||
{
|
||||
[MarshalAs( UnmanagedType.ByValArray, SizeConst = 32 )]
|
||||
public short[] Colors;
|
||||
|
||||
public short TableStart;
|
||||
public short TableEnd;
|
||||
|
||||
[MarshalAs( UnmanagedType.ByValArray, SizeConst = 20 )]
|
||||
public string Name;
|
||||
|
||||
public static HueEntry Read( BinaryReader reader )
|
||||
{
|
||||
// ReSharper disable once UseObjectOrCollectionInitializer
|
||||
HueEntry entry = new HueEntry();
|
||||
|
||||
entry.Colors = new short[32];
|
||||
|
||||
for ( int i = 0; i < 32; ++i )
|
||||
{
|
||||
entry.Colors[i] = (short) ( reader.ReadUInt16() | 0x8000 );
|
||||
}
|
||||
|
||||
entry.TableStart = (short) ( reader.ReadUInt16() | 0x8000 );
|
||||
entry.TableEnd = (short) ( reader.ReadUInt16() | 0x8000 );
|
||||
entry.Name = Encoding.ASCII.GetString( reader.ReadBytes( 20 ) ).TrimEnd( '\0' );
|
||||
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public struct LandTile
|
||||
{
|
||||
public TileFlags Flags { get; set; }
|
||||
public int ID { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public sbyte Z { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public enum Layer : byte
|
||||
{
|
||||
Invalid = 0,
|
||||
OneHanded = 1,
|
||||
TwoHanded = 2,
|
||||
Shoes = 3,
|
||||
Pants = 4,
|
||||
Shirt = 5,
|
||||
Helm = 6,
|
||||
Gloves = 7,
|
||||
Ring = 8,
|
||||
Talisman = 9,
|
||||
Neck = 10, // 0x0A
|
||||
Hair = 11, // 0x0B
|
||||
Waist = 12, // 0x0C
|
||||
InnerTorso = 13, // 0x0D
|
||||
Bracelet = 14, // 0x0E
|
||||
Unused_xF = 15, // 0x0F
|
||||
FacialHair = 16, // 0x10
|
||||
MiddleTorso = 17, // 0x11
|
||||
Earrings = 18, // 0x12
|
||||
Arms = 19, // 0x13
|
||||
Cloak = 20, // 0x14
|
||||
Backpack = 21, // 0x15
|
||||
OuterTorso = 22, // 0x16
|
||||
OuterLegs = 23, // 0x17
|
||||
InnerLegs = 24, // 0x18
|
||||
Mount = 25, // 0x19
|
||||
ShopBuy = 26, // 0x1A
|
||||
ShopResale = 27, // 0x1B
|
||||
ShopSell = 28, // 0x1C
|
||||
Bank = 29, // 0x1D
|
||||
LastValid = 29 // 0x1D
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using ClassicAssist.Shared;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public enum Map
|
||||
{
|
||||
Felucca,
|
||||
Trammel,
|
||||
Ilshenar,
|
||||
Malas,
|
||||
Tokuno,
|
||||
Ter_Mur
|
||||
}
|
||||
|
||||
public static class MapInfo
|
||||
{
|
||||
private static string _dataPath;
|
||||
|
||||
private static readonly UOPIndex[] _uopIndex = new UOPIndex[6];
|
||||
|
||||
private static int[,] _defaultMapSize { get; } =
|
||||
{
|
||||
{ 7168, 4096 }, { 7168, 4096 }, { 2304, 1600 }, { 2560, 2048 }, { 1448, 1448 }, { 1280, 4096 }
|
||||
};
|
||||
|
||||
public static void Initialize( string dataPath )
|
||||
{
|
||||
_dataPath = dataPath;
|
||||
}
|
||||
|
||||
public static LandTile GetLandTile( int map, int x, int y )
|
||||
{
|
||||
int blockIndex = x / 8 * ( _defaultMapSize[map, 1] / 8 ) + y / 8;
|
||||
int cellX = x % 8;
|
||||
int cellY = y % 8;
|
||||
string fileName = GetMapFilename( map );
|
||||
|
||||
if ( string.IsNullOrEmpty( fileName ) )
|
||||
{
|
||||
return new LandTile();
|
||||
}
|
||||
|
||||
using ( FileStream fileStream = File.OpenRead( fileName ) )
|
||||
{
|
||||
if ( fileName.EndsWith( ".uop", StringComparison.InvariantCultureIgnoreCase ) &&
|
||||
_uopIndex[map] == null )
|
||||
{
|
||||
_uopIndex[map] = new UOPIndex( File.OpenRead( fileName ) );
|
||||
}
|
||||
|
||||
int offset = blockIndex * 196 + 4 + cellY * 24 + cellX * 3;
|
||||
|
||||
if ( _uopIndex[map] != null )
|
||||
{
|
||||
offset = _uopIndex[map].Lookup( offset );
|
||||
}
|
||||
|
||||
fileStream.Seek( offset, SeekOrigin.Begin );
|
||||
|
||||
using ( BinaryReader binaryReader = new BinaryReader( fileStream ) )
|
||||
{
|
||||
int id = binaryReader.ReadUInt16();
|
||||
LandTile landTile = TileData.GetLandTile( id );
|
||||
landTile.ID = id;
|
||||
landTile.X = x;
|
||||
landTile.Y = y;
|
||||
landTile.Z = binaryReader.ReadSByte();
|
||||
|
||||
return landTile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static (int, int) GetMapSurface( int map, int x, int y )
|
||||
{
|
||||
LandTile landTile = GetLandTile( map, x, y );
|
||||
|
||||
StaticTile[] staticTiles = Statics.GetStatics( map, x, y );
|
||||
|
||||
if ( staticTiles == null || staticTiles.Length == 0 )
|
||||
{
|
||||
return ( landTile.Z, 0 );
|
||||
}
|
||||
|
||||
StaticTile staticTile = staticTiles
|
||||
.Where( i => i.Flags.HasFlag( TileFlags.Surface ) && Math.Abs( Engine.Player.Z - i.Z ) < 20 )
|
||||
.OrderByDescending( i => i.Z ).FirstOrDefault();
|
||||
|
||||
if ( staticTile.ID == 0 && landTile.Flags.HasFlag( TileFlags.Impassable ) )
|
||||
{
|
||||
staticTile = staticTiles
|
||||
.Where( i => i.Flags.HasFlag( TileFlags.Wet ) && Math.Abs( Engine.Player.Z - i.Z ) < 20 )
|
||||
.OrderByDescending( i => i.Z ).FirstOrDefault();
|
||||
}
|
||||
|
||||
if ( staticTile.ID == 0 )
|
||||
{
|
||||
return ( landTile.Z, 0 );
|
||||
}
|
||||
|
||||
return ( staticTile.Z + staticTile.Height, staticTile.ID );
|
||||
}
|
||||
|
||||
private static string GetMapFilename( int map )
|
||||
{
|
||||
if ( string.IsNullOrEmpty( _dataPath ) )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string uopFilename = Path.Combine( _dataPath, $"map{map}LegacyMUL.uop" );
|
||||
|
||||
if ( File.Exists( uopFilename ) )
|
||||
{
|
||||
return uopFilename;
|
||||
}
|
||||
|
||||
string mulFilename = Path.Combine( _dataPath, $"map{map}.mul" );
|
||||
|
||||
if ( File.Exists( mulFilename ) )
|
||||
{
|
||||
return mulFilename;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace ClassicAssist.UO.Network.PacketFilter
|
||||
{
|
||||
public enum PacketDirection : byte
|
||||
{
|
||||
Incoming,
|
||||
Outgoing,
|
||||
Any
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public class PacketReader
|
||||
{
|
||||
private readonly MemoryStream _stream;
|
||||
|
||||
public PacketReader( byte[] data, int size, bool fixedSize )
|
||||
{
|
||||
_stream = new MemoryStream( data, 0, size, false );
|
||||
|
||||
_stream.Seek( fixedSize ? 1 : 3, SeekOrigin.Current );
|
||||
}
|
||||
|
||||
public long Index => _stream.Position;
|
||||
public long Size => _stream.Length;
|
||||
|
||||
public byte[] GetData()
|
||||
{
|
||||
return _stream.ToArray();
|
||||
}
|
||||
|
||||
public static bool IsSafeChar( int c )
|
||||
{
|
||||
return c >= 0x20 && c < 0xFFFE;
|
||||
}
|
||||
|
||||
public bool ReadBoolean()
|
||||
{
|
||||
return _stream.ReadByte() != 0;
|
||||
}
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
return (byte) _stream.ReadByte();
|
||||
}
|
||||
|
||||
public byte[] ReadByteArray( int length )
|
||||
{
|
||||
byte[] bytes = new byte[length];
|
||||
|
||||
for ( int i = 0; i < length; i++ )
|
||||
{
|
||||
bytes[i] = ReadByte();
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public short ReadInt16()
|
||||
{
|
||||
byte[] buffer = new byte[2];
|
||||
_stream.Read( buffer, 0, 2 );
|
||||
|
||||
return (short) ( ( buffer[0] << 8 ) | buffer[1] );
|
||||
}
|
||||
|
||||
public int ReadInt32()
|
||||
{
|
||||
byte[] buffer = new byte[4];
|
||||
_stream.Read( buffer, 0, 4 );
|
||||
|
||||
return ( buffer[0] << 24 ) | ( buffer[1] << 16 ) | ( buffer[2] << 8 ) | buffer[3];
|
||||
}
|
||||
|
||||
public sbyte ReadSByte()
|
||||
{
|
||||
return (sbyte) ReadByte();
|
||||
}
|
||||
|
||||
public string ReadString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while ( Index + 1 < Size && ( c = ReadByte() ) != 0 )
|
||||
{
|
||||
sb.Append( (char) c );
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadString( int fixedLength )
|
||||
{
|
||||
byte[] buffer = new byte[fixedLength];
|
||||
|
||||
_stream.Read( buffer, 0, fixedLength );
|
||||
|
||||
return Encoding.ASCII.GetString( buffer ).TrimEnd( '\0' );
|
||||
}
|
||||
|
||||
public string ReadStringSafe()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string ReadStringSafe( int fixedLength )
|
||||
{
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
for ( int i = 0; i < fixedLength; i++ )
|
||||
{
|
||||
char c = (char) ReadByte();
|
||||
|
||||
if ( c == '\0' )
|
||||
{
|
||||
ReadByteArray( fixedLength - i - 1 );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( IsSafeChar( c ) )
|
||||
{
|
||||
output.Append( c );
|
||||
}
|
||||
}
|
||||
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
public ushort ReadUInt16()
|
||||
{
|
||||
return (ushort) ReadInt16();
|
||||
}
|
||||
|
||||
public uint ReadUInt32()
|
||||
{
|
||||
return (uint) ReadInt32();
|
||||
}
|
||||
|
||||
public string ReadUnicodeString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while ( Index + 1 < Size && ( c = ( ReadByte() << 8 ) | ReadByte() ) != 0 )
|
||||
{
|
||||
sb.Append( (char) c );
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadUnicodeString( int fixedLength )
|
||||
{
|
||||
byte[] buffer = new byte[fixedLength];
|
||||
|
||||
_stream.Read( buffer, 0, fixedLength );
|
||||
|
||||
return Encoding.Unicode.GetString( buffer );
|
||||
}
|
||||
|
||||
public string ReadUnicodeStringLE()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int c;
|
||||
|
||||
while ( Index + 1 < Size && ( c = ReadByte() | ( ReadByte() << 8 ) ) != 0 )
|
||||
{
|
||||
sb.Append( (char) c );
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public string ReadUnicodeStringLESafe( int fixedLength )
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string ReadUnicodeStringLESafe()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string ReadUnicodeStringSafe()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public string ReadUnicodeStringSafe( int fixedLength )
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public long Seek( int offset, SeekOrigin origin )
|
||||
{
|
||||
return _stream.Seek( offset, origin );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
/***************************************************************************
|
||||
* PacketWriter.cs
|
||||
* -------------------
|
||||
* begin : May 1, 2002
|
||||
* copyright : (C) The RunUO Software Team
|
||||
* email : info@runuo.com
|
||||
*
|
||||
* $Id$
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality for writing primitive binary data.
|
||||
/// </summary>
|
||||
public class PacketWriter : IDisposable
|
||||
{
|
||||
private static readonly Stack<PacketWriter> Pool = new Stack<PacketWriter>();
|
||||
|
||||
/// <summary>
|
||||
/// Internal format buffer.
|
||||
/// </summary>
|
||||
private readonly byte[] _buffer = new byte[4];
|
||||
|
||||
/// <summary>
|
||||
/// Internal stream which holds the entire packet.
|
||||
/// </summary>
|
||||
private readonly MemoryStream _stream;
|
||||
|
||||
private int m_Capacity;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new PacketWriter instance with the default capacity of 4 bytes.
|
||||
/// </summary>
|
||||
public PacketWriter() : this( 32 )
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new PacketWriter instance with a given capacity.
|
||||
/// </summary>
|
||||
/// <param name="capacity">Initial capacity for the internal stream.</param>
|
||||
public PacketWriter( int capacity )
|
||||
{
|
||||
_stream = new MemoryStream( capacity );
|
||||
m_Capacity = capacity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total stream length.
|
||||
/// </summary>
|
||||
public long Length => _stream.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the current stream position.
|
||||
/// </summary>
|
||||
public long Position
|
||||
{
|
||||
get => _stream.Position;
|
||||
set => _stream.Position = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The internal stream used by this PacketWriter instance.
|
||||
/// </summary>
|
||||
public MemoryStream UnderlyingStream => _stream;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose( true );
|
||||
GC.SuppressFinalize( this );
|
||||
}
|
||||
|
||||
public static PacketWriter CreateInstance()
|
||||
{
|
||||
return CreateInstance( 32 );
|
||||
}
|
||||
|
||||
public static PacketWriter CreateInstance( int capacity )
|
||||
{
|
||||
PacketWriter pw = null;
|
||||
|
||||
lock ( Pool )
|
||||
{
|
||||
if ( Pool.Count > 0 )
|
||||
{
|
||||
pw = Pool.Pop();
|
||||
|
||||
if ( pw != null )
|
||||
{
|
||||
pw.m_Capacity = capacity;
|
||||
pw._stream.SetLength( 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pw ?? new PacketWriter( capacity );
|
||||
}
|
||||
|
||||
public static void ReleaseInstance( PacketWriter pw )
|
||||
{
|
||||
lock ( Pool )
|
||||
{
|
||||
if ( !Pool.Contains( pw ) )
|
||||
{
|
||||
Pool.Push( pw );
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
using ( StreamWriter op = new StreamWriter( "neterr.log" ) )
|
||||
{
|
||||
op.WriteLine( "{0}\tInstance pool contains writer", DateTime.UtcNow );
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine( @"net error" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 1-byte boolean value to the underlying stream. False is represented by 0, true by 1.
|
||||
/// </summary>
|
||||
public void Write( bool value )
|
||||
{
|
||||
_stream.WriteByte( (byte) ( value ? 1 : 0 ) );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 1-byte unsigned integer value to the underlying stream.
|
||||
/// </summary>
|
||||
public void Write( byte value )
|
||||
{
|
||||
_stream.WriteByte( value );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 1-byte signed integer value to the underlying stream.
|
||||
/// </summary>
|
||||
public void Write( sbyte value )
|
||||
{
|
||||
_stream.WriteByte( (byte) value );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 2-byte signed integer value to the underlying stream.
|
||||
/// </summary>
|
||||
public void Write( short value )
|
||||
{
|
||||
_buffer[0] = (byte) ( value >> 8 );
|
||||
_buffer[1] = (byte) value;
|
||||
|
||||
_stream.Write( _buffer, 0, 2 );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 2-byte unsigned integer value to the underlying stream.
|
||||
/// </summary>
|
||||
public void Write( ushort value )
|
||||
{
|
||||
_buffer[0] = (byte) ( value >> 8 );
|
||||
_buffer[1] = (byte) value;
|
||||
|
||||
_stream.Write( _buffer, 0, 2 );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 4-byte signed integer value to the underlying stream.
|
||||
/// </summary>
|
||||
public void Write( int value )
|
||||
{
|
||||
_buffer[0] = (byte) ( value >> 24 );
|
||||
_buffer[1] = (byte) ( value >> 16 );
|
||||
_buffer[2] = (byte) ( value >> 8 );
|
||||
_buffer[3] = (byte) value;
|
||||
|
||||
_stream.Write( _buffer, 0, 4 );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a 4-byte unsigned integer value to the underlying stream.
|
||||
/// </summary>
|
||||
public void Write( uint value )
|
||||
{
|
||||
_buffer[0] = (byte) ( value >> 24 );
|
||||
_buffer[1] = (byte) ( value >> 16 );
|
||||
_buffer[2] = (byte) ( value >> 8 );
|
||||
_buffer[3] = (byte) value;
|
||||
|
||||
_stream.Write( _buffer, 0, 4 );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a sequence of bytes to the underlying stream
|
||||
/// </summary>
|
||||
public void Write( byte[] buffer, int offset, int size )
|
||||
{
|
||||
_stream.Write( buffer, offset, size );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a fixed-length ASCII-encoded string value to the underlying stream. To fit (size), the string content is
|
||||
/// either truncated or padded with null characters.
|
||||
/// </summary>
|
||||
public void WriteAsciiFixed( string value, int size )
|
||||
{
|
||||
if ( value == null )
|
||||
{
|
||||
Console.WriteLine( @"Network: Attempted to WriteAsciiFixed() with null value" );
|
||||
value = string.Empty;
|
||||
}
|
||||
|
||||
int length = value.Length;
|
||||
|
||||
_stream.SetLength( _stream.Length + size );
|
||||
|
||||
if ( length >= size )
|
||||
{
|
||||
_stream.Position +=
|
||||
Encoding.ASCII.GetBytes( value, 0, size, _stream.GetBuffer(), (int) _stream.Position );
|
||||
}
|
||||
else
|
||||
{
|
||||
Encoding.ASCII.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
|
||||
_stream.Position += size;
|
||||
}
|
||||
|
||||
/*byte[] buffer = Encoding.ASCII.GetBytes( value );
|
||||
|
||||
if ( buffer.Length >= size )
|
||||
{
|
||||
m_Stream.Write( buffer, 0, size );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Stream.Write( buffer, 0, buffer.Length );
|
||||
Fill( size - buffer.Length );
|
||||
}*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a dynamic-length ASCII-encoded string value to the underlying stream, followed by a 1-byte null character.
|
||||
/// </summary>
|
||||
public void WriteAsciiNull( string value )
|
||||
{
|
||||
if ( value == null )
|
||||
{
|
||||
Console.WriteLine( @"Network: Attempted to WriteAsciiNull() with null value" );
|
||||
value = string.Empty;
|
||||
}
|
||||
|
||||
int length = value.Length;
|
||||
|
||||
_stream.SetLength( _stream.Length + length + 1 );
|
||||
|
||||
Encoding.ASCII.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
|
||||
_stream.Position += length + 1;
|
||||
|
||||
/*byte[] buffer = Encoding.ASCII.GetBytes( value );
|
||||
|
||||
m_Stream.Write( buffer, 0, buffer.Length );
|
||||
m_Stream.WriteByte( 0 );*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a dynamic-length little-endian unicode string value to the underlying stream, followed by a 2-byte null
|
||||
/// character.
|
||||
/// </summary>
|
||||
public void WriteLittleUniNull( string value )
|
||||
{
|
||||
if ( value == null )
|
||||
{
|
||||
Console.WriteLine( @"Network: Attempted to WriteLittleUniNull() with null value" );
|
||||
value = string.Empty;
|
||||
}
|
||||
|
||||
int length = value.Length;
|
||||
|
||||
_stream.SetLength( _stream.Length + ( length + 1 ) * 2 );
|
||||
|
||||
_stream.Position +=
|
||||
Encoding.Unicode.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
|
||||
_stream.Position += 2;
|
||||
|
||||
/*byte[] buffer = Encoding.Unicode.GetBytes( value );
|
||||
|
||||
m_Stream.Write( buffer, 0, buffer.Length );
|
||||
|
||||
m_Buffer[0] = 0;
|
||||
m_Buffer[1] = 0;
|
||||
m_Stream.Write( m_Buffer, 0, 2 );*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a fixed-length little-endian unicode string value to the underlying stream. To fit (size), the string
|
||||
/// content is either truncated or padded with null characters.
|
||||
/// </summary>
|
||||
public void WriteLittleUniFixed( string value, int size )
|
||||
{
|
||||
if ( value == null )
|
||||
{
|
||||
Console.WriteLine( @"Network: Attempted to WriteLittleUniFixed() with null value" );
|
||||
value = string.Empty;
|
||||
}
|
||||
|
||||
size *= 2;
|
||||
|
||||
int length = value.Length;
|
||||
|
||||
_stream.SetLength( _stream.Length + size );
|
||||
|
||||
if ( length * 2 >= size )
|
||||
{
|
||||
_stream.Position +=
|
||||
Encoding.Unicode.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
|
||||
}
|
||||
else
|
||||
{
|
||||
Encoding.Unicode.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
|
||||
_stream.Position += size;
|
||||
}
|
||||
|
||||
/*size *= 2;
|
||||
|
||||
byte[] buffer = Encoding.Unicode.GetBytes( value );
|
||||
|
||||
if ( buffer.Length >= size )
|
||||
{
|
||||
m_Stream.Write( buffer, 0, size );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Stream.Write( buffer, 0, buffer.Length );
|
||||
Fill( size - buffer.Length );
|
||||
}*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a dynamic-length big-endian unicode string value to the underlying stream, followed by a 2-byte null
|
||||
/// character.
|
||||
/// </summary>
|
||||
public void WriteBigUniNull( string value )
|
||||
{
|
||||
if ( value == null )
|
||||
{
|
||||
Console.WriteLine( @"Network: Attempted to WriteBigUniNull() with null value" );
|
||||
value = string.Empty;
|
||||
}
|
||||
|
||||
int length = value.Length;
|
||||
|
||||
_stream.SetLength( _stream.Length + ( length + 1 ) * 2 );
|
||||
|
||||
_stream.Position +=
|
||||
Encoding.BigEndianUnicode.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
|
||||
_stream.Position += 2;
|
||||
|
||||
/*byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value );
|
||||
|
||||
m_Stream.Write( buffer, 0, buffer.Length );
|
||||
|
||||
m_Buffer[0] = 0;
|
||||
m_Buffer[1] = 0;
|
||||
m_Stream.Write( m_Buffer, 0, 2 );*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a fixed-length big-endian unicode string value to the underlying stream. To fit (size), the string content
|
||||
/// is either truncated or padded with null characters.
|
||||
/// </summary>
|
||||
public void WriteBigUniFixed( string value, int size )
|
||||
{
|
||||
if ( value == null )
|
||||
{
|
||||
Console.WriteLine( @"Network: Attempted to WriteBigUniFixed() with null value" );
|
||||
value = string.Empty;
|
||||
}
|
||||
|
||||
size *= 2;
|
||||
|
||||
int length = value.Length;
|
||||
|
||||
_stream.SetLength( _stream.Length + size );
|
||||
|
||||
if ( length * 2 >= size )
|
||||
{
|
||||
_stream.Position +=
|
||||
Encoding.BigEndianUnicode.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
|
||||
}
|
||||
else
|
||||
{
|
||||
Encoding.BigEndianUnicode.GetBytes( value, 0, length, _stream.GetBuffer(), (int) _stream.Position );
|
||||
_stream.Position += size;
|
||||
}
|
||||
|
||||
/*size *= 2;
|
||||
|
||||
byte[] buffer = Encoding.BigEndianUnicode.GetBytes( value );
|
||||
|
||||
if ( buffer.Length >= size )
|
||||
{
|
||||
m_Stream.Write( buffer, 0, size );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Stream.Write( buffer, 0, buffer.Length );
|
||||
Fill( size - buffer.Length );
|
||||
}*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills the stream from the current position up to (capacity) with 0x00's
|
||||
/// </summary>
|
||||
public void Fill()
|
||||
{
|
||||
Fill( (int) ( m_Capacity - _stream.Length ) );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a number of 0x00 byte values to the underlying stream.
|
||||
/// </summary>
|
||||
public void Fill( int length )
|
||||
{
|
||||
if ( _stream.Position == _stream.Length )
|
||||
{
|
||||
_stream.SetLength( _stream.Length + length );
|
||||
_stream.Seek( 0, SeekOrigin.End );
|
||||
}
|
||||
else
|
||||
{
|
||||
_stream.Write( new byte[length], 0, length );
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Offsets the current position from an origin.
|
||||
/// </summary>
|
||||
public long Seek( long offset, SeekOrigin origin )
|
||||
{
|
||||
return _stream.Seek( offset, origin );
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entire stream content as a byte array.
|
||||
/// </summary>
|
||||
public byte[] ToArray()
|
||||
{
|
||||
return _stream.ToArray();
|
||||
}
|
||||
|
||||
protected virtual void Dispose( bool isDisposing )
|
||||
{
|
||||
if ( isDisposing )
|
||||
{
|
||||
_stream.Dispose();
|
||||
}
|
||||
|
||||
GC.SuppressFinalize( this );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public class PlayerStatus
|
||||
{
|
||||
public int ColdResist { get; set; }
|
||||
public int DamageIncrease { get; set; }
|
||||
public int DefenseChanceIncrease { get; set; }
|
||||
public int Dex { get; set; }
|
||||
public int EnergyResist { get; set; }
|
||||
public int FasterCasting { get; set; }
|
||||
public int FasterCastRecovery { get; set; }
|
||||
public int Features { get; set; }
|
||||
public int FireResist { get; set; }
|
||||
public int Followers { get; set; }
|
||||
public int Gold { get; set; }
|
||||
public int Health { get; set; }
|
||||
public int HitChanceIncrease { get; set; }
|
||||
public int Int { get; set; }
|
||||
public int LowerManaCost { get; set; }
|
||||
public int LowerReagentCost { get; set; }
|
||||
public int Luck { get; set; }
|
||||
public int Mana { get; set; }
|
||||
public int MaxColdResist { get; set; }
|
||||
public int MaxDamage { get; set; }
|
||||
public int MaxEnergyResist { get; set; }
|
||||
public int MaxFireResist { get; set; }
|
||||
public int MaxFollowers { get; set; }
|
||||
public int MaxHealth { get; set; }
|
||||
public int MaxMana { get; set; }
|
||||
public int MaxPhysicalResist { get; set; }
|
||||
public int MaxPoisonResist { get; set; }
|
||||
public int MaxStamina { get; set; }
|
||||
public int MaxWeight { get; set; }
|
||||
public int MinDamage { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int PhysicalResist { get; set; }
|
||||
public int PoisonResist { get; set; }
|
||||
public int Race { get; set; }
|
||||
public int Sex { get; set; }
|
||||
public int SpellDamageIncrease { get; set; }
|
||||
public int Stamina { get; set; }
|
||||
public int StatCap { get; set; }
|
||||
public int Str { get; set; }
|
||||
public int SwingSpeedIncrease { get; set; }
|
||||
public int TithingPoints { get; set; }
|
||||
public int Weight { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.UO.Network.Packets;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public enum QuestPointerType
|
||||
{
|
||||
Resurrection,
|
||||
Corpse,
|
||||
Other
|
||||
}
|
||||
|
||||
public class QuestPointerList : IEnumerable<QuestPointer>
|
||||
{
|
||||
private readonly object _lock = new object();
|
||||
private readonly List<QuestPointer> _questPointers = new List<QuestPointer>();
|
||||
|
||||
public IEnumerator<QuestPointer> GetEnumerator()
|
||||
{
|
||||
return _questPointers.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add( QuestPointer questPointer )
|
||||
{
|
||||
lock ( _lock )
|
||||
{
|
||||
_questPointers.Add( questPointer );
|
||||
}
|
||||
|
||||
Engine.SendPacketToClient( new DisplayQuestPointer( true, questPointer.X, questPointer.Y,
|
||||
questPointer.Serial ) );
|
||||
}
|
||||
|
||||
public void Remove( QuestPointer questPointer )
|
||||
{
|
||||
lock ( _lock )
|
||||
{
|
||||
_questPointers.Remove( questPointer );
|
||||
}
|
||||
|
||||
Engine.SendPacketToClient( new DisplayQuestPointer( false, questPointer.X, questPointer.Y,
|
||||
questPointer.Serial ) );
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Remove( _questPointers );
|
||||
}
|
||||
|
||||
public void Remove( IEnumerable<QuestPointer> questPointers )
|
||||
{
|
||||
foreach ( QuestPointer questPointer in questPointers.ToArray() )
|
||||
{
|
||||
Remove( questPointer );
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveByType( QuestPointerType type )
|
||||
{
|
||||
IEnumerable<QuestPointer> pointers = _questPointers.Where( p => p.Type == type );
|
||||
|
||||
Remove( pointers );
|
||||
}
|
||||
}
|
||||
|
||||
public class QuestPointer
|
||||
{
|
||||
public int Serial { get; set; }
|
||||
public QuestPointerType Type { get; set; }
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ClassicAssist.Shared;
|
||||
using ClassicAssist.Data;
|
||||
using ClassicAssist.Shared.UO;
|
||||
using ClassicAssist.UO.Network.Packets;
|
||||
using ClassicAssist.UO.Objects;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public enum RehueType
|
||||
{
|
||||
Custom,
|
||||
Friends,
|
||||
Enemies
|
||||
}
|
||||
|
||||
public class RehueEntry
|
||||
{
|
||||
public int Hue { get; set; }
|
||||
public int Serial { get; set; }
|
||||
public RehueType Type { get; set; }
|
||||
}
|
||||
|
||||
public class RehueList
|
||||
{
|
||||
private readonly ConcurrentDictionary<int, RehueEntry> _rehueList = new ConcurrentDictionary<int, RehueEntry>();
|
||||
|
||||
public void Add( int serial, int hue, RehueType type = RehueType.Custom )
|
||||
{
|
||||
RehueEntry entry = new RehueEntry { Serial = serial, Hue = hue, Type = type };
|
||||
|
||||
_rehueList.AddOrUpdate( serial, i => entry, ( i, rehueEntry ) => entry );
|
||||
}
|
||||
|
||||
public bool Remove( int serial )
|
||||
{
|
||||
return _rehueList.TryRemove( serial, out _ );
|
||||
}
|
||||
|
||||
public bool Contains( int serial )
|
||||
{
|
||||
return _rehueList.ContainsKey( serial );
|
||||
}
|
||||
|
||||
public void RemoveByType( RehueType type )
|
||||
{
|
||||
IEnumerable<int> keys = _rehueList.Where( kvp => kvp.Value.Type == type ).Select( kvp => kvp.Key );
|
||||
|
||||
foreach ( int key in keys )
|
||||
{
|
||||
Remove( key );
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckItem( Item item )
|
||||
{
|
||||
if ( !_rehueList.TryGetValue( item.Serial, out RehueEntry entry ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( item.Owner != 0 && !UOMath.IsMobile( item.Serial ) )
|
||||
{
|
||||
Engine.SendPacketToClient( new ContainerContentUpdate( item.Serial, item.ID, item.Direction, item.Count,
|
||||
item.X, item.Y, item.Grid, item.Owner, entry.Hue ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
Commands.Resync();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckSAWorldItem( ref byte[] packet, ref int length )
|
||||
{
|
||||
int serial = ( packet[4] << 24 ) | ( packet[5] << 16 ) | ( packet[6] << 8 ) | packet[7];
|
||||
|
||||
if ( !_rehueList.TryGetValue( serial, out RehueEntry entry ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Engine.SendPacketToClient( new SAWorldItem( packet, length, entry.Hue ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CheckMobileIncoming( Mobile mobile, ItemCollection equipment )
|
||||
{
|
||||
bool result = _rehueList.TryGetValue( mobile.Serial, out RehueEntry entry );
|
||||
|
||||
if ( result )
|
||||
{
|
||||
Engine.SendPacketToClient( new MobileIncoming( mobile, equipment, entry.Hue ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( Options.CurrentOptions.RehueFriends &&
|
||||
Options.CurrentOptions.Friends.Any( e => e.Serial == mobile.Serial ) )
|
||||
{
|
||||
Engine.SendPacketToClient( new MobileIncoming( mobile, equipment,
|
||||
Options.CurrentOptions.RehueFriendsHue ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CheckMobileUpdate( Mobile mobile )
|
||||
{
|
||||
bool result = _rehueList.TryGetValue( mobile.Serial, out RehueEntry entry );
|
||||
|
||||
if ( result )
|
||||
{
|
||||
Engine.SendPacketToClient( new MobileUpdate( mobile.Serial, mobile.ID,
|
||||
entry.Hue > 0 ? entry.Hue : mobile.Hue, mobile.Status, mobile.X, mobile.Y, mobile.Z,
|
||||
mobile.Direction ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !Options.CurrentOptions.RehueFriends ||
|
||||
Options.CurrentOptions.Friends.All( e => e.Serial != mobile.Serial ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Engine.SendPacketToClient( new MobileUpdate( mobile.Serial, mobile.ID,
|
||||
Options.CurrentOptions.RehueFriendsHue, mobile.Status, mobile.X, mobile.Y, mobile.Z,
|
||||
mobile.Direction ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CheckContainer( ItemCollection collection )
|
||||
{
|
||||
int backpack = Engine.Player?.Backpack?.Serial ?? 0;
|
||||
|
||||
foreach ( Item item in collection.GetItems() )
|
||||
{
|
||||
if ( item.IsDescendantOf( backpack ) )
|
||||
{
|
||||
CheckItem( item );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool CheckMobileMoving( Mobile mobile )
|
||||
{
|
||||
bool result = _rehueList.TryGetValue( mobile.Serial, out RehueEntry entry );
|
||||
|
||||
if ( result )
|
||||
{
|
||||
Engine.SendPacketToClient( new MobileMoving( mobile, entry.Hue ) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !Options.CurrentOptions.RehueFriends ||
|
||||
Options.CurrentOptions.Friends.All( e => e.Serial != mobile.Serial ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Engine.SendPacketToClient( new MobileMoving( mobile, Options.CurrentOptions.RehueFriendsHue ) );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public class ShardEntry
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public int IP { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int PercentFull { get; set; }
|
||||
public int Timezone { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public enum LockStatus : byte
|
||||
{
|
||||
Up,
|
||||
Down,
|
||||
Locked
|
||||
}
|
||||
|
||||
public enum StatType : byte
|
||||
{
|
||||
Str,
|
||||
Dex,
|
||||
Int
|
||||
}
|
||||
|
||||
public class SkillData
|
||||
{
|
||||
public bool Invokable { get; internal set; }
|
||||
public string Name { get; internal set; }
|
||||
}
|
||||
|
||||
public static class Skills
|
||||
{
|
||||
private static Lazy<Dictionary<int, SkillData>> _lazySkillData;
|
||||
private static string _dataPath;
|
||||
|
||||
internal static void Initialize( string dataPath )
|
||||
{
|
||||
_dataPath = dataPath;
|
||||
_lazySkillData = new Lazy<Dictionary<int, SkillData>>( LoadSkills );
|
||||
}
|
||||
|
||||
public static string GetSkillName( int skillID )
|
||||
{
|
||||
return _lazySkillData.Value.ContainsKey( skillID ) ? _lazySkillData.Value[skillID].Name : string.Empty;
|
||||
}
|
||||
|
||||
public static bool IsInvokable( int skillID )
|
||||
{
|
||||
if ( skillID >= 0 && skillID <= _lazySkillData.Value.Count - 1 )
|
||||
{
|
||||
return _lazySkillData.Value[skillID].Invokable;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get string array of all skill names for UO installation defined in options.
|
||||
/// Array returned will be indexed by skill ID.
|
||||
/// </summary>
|
||||
public static string[] GetSkillNames()
|
||||
{
|
||||
string[] skillNames = (string[]) _lazySkillData.Value.Values.Select( s => s.Name );
|
||||
|
||||
return skillNames;
|
||||
}
|
||||
|
||||
public static SkillData[] GetSkillsArray()
|
||||
{
|
||||
return _lazySkillData?.Value.Values.ToArray();
|
||||
}
|
||||
|
||||
internal static Dictionary<int, SkillData> GetSkills()
|
||||
{
|
||||
return _lazySkillData.Value;
|
||||
}
|
||||
|
||||
internal static Dictionary<int, SkillData> LoadSkills()
|
||||
{
|
||||
string skillIndexFile = Path.Combine( _dataPath, "Skills.idx" );
|
||||
string skillMulFile = Path.Combine( _dataPath, "skills.mul" );
|
||||
Dictionary<int, SkillData> skills = new Dictionary<int, SkillData>();
|
||||
|
||||
if ( !File.Exists( skillIndexFile ) )
|
||||
{
|
||||
throw new FileNotFoundException( "File not found!", skillIndexFile );
|
||||
}
|
||||
|
||||
if ( !File.Exists( skillMulFile ) )
|
||||
{
|
||||
throw new FileNotFoundException( "File not found!", skillMulFile );
|
||||
}
|
||||
|
||||
byte[] indexBytes = File.ReadAllBytes( skillIndexFile );
|
||||
byte[] mulBytes = File.ReadAllBytes( skillMulFile );
|
||||
|
||||
for ( int x = 0; x < indexBytes.Length / 12; x++ )
|
||||
{
|
||||
int offset = x * 12;
|
||||
int start = BitConverter.ToInt32( indexBytes, offset );
|
||||
int length = BitConverter.ToInt32( indexBytes, offset + 4 );
|
||||
|
||||
if ( length == 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
skills.Add( x,
|
||||
new SkillData
|
||||
{
|
||||
Invokable = mulBytes[start] == 1,
|
||||
Name = Encoding.ASCII.GetString( mulBytes, start + 1, length - 2 )
|
||||
} );
|
||||
}
|
||||
|
||||
return skills;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public static class Speech
|
||||
{
|
||||
private static readonly Lazy<SpeechEntry[]> _entries = new Lazy<SpeechEntry[]>( LoadEntries );
|
||||
|
||||
private static string _dataPath;
|
||||
|
||||
public static int[] GetKeywords( string input )
|
||||
{
|
||||
IEnumerable<SpeechEntry> matches =
|
||||
_entries.Value.Where( e => Regex.IsMatch( input, WildCardToRegular( e.Keywords ) ) );
|
||||
|
||||
return matches.Select( e => e.Id ).Distinct().ToArray();
|
||||
}
|
||||
|
||||
public static void Initialize( string dataPath )
|
||||
{
|
||||
_dataPath = dataPath;
|
||||
}
|
||||
|
||||
private static SpeechEntry[] LoadEntries()
|
||||
{
|
||||
string fullPath = Path.Combine( _dataPath, "speech.mul" );
|
||||
|
||||
using ( FileStream reader =
|
||||
new FileStream( fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ) )
|
||||
{
|
||||
using ( BinaryReader binaryReader = new BinaryReader( reader ) )
|
||||
{
|
||||
List<SpeechEntry> entries = new List<SpeechEntry>();
|
||||
|
||||
while ( reader.Position < reader.Length )
|
||||
{
|
||||
int id = ( binaryReader.ReadByte() << 8 ) | binaryReader.ReadByte();
|
||||
int length = ( binaryReader.ReadByte() << 8 ) | binaryReader.ReadByte();
|
||||
|
||||
byte[] buffer = new byte[length];
|
||||
|
||||
binaryReader.Read( buffer, 0, length );
|
||||
|
||||
string text = Encoding.UTF8.GetString( buffer );
|
||||
|
||||
if ( text.Contains( "guards" ) )
|
||||
{
|
||||
}
|
||||
|
||||
entries.Add( new SpeechEntry { Id = id, Keywords = text } );
|
||||
}
|
||||
|
||||
return entries.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string WildCardToRegular( string value )
|
||||
{
|
||||
return "^" + Regex.Escape( value ).Replace( "\\*", ".*" ) + "$";
|
||||
}
|
||||
|
||||
internal struct SpeechEntry
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Keywords { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public struct StaticTile
|
||||
{
|
||||
public TileFlags Flags { get; set; }
|
||||
public byte Weight { get; set; }
|
||||
public byte Quality { get; set; }
|
||||
public byte Quantity { get; set; }
|
||||
public ushort ID { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public int Z { get; set; }
|
||||
public int Hue { get; set; }
|
||||
public int Height { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
#region License
|
||||
|
||||
// Copyright (C) 2020 Reetus
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public class Statics
|
||||
{
|
||||
private static string _dataPath;
|
||||
|
||||
private static readonly Lazy<StaticRecord[][]>[] _staticData = new Lazy<StaticRecord[][]>[6];
|
||||
|
||||
private static int[,] _defaultMapSize { get; } =
|
||||
{
|
||||
{ 7168, 4096 }, { 7168, 4096 }, { 2304, 1600 }, { 2560, 2048 }, { 1448, 1448 }, { 1280, 4096 }
|
||||
};
|
||||
|
||||
internal static void Initialize( string dataPath )
|
||||
{
|
||||
_dataPath = dataPath;
|
||||
|
||||
for ( int i = 0; i < 6; i++ )
|
||||
{
|
||||
int map = i;
|
||||
_staticData[i] = new Lazy<StaticRecord[][]>( () => LoadStatics( map ) );
|
||||
}
|
||||
}
|
||||
|
||||
private static StaticRecord[][] LoadStatics( int map )
|
||||
{
|
||||
string staticIndexFile = Path.Combine( _dataPath, $"staidx{map}.mul" );
|
||||
string staticMulFile = Path.Combine( _dataPath, $"statics{map}.mul" );
|
||||
|
||||
if ( !File.Exists( staticIndexFile ) )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( !File.Exists( staticMulFile ) )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] indexBytes = File.ReadAllBytes( staticIndexFile );
|
||||
|
||||
StaticRecord[][] staticItems = new StaticRecord[indexBytes.Length / 12][];
|
||||
|
||||
using ( FileStream fileStream =
|
||||
new FileStream( staticMulFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ) )
|
||||
{
|
||||
using ( BinaryReader binaryReader = new BinaryReader( fileStream ) )
|
||||
{
|
||||
for ( int x = 0; x < indexBytes.Length / 12; x++ )
|
||||
{
|
||||
int offset = x * 12;
|
||||
int start = BitConverter.ToInt32( indexBytes, offset );
|
||||
int length = BitConverter.ToInt32( indexBytes, offset + 4 );
|
||||
|
||||
if ( start == -1 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fileStream.Seek( start, SeekOrigin.Begin );
|
||||
|
||||
staticItems[x] = new StaticRecord[length / 7];
|
||||
|
||||
for ( int y = 0; y < length / 7; y++ )
|
||||
{
|
||||
staticItems[x][y].ID = binaryReader.ReadUInt16();
|
||||
staticItems[x][y].X = binaryReader.ReadByte();
|
||||
staticItems[x][y].Y = binaryReader.ReadByte();
|
||||
staticItems[x][y].Z = binaryReader.ReadSByte();
|
||||
staticItems[x][y].Hue = binaryReader.ReadUInt16();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return staticItems;
|
||||
}
|
||||
|
||||
public static StaticTile[] GetStatics( int map, int x, int y )
|
||||
{
|
||||
if ( _staticData?[map]?.Value == null )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int blockIndex = x / 8 * ( _defaultMapSize[map, 1] / 8 ) + y / 8;
|
||||
|
||||
int cellX = x % 8;
|
||||
int cellY = y % 8;
|
||||
|
||||
StaticRecord[] blockStatics = _staticData[map].Value[blockIndex];
|
||||
|
||||
if ( blockStatics == null )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
IEnumerable<StaticRecord> statics = blockStatics.Where( i => i.X == cellX && i.Y == cellY );
|
||||
|
||||
List<StaticTile> tiles = new List<StaticTile>();
|
||||
|
||||
foreach ( StaticRecord staticRecord in statics )
|
||||
{
|
||||
StaticTile tile = TileData.GetStaticTile( staticRecord.ID );
|
||||
tile.X = x;
|
||||
tile.Y = y;
|
||||
tile.Z = staticRecord.Z;
|
||||
tile.Hue = staticRecord.Hue;
|
||||
|
||||
tiles.Add( tile );
|
||||
}
|
||||
|
||||
return tiles.ToArray();
|
||||
}
|
||||
|
||||
private struct StaticRecord
|
||||
{
|
||||
public ushort ID { get; set; }
|
||||
public byte X { get; set; }
|
||||
public byte Y { get; set; }
|
||||
public sbyte Z { get; set; }
|
||||
public int Hue { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public static class TileData
|
||||
{
|
||||
private static string _dataPath;
|
||||
private static readonly Lazy<LandTile[]> _landTiles = new Lazy<LandTile[]>( LoadLandTiles );
|
||||
private static readonly Lazy<StaticTile[]> _staticTiles = new Lazy<StaticTile[]>( LoadStaticTiles );
|
||||
|
||||
private static bool _oldFormat;
|
||||
|
||||
public static void Initialize( string dataPath )
|
||||
{
|
||||
_dataPath = dataPath;
|
||||
}
|
||||
|
||||
private static StaticTile[] LoadStaticTiles()
|
||||
{
|
||||
string fileName = Path.Combine( _dataPath, "tiledata.mul" );
|
||||
|
||||
if ( !File.Exists( fileName ) )
|
||||
{
|
||||
throw new FileNotFoundException( "File not found.", fileName );
|
||||
}
|
||||
|
||||
byte[] fileBytes = File.ReadAllBytes( fileName );
|
||||
|
||||
StaticTile[] staticTiles = new StaticTile[( fileBytes.Length - 428032 ) / 1188 * 32];
|
||||
|
||||
MemoryStream ms = new MemoryStream( fileBytes, false );
|
||||
|
||||
using ( BinaryReader reader = new BinaryReader( ms ) )
|
||||
{
|
||||
ms.Seek( 36, SeekOrigin.Begin );
|
||||
string name = Encoding.ASCII.GetString( reader.ReadBytes( 20 ) ).TrimEnd( '\0' );
|
||||
|
||||
if ( name == "VOID!!!!!!" )
|
||||
{
|
||||
_oldFormat = true;
|
||||
}
|
||||
|
||||
if ( _oldFormat )
|
||||
{
|
||||
const int offset = 428032;
|
||||
|
||||
ms.Seek( offset, SeekOrigin.Begin );
|
||||
|
||||
for ( int i = 0; i < 0x4000; ++i )
|
||||
{
|
||||
if ( ( i & 0x1F ) == 0 )
|
||||
{
|
||||
reader.ReadInt32(); // header
|
||||
}
|
||||
|
||||
staticTiles[i].ID = (ushort) i;
|
||||
staticTiles[i].Flags = (TileFlags) reader.ReadInt32();
|
||||
staticTiles[i].Weight = reader.ReadByte();
|
||||
staticTiles[i].Quality = reader.ReadByte();
|
||||
reader.ReadInt16();
|
||||
reader.ReadByte();
|
||||
staticTiles[i].Quantity = reader.ReadByte();
|
||||
reader.ReadInt16();
|
||||
reader.ReadByte();
|
||||
reader.ReadByte();
|
||||
reader.ReadInt16();
|
||||
staticTiles[i].Height = reader.ReadByte();
|
||||
staticTiles[i].Name = Encoding.ASCII.GetString( reader.ReadBytes( 20 ) ).TrimEnd( '\0' );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const int offset = 493568;
|
||||
|
||||
ms.Seek( offset, SeekOrigin.Begin );
|
||||
|
||||
for ( int i = 0; i < 0x10000; ++i )
|
||||
{
|
||||
if ( ( i & 0x1F ) == 0 )
|
||||
{
|
||||
reader.ReadInt32(); // header
|
||||
}
|
||||
|
||||
staticTiles[i].ID = (ushort) i;
|
||||
staticTiles[i].Flags = (TileFlags) reader.ReadInt64();
|
||||
staticTiles[i].Weight = reader.ReadByte();
|
||||
staticTiles[i].Quality = reader.ReadByte();
|
||||
reader.ReadInt16();
|
||||
reader.ReadByte();
|
||||
staticTiles[i].Quantity = reader.ReadByte();
|
||||
reader.ReadInt32();
|
||||
reader.ReadByte();
|
||||
/*int value = */
|
||||
reader.ReadByte();
|
||||
staticTiles[i].Height = reader.ReadByte();
|
||||
staticTiles[i].Name = Encoding.ASCII.GetString( reader.ReadBytes( 20 ) ).TrimEnd( '\0' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ms.Close();
|
||||
return staticTiles;
|
||||
}
|
||||
|
||||
private static LandTile[] LoadLandTiles()
|
||||
{
|
||||
string fileName = Path.Combine( _dataPath, "tiledata.mul" );
|
||||
|
||||
if ( !File.Exists( fileName ) )
|
||||
{
|
||||
throw new FileNotFoundException( "File not found.", fileName );
|
||||
}
|
||||
|
||||
byte[] fileBytes = File.ReadAllBytes( fileName );
|
||||
|
||||
LandTile[] landTiles = new LandTile[16384];
|
||||
|
||||
MemoryStream ms = new MemoryStream( fileBytes, false );
|
||||
|
||||
using ( BinaryReader bin = new BinaryReader( ms ) )
|
||||
{
|
||||
ms.Seek( 36, SeekOrigin.Begin );
|
||||
|
||||
string name = Encoding.ASCII.GetString( bin.ReadBytes( 20 ) ).TrimEnd( '\0' );
|
||||
|
||||
if ( name == "VOID!!!!!!" )
|
||||
{
|
||||
_oldFormat = true;
|
||||
}
|
||||
|
||||
ms.Seek( 0, SeekOrigin.Begin );
|
||||
|
||||
if ( _oldFormat )
|
||||
{
|
||||
for ( int i = 0; i < 0x4000; ++i )
|
||||
{
|
||||
if ( i == 0 || i > 0 && ( i & 0x1f ) == 0 )
|
||||
{
|
||||
bin.ReadInt32(); // block header
|
||||
}
|
||||
|
||||
landTiles[i].Flags = (TileFlags) bin.ReadInt32();
|
||||
landTiles[i].ID = bin.ReadInt16();
|
||||
landTiles[i].Name = Encoding.ASCII.GetString( bin.ReadBytes( 20 ) ).TrimEnd( '\0' );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( int i = 0; i < 0x4000; ++i )
|
||||
{
|
||||
if ( i == 1 || i > 0 && ( i & 0x1f ) == 0 )
|
||||
{
|
||||
bin.ReadInt32(); // block header
|
||||
}
|
||||
|
||||
landTiles[i].Flags = (TileFlags) bin.ReadInt64();
|
||||
landTiles[i].ID = bin.ReadInt16();
|
||||
landTiles[i].Name = Encoding.ASCII.GetString( bin.ReadBytes( 20 ) ).TrimEnd( '\0' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ms.Close();
|
||||
return landTiles;
|
||||
}
|
||||
|
||||
public static LandTile GetLandTile( int index )
|
||||
{
|
||||
return _landTiles.Value[index];
|
||||
}
|
||||
|
||||
public static StaticTile GetStaticTile( int index )
|
||||
{
|
||||
try
|
||||
{
|
||||
return _staticTiles.Value[index];
|
||||
}
|
||||
catch ( IndexOutOfRangeException )
|
||||
{
|
||||
return _staticTiles.Value[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
[Flags]
|
||||
public enum TileFlags : ulong
|
||||
{
|
||||
Background = 0x00000001,
|
||||
Weapon = 0x00000002,
|
||||
Transparent = 0x00000004,
|
||||
Translucent = 0x00000008,
|
||||
Wall = 0x00000010,
|
||||
Damaging = 0x00000020,
|
||||
Impassable = 0x00000040,
|
||||
Wet = 0x00000080,
|
||||
Unknown = 0x00000100,
|
||||
Surface = 0x00000200,
|
||||
Bridge = 0x00000400,
|
||||
Stackable = 0x00000800,
|
||||
Window = 0x00001000,
|
||||
NoShoot = 0x00002000,
|
||||
PrefixA = 0x00004000,
|
||||
PrefixAn = 0x00008000,
|
||||
Internal = 0x00010000,
|
||||
Foliage = 0x00020000,
|
||||
PartialHue = 0x00040000,
|
||||
Unknown1 = 0x00080000,
|
||||
Map = 0x00100000,
|
||||
Container = 0x00200000,
|
||||
Wearable = 0x00400000,
|
||||
LightSource = 0x00800000,
|
||||
Animated = 0x01000000,
|
||||
NoDiagonal = 0x02000000,
|
||||
Unknown2 = 0x04000000,
|
||||
Armor = 0x08000000,
|
||||
Roof = 0x10000000,
|
||||
Door = 0x20000000,
|
||||
StairBack = 0x40000000,
|
||||
StairRight = 0x80000000
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace ClassicAssist.UO.Data
|
||||
{
|
||||
public class UOPIndex : IDisposable
|
||||
{
|
||||
private readonly UOPEntry[] _entries;
|
||||
private readonly int _length;
|
||||
private readonly BinaryReader _reader;
|
||||
|
||||
public UOPIndex( Stream stream )
|
||||
{
|
||||
_reader = new BinaryReader( stream );
|
||||
_length = (int) stream.Length;
|
||||
|
||||
if ( _reader.ReadInt32() != 0x50594D )
|
||||
{
|
||||
throw new ArgumentException( "Invalid UOP file." );
|
||||
}
|
||||
|
||||
Version = _reader.ReadInt32();
|
||||
_reader.ReadInt32();
|
||||
int nextTable = _reader.ReadInt32();
|
||||
|
||||
List<UOPEntry> entries = new List<UOPEntry>();
|
||||
|
||||
do
|
||||
{
|
||||
stream.Seek( nextTable, SeekOrigin.Begin );
|
||||
int count = _reader.ReadInt32();
|
||||
nextTable = _reader.ReadInt32();
|
||||
_reader.ReadInt32();
|
||||
|
||||
for ( int i = 0; i < count; ++i )
|
||||
{
|
||||
int offset = _reader.ReadInt32();
|
||||
|
||||
if ( offset == 0 )
|
||||
{
|
||||
stream.Seek( 30, SeekOrigin.Current );
|
||||
continue;
|
||||
}
|
||||
|
||||
_reader.ReadInt64();
|
||||
int length = _reader.ReadInt32();
|
||||
|
||||
entries.Add( new UOPEntry( offset, length ) );
|
||||
|
||||
stream.Seek( 18, SeekOrigin.Current );
|
||||
}
|
||||
}
|
||||
while ( nextTable != 0 && nextTable < _length );
|
||||
|
||||
entries.Sort( OffsetComparer.Instance );
|
||||
|
||||
foreach ( UOPEntry t in entries )
|
||||
{
|
||||
stream.Seek( t.Offset + 2, SeekOrigin.Begin );
|
||||
|
||||
int dataOffset = _reader.ReadInt16();
|
||||
t.Offset += 4 + dataOffset;
|
||||
|
||||
stream.Seek( dataOffset, SeekOrigin.Current );
|
||||
t.Order = _reader.ReadInt32();
|
||||
}
|
||||
|
||||
entries.Sort();
|
||||
_entries = entries.ToArray();
|
||||
}
|
||||
|
||||
public int Version { get; }
|
||||
|
||||
public int Lookup( int offset )
|
||||
{
|
||||
int total = 0;
|
||||
|
||||
foreach ( UOPEntry t in _entries )
|
||||
{
|
||||
int newTotal = total + t.Length;
|
||||
|
||||
if ( offset < newTotal )
|
||||
{
|
||||
return t.Offset + ( offset - total );
|
||||
}
|
||||
|
||||
total = newTotal;
|
||||
}
|
||||
|
||||
return _length;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
_reader.Close();
|
||||
}
|
||||
|
||||
private class OffsetComparer : IComparer<UOPEntry>
|
||||
{
|
||||
public static readonly IComparer<UOPEntry> Instance = new OffsetComparer();
|
||||
|
||||
public int Compare( UOPEntry x, UOPEntry y )
|
||||
{
|
||||
if ( x == null || y == null )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return x.Offset.CompareTo( y.Offset );
|
||||
}
|
||||
}
|
||||
|
||||
private class UOPEntry : IComparable<UOPEntry>
|
||||
{
|
||||
public readonly int Length;
|
||||
public int Offset;
|
||||
public int Order;
|
||||
|
||||
public UOPEntry( int offset, int length )
|
||||
{
|
||||
Offset = offset;
|
||||
Length = length;
|
||||
Order = 0;
|
||||
}
|
||||
|
||||
public int CompareTo( UOPEntry other )
|
||||
{
|
||||
return Order.CompareTo( other.Order );
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
|
||||
private bool disposedValue; // To detect redundant calls
|
||||
|
||||
protected virtual void Dispose( bool disposing )
|
||||
{
|
||||
if ( disposedValue )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( disposing )
|
||||
{
|
||||
_reader.Dispose();
|
||||
}
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose( true );
|
||||
GC.SuppressFinalize( this );
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user