Files
Beyhan Oğur d5f52cc3d3 first commit
2026-09-03 04:45:36 +03:00

382 lines
12 KiB
C#

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
}
}