Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
212
Ultima/ASCIIFont.cs
Normal file
212
Ultima/ASCIIFont.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
#region References
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
#endregion
|
||||
|
||||
// ascii text support written by arul
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class ASCIIFont
|
||||
{
|
||||
public byte Header { get; private set; }
|
||||
public byte[] Unk { get; set; }
|
||||
public Bitmap[] Characters { get; set; }
|
||||
public int Height { get; set; }
|
||||
|
||||
public ASCIIFont(byte header)
|
||||
{
|
||||
Header = header;
|
||||
Height = 0;
|
||||
Unk = new byte[224];
|
||||
Characters = new Bitmap[224];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets Bitmap of given character
|
||||
/// </summary>
|
||||
/// <param name="character"></param>
|
||||
/// <returns></returns>
|
||||
public Bitmap GetBitmap(char character)
|
||||
{
|
||||
return Characters[((((character) - 0x20) & 0x7FFFFFFF) % 224)];
|
||||
}
|
||||
|
||||
public int GetWidth(string text)
|
||||
{
|
||||
if (text == null || text.Length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int width = 0;
|
||||
|
||||
for (int i = 0; i < text.Length; ++i)
|
||||
{
|
||||
width += GetBitmap(text[i]).Width;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
public void ReplaceCharacter(int character, Bitmap import)
|
||||
{
|
||||
Characters[character] = import;
|
||||
Height = import.Height;
|
||||
}
|
||||
|
||||
public static ASCIIFont GetFixed(int font)
|
||||
{
|
||||
if (font < 0 || font > 9)
|
||||
{
|
||||
return ASCIIText.Fonts[3];
|
||||
}
|
||||
|
||||
return ASCIIText.Fonts[font];
|
||||
}
|
||||
}
|
||||
|
||||
public static class ASCIIText
|
||||
{
|
||||
public static ASCIIFont[] Fonts = new ASCIIFont[10];
|
||||
|
||||
static ASCIIText()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads fonts.mul
|
||||
/// </summary>
|
||||
public static unsafe void Initialize()
|
||||
{
|
||||
string path = Files.GetFilePath("fonts.mul");
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
using (var reader = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
var buffer = new byte[(int)reader.Length];
|
||||
reader.Read(buffer, 0, (int)reader.Length);
|
||||
fixed (byte* bin = buffer)
|
||||
{
|
||||
byte* read = bin;
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
byte header = *read++;
|
||||
Fonts[i] = new ASCIIFont(header);
|
||||
|
||||
for (int k = 0; k < 224; ++k)
|
||||
{
|
||||
byte width = *read++;
|
||||
byte height = *read++;
|
||||
byte unk = *read++; // delimeter?
|
||||
|
||||
if (width > 0 && height > 0)
|
||||
{
|
||||
if (height > Fonts[i].Height && k < 96)
|
||||
{
|
||||
Fonts[i].Height = height;
|
||||
}
|
||||
|
||||
var bmp = new Bitmap(width, height);
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, Settings.PixelFormat);
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
|
||||
for (int y = 0; y < height; ++y, line += delta)
|
||||
{
|
||||
ushort* cur = line;
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
var pixel = (ushort)(*read++ | (*read++ << 8));
|
||||
if (pixel == 0)
|
||||
{
|
||||
cur[x] = pixel;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur[x] = (ushort)(pixel ^ 0x8000);
|
||||
}
|
||||
}
|
||||
}
|
||||
bmp.UnlockBits(bd);
|
||||
Fonts[i].Characters[k] = bmp;
|
||||
Fonts[i].Unk[k] = unk;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe void Save(string FileName)
|
||||
{
|
||||
using (var fs = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
using (var bin = new BinaryWriter(fs))
|
||||
{
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
bin.Write(Fonts[i].Header);
|
||||
for (int k = 0; k < 224; ++k)
|
||||
{
|
||||
bin.Write((byte)Fonts[i].Characters[k].Width);
|
||||
bin.Write((byte)Fonts[i].Characters[k].Height);
|
||||
bin.Write(Fonts[i].Unk[k]);
|
||||
Bitmap bmp = Fonts[i].Characters[k];
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, Settings.PixelFormat);
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
for (int y = 0; y < bmp.Height; ++y, line += delta)
|
||||
{
|
||||
ushort* cur = line;
|
||||
for (int x = 0; x < bmp.Width; ++x)
|
||||
{
|
||||
if (cur[x] == 0)
|
||||
{
|
||||
bin.Write(cur[x]);
|
||||
}
|
||||
else
|
||||
{
|
||||
bin.Write((ushort)(cur[x] ^ 0x8000));
|
||||
}
|
||||
}
|
||||
}
|
||||
bmp.UnlockBits(bd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws Text with font in Bitmap and returns
|
||||
/// </summary>
|
||||
/// <param name="fontId"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap DrawText(int fontId, string text)
|
||||
{
|
||||
ASCIIFont font = ASCIIFont.GetFixed(fontId);
|
||||
var result = new Bitmap(font.GetWidth(text) + 2, font.Height + 2);
|
||||
|
||||
int dx = 2;
|
||||
int dy = font.Height + 2;
|
||||
using (Graphics graph = Graphics.FromImage(result))
|
||||
{
|
||||
for (int i = 0; i < text.Length; ++i)
|
||||
{
|
||||
Bitmap bmp = font.GetBitmap(text[i]);
|
||||
graph.DrawImage(bmp, dx, dy - bmp.Height);
|
||||
dx += bmp.Width;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
1093
Ultima/AnimationEdit.cs
Normal file
1093
Ultima/AnimationEdit.cs
Normal file
File diff suppressed because it is too large
Load Diff
1089
Ultima/Animations.cs
Normal file
1089
Ultima/Animations.cs
Normal file
File diff suppressed because it is too large
Load Diff
165
Ultima/Animdata.cs
Normal file
165
Ultima/Animdata.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
#region References
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class Animdata
|
||||
{
|
||||
private static int[] m_Header;
|
||||
private static byte[] m_Unknown;
|
||||
|
||||
public static Hashtable AnimData { get; set; }
|
||||
|
||||
static Animdata()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads animdata.mul and fills <see cref="AnimData" />
|
||||
/// </summary>
|
||||
public static void Initialize()
|
||||
{
|
||||
AnimData = new Hashtable();
|
||||
string path = Files.GetFilePath("animdata.mul");
|
||||
if (path != null)
|
||||
{
|
||||
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
using (var bin = new BinaryReader(fs))
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
int id = 0;
|
||||
int h = 0;
|
||||
byte unk;
|
||||
byte fcount;
|
||||
byte finter;
|
||||
byte fstart;
|
||||
sbyte[] fdata;
|
||||
m_Header = new int[bin.BaseStream.Length / (4 + 8 * (64 + 4))];
|
||||
while (h < m_Header.Length /*bin.BaseStream.Length != bin.BaseStream.Position*/)
|
||||
{
|
||||
m_Header[h++] = bin.ReadInt32(); // chunk header
|
||||
// Read 8 tiles
|
||||
byte[] buffer = bin.ReadBytes(544);
|
||||
fixed (byte* buf = buffer)
|
||||
{
|
||||
byte* data = buf;
|
||||
for (int i = 0; i < 8; ++i, ++id)
|
||||
{
|
||||
fdata = new sbyte[64];
|
||||
for (int j = 0; j < 64; ++j)
|
||||
{
|
||||
fdata[j] = (sbyte)*data++;
|
||||
}
|
||||
unk = *data++;
|
||||
fcount = *data++;
|
||||
finter = *data++;
|
||||
fstart = *data++;
|
||||
if (fcount > 0)
|
||||
{
|
||||
AnimData[id] = new Data(fdata, unk, fcount, finter, fstart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var remaining = (int)(bin.BaseStream.Length - bin.BaseStream.Position);
|
||||
if (remaining > 0)
|
||||
{
|
||||
m_Unknown = bin.ReadBytes(remaining);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets Animation <see cref="Data" />
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public static Data GetAnimData(int id)
|
||||
{
|
||||
if (AnimData.Contains(id))
|
||||
{
|
||||
return ((Data)AnimData[id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Save(string path)
|
||||
{
|
||||
string FileName = Path.Combine(path, "animdata.mul");
|
||||
using (var fs = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
using (var bin = new BinaryWriter(fs))
|
||||
{
|
||||
int id = 0;
|
||||
int h = 0;
|
||||
while (id < m_Header.Length * 8)
|
||||
{
|
||||
bin.Write(m_Header[h++]);
|
||||
for (int i = 0; i < 8; ++i, ++id)
|
||||
{
|
||||
Data data = GetAnimData(id);
|
||||
for (int j = 0; j < 64; ++j)
|
||||
{
|
||||
if (data != null)
|
||||
{
|
||||
bin.Write(data.FrameData[j]);
|
||||
}
|
||||
else
|
||||
{
|
||||
bin.Write((sbyte)0);
|
||||
}
|
||||
}
|
||||
if (data != null)
|
||||
{
|
||||
bin.Write(data.Unknown);
|
||||
bin.Write(data.FrameCount);
|
||||
bin.Write(data.FrameInterval);
|
||||
bin.Write(data.FrameStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
bin.Write((byte)0);
|
||||
bin.Write((byte)0);
|
||||
bin.Write((byte)0);
|
||||
bin.Write((byte)0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m_Unknown != null)
|
||||
{
|
||||
bin.Write(m_Unknown);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Data
|
||||
{
|
||||
public sbyte[] FrameData { get; set; }
|
||||
public byte Unknown { get; private set; }
|
||||
public byte FrameCount { get; set; }
|
||||
public byte FrameInterval { get; set; }
|
||||
public byte FrameStart { get; set; }
|
||||
|
||||
public Data(sbyte[] frame, byte unk, byte fcount, byte finter, byte fstart)
|
||||
{
|
||||
FrameData = frame;
|
||||
Unknown = unk;
|
||||
FrameCount = fcount;
|
||||
FrameInterval = finter;
|
||||
FrameStart = fstart;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
837
Ultima/Art.cs
Normal file
837
Ultima/Art.cs
Normal file
@@ -0,0 +1,837 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class Art
|
||||
{
|
||||
private static FileIndex m_FileIndex = new FileIndex(
|
||||
"Artidx.mul", "Art.mul", "artLegacyMUL.uop", 0x10000 /*0x13FDC*/, 4, ".tga", 0x13FDC, false);
|
||||
|
||||
private static Bitmap[] m_Cache;
|
||||
private static bool[] m_Removed;
|
||||
private static readonly Hashtable m_patched = new Hashtable();
|
||||
public static bool Modified = false;
|
||||
|
||||
private static byte[] m_StreamBuffer;
|
||||
private static byte[] Validbuffer;
|
||||
|
||||
private struct CheckSums
|
||||
{
|
||||
public byte[] checksum;
|
||||
public int pos;
|
||||
public int length;
|
||||
public int index;
|
||||
}
|
||||
|
||||
private static List<CheckSums> checksumsLand;
|
||||
private static List<CheckSums> checksumsStatic;
|
||||
|
||||
static Art()
|
||||
{
|
||||
m_Cache = new Bitmap[0xFFFF];
|
||||
m_Removed = new bool[0xFFFF];
|
||||
}
|
||||
|
||||
public static int GetMaxItemID()
|
||||
{
|
||||
if (GetIdxLength() >= 0x13FDC)
|
||||
{
|
||||
return 0xFFFF;
|
||||
}
|
||||
|
||||
if (GetIdxLength() == 0xC000)
|
||||
{
|
||||
return 0x7FFF;
|
||||
}
|
||||
|
||||
return 0x3FFF;
|
||||
}
|
||||
|
||||
public static bool IsUOAHS()
|
||||
{
|
||||
return (GetIdxLength() >= 0x13FDC);
|
||||
}
|
||||
|
||||
public static ushort GetLegalItemID(int itemID, bool checkmaxid = true)
|
||||
{
|
||||
if (itemID < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (checkmaxid)
|
||||
{
|
||||
int max = GetMaxItemID();
|
||||
if (itemID > max)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return (ushort)itemID;
|
||||
}
|
||||
|
||||
public static int GetIdxLength()
|
||||
{
|
||||
return (int)(m_FileIndex.IdxLength / 12);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ReReads Art.mul
|
||||
/// </summary>
|
||||
public static void Reload()
|
||||
{
|
||||
m_FileIndex = new FileIndex(
|
||||
"Artidx.mul", "Art.mul", "artLegacyMUL.uop", 0x10000 /*0x13FDC*/, 4, ".tga", 0x13FDC, false);
|
||||
m_Cache = new Bitmap[0xFFFF];
|
||||
m_Removed = new bool[0xFFFF];
|
||||
m_patched.Clear();
|
||||
Modified = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets bmp of index in <see cref="m_Cache" /> of Static
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="bmp"></param>
|
||||
public static void ReplaceStatic(int index, Bitmap bmp)
|
||||
{
|
||||
index = GetLegalItemID(index);
|
||||
index += 0x4000;
|
||||
|
||||
m_Cache[index] = bmp;
|
||||
m_Removed[index] = false;
|
||||
if (m_patched.Contains(index))
|
||||
{
|
||||
m_patched.Remove(index);
|
||||
}
|
||||
Modified = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets bmp of index in <see cref="m_Cache" /> of Land
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="bmp"></param>
|
||||
public static void ReplaceLand(int index, Bitmap bmp)
|
||||
{
|
||||
index &= 0x3FFF;
|
||||
m_Cache[index] = bmp;
|
||||
m_Removed[index] = false;
|
||||
if (m_patched.Contains(index))
|
||||
{
|
||||
m_patched.Remove(index);
|
||||
}
|
||||
Modified = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes Static index <see cref="m_Removed" />
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
public static void RemoveStatic(int index)
|
||||
{
|
||||
index = GetLegalItemID(index);
|
||||
index += 0x4000;
|
||||
|
||||
m_Removed[index] = true;
|
||||
Modified = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes Land index <see cref="m_Removed" />
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
public static void RemoveLand(int index)
|
||||
{
|
||||
index &= 0x3FFF;
|
||||
m_Removed[index] = true;
|
||||
Modified = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if Static is definied (width and hight check)
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe bool IsValidStatic(int index)
|
||||
{
|
||||
index = GetLegalItemID(index);
|
||||
index += 0x4000;
|
||||
|
||||
if (m_Removed[index])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_Cache[index] != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int length, extra;
|
||||
bool patched;
|
||||
Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Validbuffer == null)
|
||||
{
|
||||
Validbuffer = new byte[4];
|
||||
}
|
||||
stream.Seek(4, SeekOrigin.Current);
|
||||
stream.Read(Validbuffer, 0, 4);
|
||||
fixed (byte* b = Validbuffer)
|
||||
{
|
||||
var dat = (short*)b;
|
||||
if (*dat++ <= 0 || *dat <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if LandTile is definied
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsValidLand(int index)
|
||||
{
|
||||
index &= 0x3FFF;
|
||||
if (m_Removed[index])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_Cache[index] != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int length, extra;
|
||||
bool patched;
|
||||
|
||||
return m_FileIndex.Valid(index, out length, out extra, out patched);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Bitmap of LandTile (with Cache)
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap GetLand(int index)
|
||||
{
|
||||
bool patched;
|
||||
return GetLand(index, out patched);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Bitmap of LandTile (with Cache) and verdata bool
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="patched"></param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap GetLand(int index, out bool patched)
|
||||
{
|
||||
index &= 0x3FFF;
|
||||
if (m_patched.Contains(index))
|
||||
{
|
||||
patched = (bool)m_patched[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
patched = false;
|
||||
}
|
||||
|
||||
if (m_Removed[index])
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (m_Cache[index] != null)
|
||||
{
|
||||
return m_Cache[index];
|
||||
}
|
||||
|
||||
int length, extra;
|
||||
Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (patched)
|
||||
{
|
||||
m_patched[index] = true;
|
||||
}
|
||||
|
||||
if (Files.CacheData)
|
||||
{
|
||||
return m_Cache[index] = LoadLand(stream, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
return LoadLand(stream, length);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] GetRawLand(int index)
|
||||
{
|
||||
index &= 0x3FFF;
|
||||
|
||||
int length, extra;
|
||||
bool patched;
|
||||
Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var buffer = new byte[length];
|
||||
stream.Read(buffer, 0, length);
|
||||
stream.Close();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Bitmap of Static (with Cache)
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap GetStatic(int index, bool checkmaxid = true)
|
||||
{
|
||||
bool patched;
|
||||
return GetStatic(index, out patched, checkmaxid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Bitmap of Static (with Cache) and verdata bool
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="patched"></param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap GetStatic(int index, out bool patched, bool checkmaxid = true)
|
||||
{
|
||||
index = GetLegalItemID(index, checkmaxid);
|
||||
index += 0x4000;
|
||||
|
||||
if (m_patched.Contains(index))
|
||||
{
|
||||
patched = (bool)m_patched[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
patched = false;
|
||||
}
|
||||
|
||||
if (m_Removed[index])
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (m_Cache[index] != null)
|
||||
{
|
||||
return m_Cache[index];
|
||||
}
|
||||
|
||||
int length, extra;
|
||||
Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (patched)
|
||||
{
|
||||
m_patched[index] = true;
|
||||
}
|
||||
|
||||
if (Files.CacheData)
|
||||
{
|
||||
return m_Cache[index] = LoadStatic(stream, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
return LoadStatic(stream, length);
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] GetRawStatic(int index)
|
||||
{
|
||||
index = GetLegalItemID(index);
|
||||
index += 0x4000;
|
||||
|
||||
int length, extra;
|
||||
bool patched;
|
||||
Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var buffer = new byte[length];
|
||||
stream.Read(buffer, 0, length);
|
||||
stream.Close();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static unsafe void Measure(Bitmap bmp, out int xMin, out int yMin, out int xMax, out int yMax)
|
||||
{
|
||||
xMin = yMin = 0;
|
||||
xMax = yMax = -1;
|
||||
|
||||
if (bmp == null || bmp.Width <= 0 || bmp.Height <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, Settings.PixelFormat);
|
||||
|
||||
int delta = (bd.Stride >> 1) - bd.Width;
|
||||
int lineDelta = bd.Stride >> 1;
|
||||
|
||||
var pBuffer = (ushort*)bd.Scan0;
|
||||
ushort* pLineEnd = pBuffer + bd.Width;
|
||||
ushort* pEnd = pBuffer + (bd.Height * lineDelta);
|
||||
|
||||
bool foundPixel = false;
|
||||
|
||||
int x = 0, y = 0;
|
||||
|
||||
while (pBuffer < pEnd)
|
||||
{
|
||||
while (pBuffer < pLineEnd)
|
||||
{
|
||||
ushort c = *pBuffer++;
|
||||
|
||||
if ((c & 0x8000) != 0)
|
||||
{
|
||||
if (!foundPixel)
|
||||
{
|
||||
foundPixel = true;
|
||||
xMin = xMax = x;
|
||||
yMin = yMax = y;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x < xMin)
|
||||
{
|
||||
xMin = x;
|
||||
}
|
||||
|
||||
if (y < yMin)
|
||||
{
|
||||
yMin = y;
|
||||
}
|
||||
|
||||
if (x > xMax)
|
||||
{
|
||||
xMax = x;
|
||||
}
|
||||
|
||||
if (y > yMax)
|
||||
{
|
||||
yMax = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
++x;
|
||||
}
|
||||
|
||||
pBuffer += delta;
|
||||
pLineEnd += lineDelta;
|
||||
++y;
|
||||
x = 0;
|
||||
}
|
||||
|
||||
bmp.UnlockBits(bd);
|
||||
}
|
||||
|
||||
private static unsafe Bitmap LoadStatic(Stream stream, int length)
|
||||
{
|
||||
Bitmap bmp;
|
||||
if (m_StreamBuffer == null || m_StreamBuffer.Length < length)
|
||||
{
|
||||
m_StreamBuffer = new byte[length];
|
||||
}
|
||||
stream.Read(m_StreamBuffer, 0, length);
|
||||
stream.Close();
|
||||
|
||||
fixed (byte* data = m_StreamBuffer)
|
||||
{
|
||||
var bindata = (ushort*)data;
|
||||
int count = 2;
|
||||
//bin.ReadInt32();
|
||||
int width = bindata[count++];
|
||||
int height = bindata[count++];
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var lookups = new int[height];
|
||||
|
||||
int start = (height + 4);
|
||||
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
lookups[i] = (start + (bindata[count++]));
|
||||
}
|
||||
|
||||
bmp = new Bitmap(width, height, Settings.PixelFormat);
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, Settings.PixelFormat);
|
||||
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
|
||||
for (int y = 0; y < height; ++y, line += delta)
|
||||
{
|
||||
count = lookups[y];
|
||||
|
||||
ushort* cur = line;
|
||||
ushort* end;
|
||||
int xOffset, xRun;
|
||||
|
||||
while (((xOffset = bindata[count++]) + (xRun = bindata[count++])) != 0)
|
||||
{
|
||||
if (xOffset > delta)
|
||||
{
|
||||
break;
|
||||
}
|
||||
cur += xOffset;
|
||||
if (xOffset + xRun > delta)
|
||||
{
|
||||
break;
|
||||
}
|
||||
end = cur + xRun;
|
||||
|
||||
while (cur < end)
|
||||
{
|
||||
*cur++ = (ushort)(bindata[count++] ^ 0x8000);
|
||||
}
|
||||
}
|
||||
}
|
||||
bmp.UnlockBits(bd);
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
|
||||
private static unsafe Bitmap LoadLand(Stream stream, int length)
|
||||
{
|
||||
var bmp = new Bitmap(44, 44, Settings.PixelFormat);
|
||||
BitmapData bd = bmp.LockBits(new Rectangle(0, 0, 44, 44), ImageLockMode.WriteOnly, Settings.PixelFormat);
|
||||
if (m_StreamBuffer == null || m_StreamBuffer.Length < length)
|
||||
{
|
||||
m_StreamBuffer = new byte[length];
|
||||
}
|
||||
stream.Read(m_StreamBuffer, 0, length);
|
||||
stream.Close();
|
||||
fixed (byte* bindata = m_StreamBuffer)
|
||||
{
|
||||
var bdata = (ushort*)bindata;
|
||||
int xOffset = 21;
|
||||
int xRun = 2;
|
||||
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
|
||||
for (int y = 0; y < 22; ++y, --xOffset, xRun += 2, line += delta)
|
||||
{
|
||||
ushort* cur = line + xOffset;
|
||||
ushort* end = cur + xRun;
|
||||
|
||||
while (cur < end)
|
||||
{
|
||||
*cur++ = (ushort)(*bdata++ | 0x8000);
|
||||
}
|
||||
}
|
||||
|
||||
xOffset = 0;
|
||||
xRun = 44;
|
||||
|
||||
for (int y = 0; y < 22; ++y, ++xOffset, xRun -= 2, line += delta)
|
||||
{
|
||||
ushort* cur = line + xOffset;
|
||||
ushort* end = cur + xRun;
|
||||
|
||||
while (cur < end)
|
||||
{
|
||||
*cur++ = (ushort)(*bdata++ | 0x8000);
|
||||
}
|
||||
}
|
||||
}
|
||||
bmp.UnlockBits(bd);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves mul
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
public static unsafe void Save(string path)
|
||||
{
|
||||
checksumsLand = new List<CheckSums>();
|
||||
checksumsStatic = new List<CheckSums>();
|
||||
string idx = Path.Combine(path, "artidx.mul");
|
||||
string mul = Path.Combine(path, "art.mul");
|
||||
using (
|
||||
FileStream fsidx = new FileStream(idx, FileMode.Create, FileAccess.Write, FileShare.Write),
|
||||
fsmul = new FileStream(mul, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
var memidx = new MemoryStream();
|
||||
var memmul = new MemoryStream();
|
||||
var sha = new SHA256Managed();
|
||||
//StreamWriter Tex = new StreamWriter(new FileStream("d:/artlog.txt", FileMode.Create, FileAccess.ReadWrite));
|
||||
|
||||
using (BinaryWriter binidx = new BinaryWriter(memidx), binmul = new BinaryWriter(memmul))
|
||||
{
|
||||
for (int index = 0; index < GetIdxLength(); index++)
|
||||
{
|
||||
Files.FireFileSaveEvent();
|
||||
if (m_Cache[index] == null)
|
||||
{
|
||||
if (index < 0x4000)
|
||||
{
|
||||
m_Cache[index] = GetLand(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Cache[index] = GetStatic(index - 0x4000, false);
|
||||
}
|
||||
}
|
||||
Bitmap bmp = m_Cache[index];
|
||||
if ((bmp == null) || (m_Removed[index]))
|
||||
{
|
||||
binidx.Write(-1); // lookup
|
||||
binidx.Write(0); // length
|
||||
binidx.Write(-1); // extra
|
||||
//Tex.WriteLine(System.String.Format("0x{0:X4} : 0x{1:X4} 0x{2:X4}", index, (int)-1, (int)-1));
|
||||
}
|
||||
else if (index < 0x4000)
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
bmp.Save(ms, ImageFormat.Bmp);
|
||||
byte[] checksum = sha.ComputeHash(ms.ToArray());
|
||||
CheckSums sum;
|
||||
if (compareSaveImagesLand(checksum, out sum))
|
||||
{
|
||||
binidx.Write(sum.pos); //lookup
|
||||
binidx.Write(sum.length);
|
||||
binidx.Write(0);
|
||||
//Tex.WriteLine(System.String.Format("0x{0:X4} : 0x{1:X4} 0x{2:X4}", index, (int)sum.pos, (int)sum.length));
|
||||
//Tex.WriteLine(System.String.Format("0x{0:X4} -> 0x{1:X4}", sum.index, index));
|
||||
continue;
|
||||
}
|
||||
//land
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, Settings.PixelFormat);
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
binidx.Write((int)binmul.BaseStream.Position); //lookup
|
||||
var length = (int)binmul.BaseStream.Position;
|
||||
int x = 22;
|
||||
int y = 0;
|
||||
int linewidth = 2;
|
||||
for (int m = 0; m < 22; ++m, ++y, line += delta, linewidth += 2)
|
||||
{
|
||||
--x;
|
||||
ushort* cur = line;
|
||||
for (int n = 0; n < linewidth; ++n)
|
||||
{
|
||||
binmul.Write((ushort)(cur[x + n] ^ 0x8000));
|
||||
}
|
||||
}
|
||||
x = 0;
|
||||
linewidth = 44;
|
||||
y = 22;
|
||||
line = (ushort*)bd.Scan0;
|
||||
line += delta * 22;
|
||||
for (int m = 0; m < 22; m++, y++, line += delta, ++x, linewidth -= 2)
|
||||
{
|
||||
ushort* cur = line;
|
||||
for (int n = 0; n < linewidth; n++)
|
||||
{
|
||||
binmul.Write((ushort)(cur[x + n] ^ 0x8000));
|
||||
}
|
||||
}
|
||||
int start = length;
|
||||
length = (int)binmul.BaseStream.Position - length;
|
||||
binidx.Write(length);
|
||||
binidx.Write(0);
|
||||
bmp.UnlockBits(bd);
|
||||
var s = new CheckSums
|
||||
{
|
||||
pos = start,
|
||||
length = length,
|
||||
checksum = checksum,
|
||||
index = index
|
||||
};
|
||||
//Tex.WriteLine(System.String.Format("0x{0:X4} : 0x{1:X4} 0x{2:X4}", index, start, length));
|
||||
checksumsLand.Add(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
bmp.Save(ms, ImageFormat.Bmp);
|
||||
byte[] checksum = sha.ComputeHash(ms.ToArray());
|
||||
CheckSums sum;
|
||||
if (compareSaveImagesStatic(checksum, out sum))
|
||||
{
|
||||
binidx.Write(sum.pos); //lookup
|
||||
binidx.Write(sum.length);
|
||||
binidx.Write(0);
|
||||
//Tex.WriteLine(System.String.Format("0x{0:X4} -> 0x{1:X4}", sum.index, index));
|
||||
//Tex.WriteLine(System.String.Format("0x{0:X4} : 0x{1:X4} 0x{2:X4}", index, sum.pos, sum.length));
|
||||
continue;
|
||||
}
|
||||
|
||||
// art
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, Settings.PixelFormat);
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
binidx.Write((int)binmul.BaseStream.Position); //lookup
|
||||
var length = (int)binmul.BaseStream.Position;
|
||||
binmul.Write(1234); // header
|
||||
binmul.Write((short)bmp.Width);
|
||||
binmul.Write((short)bmp.Height);
|
||||
var lookup = (int)binmul.BaseStream.Position;
|
||||
int streamloc = lookup + bmp.Height * 2;
|
||||
int width = 0;
|
||||
for (int i = 0; i < bmp.Height; ++i) // fill lookup
|
||||
{
|
||||
binmul.Write(width);
|
||||
}
|
||||
int X = 0;
|
||||
for (int Y = 0; Y < bmp.Height; ++Y, line += delta)
|
||||
{
|
||||
ushort* cur = line;
|
||||
width = (int)(binmul.BaseStream.Position - streamloc) / 2;
|
||||
binmul.BaseStream.Seek(lookup + Y * 2, SeekOrigin.Begin);
|
||||
binmul.Write(width);
|
||||
binmul.BaseStream.Seek(streamloc + width * 2, SeekOrigin.Begin);
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
X = 0;
|
||||
while (i < bmp.Width)
|
||||
{
|
||||
i = X;
|
||||
for (i = X; i <= bmp.Width; ++i)
|
||||
{
|
||||
//first pixel set
|
||||
if (i < bmp.Width)
|
||||
{
|
||||
if (cur[i] != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i < bmp.Width)
|
||||
{
|
||||
for (j = (i + 1); j < bmp.Width; ++j)
|
||||
{
|
||||
//next non set pixel
|
||||
if (cur[j] == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
binmul.Write((short)(i - X)); //xoffset
|
||||
binmul.Write((short)(j - i)); //run
|
||||
for (int p = i; p < j; ++p)
|
||||
{
|
||||
binmul.Write((ushort)(cur[p] ^ 0x8000));
|
||||
}
|
||||
X = j;
|
||||
}
|
||||
}
|
||||
binmul.Write((short)0); //xOffset
|
||||
binmul.Write((short)0); //Run
|
||||
}
|
||||
int start = length;
|
||||
length = (int)binmul.BaseStream.Position - length;
|
||||
binidx.Write(length);
|
||||
binidx.Write(0);
|
||||
bmp.UnlockBits(bd);
|
||||
var s = new CheckSums
|
||||
{
|
||||
pos = start,
|
||||
length = length,
|
||||
checksum = checksum,
|
||||
index = index
|
||||
};
|
||||
//Tex.WriteLine(System.String.Format("0x{0:X4} : 0x{1:X4} 0x{2:X4}", index, start, length));
|
||||
checksumsStatic.Add(s);
|
||||
}
|
||||
}
|
||||
memidx.WriteTo(fsidx);
|
||||
memmul.WriteTo(fsmul);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool compareSaveImagesLand(byte[] newchecksum, out CheckSums sum)
|
||||
{
|
||||
sum = new CheckSums();
|
||||
for (int i = 0; i < checksumsLand.Count; ++i)
|
||||
{
|
||||
byte[] cmp = checksumsLand[i].checksum;
|
||||
if (((cmp == null) || (newchecksum == null)) || (cmp.Length != newchecksum.Length))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool valid = true;
|
||||
for (int j = 0; j < cmp.Length; ++j)
|
||||
{
|
||||
if (cmp[j] != newchecksum[j])
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
{
|
||||
sum = checksumsLand[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool compareSaveImagesStatic(byte[] newchecksum, out CheckSums sum)
|
||||
{
|
||||
sum = new CheckSums();
|
||||
for (int i = 0; i < checksumsStatic.Count; ++i)
|
||||
{
|
||||
byte[] cmp = checksumsStatic[i].checksum;
|
||||
if (((cmp == null) || (newchecksum == null)) || (cmp.Length != newchecksum.Length))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool valid = true;
|
||||
for (int j = 0; j < cmp.Length; ++j)
|
||||
{
|
||||
if (cmp[j] != newchecksum[j])
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
{
|
||||
sum = checksumsStatic[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
62
Ultima/AssemblyInfo.cs
Normal file
62
Ultima/AssemblyInfo.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
#region References
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.InteropServices;
|
||||
#endregion
|
||||
|
||||
//
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
//
|
||||
|
||||
[assembly: AssemblyTitle("UltimaSDK")]
|
||||
[assembly: AssemblyDescription("The Ultima SDK is a Software Development Kit")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("")]
|
||||
[assembly: AssemblyCopyright("")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
//
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
|
||||
[assembly: AssemblyVersion("2.0.*")]
|
||||
//
|
||||
// In order to sign your assembly you must specify a key to use. Refer to the
|
||||
// Microsoft .NET Framework documentation for more information on assembly signing.
|
||||
//
|
||||
// Use the attributes below to control which key is used for signing.
|
||||
//
|
||||
// Notes:
|
||||
// (*) If no key is specified, the assembly is not signed.
|
||||
// (*) KeyName refers to a key that has been installed in the Crypto Service
|
||||
// Provider (CSP) on your machine. KeyFile refers to a file which contains
|
||||
// a key.
|
||||
// (*) If the KeyFile and the KeyName values are both specified, the
|
||||
// following processing occurs:
|
||||
// (1) If the KeyName can be found in the CSP, that key is used.
|
||||
// (2) If the KeyName does not exist and the KeyFile does exist, the key
|
||||
// in the KeyFile is installed into the CSP and used.
|
||||
// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
|
||||
// When specifying the KeyFile, the location of the KeyFile should be
|
||||
// relative to the project output directory which is
|
||||
// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
|
||||
// located in the project directory, you would specify the AssemblyKeyFile
|
||||
// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
|
||||
// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
|
||||
// documentation for more information on this.
|
||||
//
|
||||
[assembly: AssemblyDelaySign(false)]
|
||||
[assembly: AssemblyKeyFile("")]
|
||||
[assembly: AssemblyKeyName("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: NeutralResourcesLanguage("")]
|
||||
230
Ultima/CalibrationInfo.cs
Normal file
230
Ultima/CalibrationInfo.cs
Normal file
@@ -0,0 +1,230 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class CalibrationInfo
|
||||
{
|
||||
public byte[] Mask { get; private set; }
|
||||
public byte[] Vals { get; private set; }
|
||||
public byte[] DetX { get; private set; }
|
||||
public byte[] DetY { get; private set; }
|
||||
public byte[] DetZ { get; private set; }
|
||||
public byte[] DetF { get; private set; }
|
||||
|
||||
public CalibrationInfo(byte[] mask, byte[] vals, byte[] detx, byte[] dety, byte[] detz, byte[] detf)
|
||||
{
|
||||
Mask = mask;
|
||||
Vals = vals;
|
||||
DetX = detx;
|
||||
DetY = dety;
|
||||
DetZ = detz;
|
||||
DetF = detf;
|
||||
}
|
||||
|
||||
private static byte[] ReadBytes(StreamReader ip)
|
||||
{
|
||||
string line = ip.ReadLine();
|
||||
|
||||
if (line == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var buffer = new byte[(line.Length + 2) / 3];
|
||||
int index = 0;
|
||||
|
||||
for (int i = 0; (i + 1) < line.Length; i += 3)
|
||||
{
|
||||
char ch = line[i + 0];
|
||||
char cl = line[i + 1];
|
||||
|
||||
if (ch >= '0' && ch <= '9')
|
||||
{
|
||||
ch -= '0';
|
||||
}
|
||||
else if (ch >= 'a' && ch <= 'f')
|
||||
{
|
||||
ch -= (char)('a' - 10);
|
||||
}
|
||||
else if (ch >= 'A' && ch <= 'F')
|
||||
{
|
||||
ch -= (char)('A' - 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (cl >= '0' && cl <= '9')
|
||||
{
|
||||
cl -= '0';
|
||||
}
|
||||
else if (cl >= 'a' && cl <= 'f')
|
||||
{
|
||||
cl -= (char)('a' - 10);
|
||||
}
|
||||
else if (cl >= 'A' && cl <= 'F')
|
||||
{
|
||||
cl -= (char)('A' - 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
buffer[index++] = (byte)((ch << 4) | cl);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static CalibrationInfo[] m_DefaultList = new[]
|
||||
{
|
||||
new CalibrationInfo(
|
||||
//Post 7.0.4.0 (Andreew)
|
||||
new byte[]
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xFF, 0xD0, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x11, 0x8B, 0x82, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFF, 0xD0, 0x5B, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC
|
||||
},
|
||||
new byte[] {0x22, 0x04, 0xFF, 0xFF, 0xFF, 0x04, 0x0C},
|
||||
//x
|
||||
new byte[] {0x22, 0x04, 0xFF, 0xFF, 0xFF, 0x04, 0x08},
|
||||
//y
|
||||
new byte[] {0x22, 0x04, 0xFF, 0xFF, 0xFF, 0x04, 0x04},
|
||||
//z
|
||||
new byte[] {0x22, 0x04, 0xFF, 0xFF, 0xFF, 0x04, 0x10}), //f
|
||||
new CalibrationInfo(
|
||||
/* (arul) 6.0.9.x+ : Calibrates both */
|
||||
new byte[]
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xFF, 0xD0, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x11, 0x8B, 0x82, 0x00, 0x00,
|
||||
0x00, 0x00, 0xFF, 0xD0, 0x5E, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x0D
|
||||
},
|
||||
new byte[] {0x1F, 0x04, 0xFF, 0xFF, 0xFF, 0x04, 0x0C},
|
||||
new byte[] {0x1F, 0x04, 0xFF, 0xFF, 0xFF, 0x04, 0x08},
|
||||
new byte[] {0x1F, 0x04, 0xFF, 0xFF, 0xFF, 0x04, 0x04},
|
||||
new byte[] {0x1F, 0x04, 0xFF, 0xFF, 0xFF, 0x04, 0x10}),
|
||||
new CalibrationInfo(
|
||||
/* Facet */
|
||||
new byte[] {0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
|
||||
new byte[] {0xA0, 0x00, 0x00, 0x00, 0x00, 0x84, 0xC0, 0x0F, 0x85, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x0D},
|
||||
new byte[0],
|
||||
new byte[0],
|
||||
new byte[0],
|
||||
new byte[] {0x01, 0x04, 0xFF, 0xFF, 0xFF, 0x01}),
|
||||
new CalibrationInfo(
|
||||
/* Location */
|
||||
new byte[]
|
||||
{
|
||||
0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF,
|
||||
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0x8B, 0x15, 0x00, 0x00, 0x00, 0x00, 0x83, 0xC4, 0x10, 0x66, 0x89, 0x5A, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x66,
|
||||
0x89, 0x78, 0x00, 0x8B, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x66, 0x89, 0x71, 0x00
|
||||
},
|
||||
new byte[] {0x02, 0x04, 0x04, 0x0C, 0x01, 0x02},
|
||||
new byte[] {0x0E, 0x04, 0x04, 0x15, 0x01, 0x02},
|
||||
new byte[] {0x18, 0x04, 0x04, 0x1F, 0x01, 0x02},
|
||||
new byte[0]),
|
||||
new CalibrationInfo(
|
||||
/* UO3D Only, calibrates both */
|
||||
new byte[]
|
||||
{
|
||||
0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00
|
||||
},
|
||||
new byte[]
|
||||
{
|
||||
0xA1, 0x00, 0x00, 0x00, 0x00, 0x68, 0x40, 0x2E, 0x04, 0x01, 0x0F, 0xBF, 0x50, 0x00, 0x0F, 0xBF, 0x48, 0x00, 0x52,
|
||||
0x51, 0x0F, 0xBF, 0x50, 0x00, 0x52, 0x8D, 0x85, 0xE4, 0xFD, 0xFF, 0xFF, 0x68, 0x00, 0x00, 0x00, 0x00, 0x50, 0xE8,
|
||||
0x07, 0x44, 0x10, 0x00, 0x8A, 0x0D, 0x00, 0x00, 0x00, 0x00
|
||||
},
|
||||
new byte[] {0x01, 0x04, 0x04, 0x17, 0x01, 0x02},
|
||||
new byte[] {0x01, 0x04, 0x04, 0x11, 0x01, 0x02},
|
||||
new byte[] {0x01, 0x04, 0x04, 0x0D, 0x01, 0x02},
|
||||
new byte[] {0x2C, 0x04, 0xFF, 0xFF, 0xFF, 0x01})
|
||||
};
|
||||
|
||||
public static CalibrationInfo[] DefaultList { get { return m_DefaultList; } set { m_DefaultList = value; } }
|
||||
|
||||
public static CalibrationInfo[] GetList()
|
||||
{
|
||||
var list = new List<CalibrationInfo>();
|
||||
|
||||
string path = Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]);
|
||||
path = Path.Combine(path, "calibration.cfg");
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
using (var ip = new StreamReader(path))
|
||||
{
|
||||
string line;
|
||||
|
||||
while ((line = ip.ReadLine()) != null)
|
||||
{
|
||||
line = line.Trim();
|
||||
|
||||
if (line.Equals("Begin", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
byte[] mask, vals, detx, dety, detz, detf;
|
||||
|
||||
if ((mask = ReadBytes(ip)) == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((vals = ReadBytes(ip)) == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((detx = ReadBytes(ip)) == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((dety = ReadBytes(ip)) == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((detz = ReadBytes(ip)) == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((detf = ReadBytes(ip)) == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
list.Add(new CalibrationInfo(mask, vals, detx, dety, detz, detf));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list.AddRange(DefaultList);
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
495
Ultima/Client.cs
Normal file
495
Ultima/Client.cs
Normal file
@@ -0,0 +1,495 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.IO;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to interact with the Ultima Online client.
|
||||
/// </summary>
|
||||
public sealed class Client
|
||||
{
|
||||
private const int WM_CHAR = 0x102;
|
||||
|
||||
private static ClientWindowHandle m_Handle = ClientWindowHandle.Invalid;
|
||||
|
||||
private static WindowProcessStream m_ProcStream;
|
||||
private static LocationPointer m_LocationPointer;
|
||||
|
||||
private static bool m_Is_Iris2;
|
||||
|
||||
private Client()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="ProcessStream" /> instance which can be used to read the memory. Null is returned if the Client is not running.
|
||||
/// </summary>
|
||||
public static ProcessStream ProcessStream
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_ProcStream == null || m_ProcStream.Window != Handle)
|
||||
{
|
||||
if (Running)
|
||||
{
|
||||
m_ProcStream = new WindowProcessStream(Handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ProcStream = null;
|
||||
}
|
||||
}
|
||||
|
||||
return m_ProcStream;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the current <paramref name="x" />, <paramref name="y" />, and <paramref name="z" /> from memory based on a
|
||||
/// <see
|
||||
/// cref="Calibrate">
|
||||
/// calibrated memory location
|
||||
/// </see>
|
||||
/// .
|
||||
/// <seealso cref="Calibrate" />
|
||||
/// <seealso cref="ProcessStream" />
|
||||
/// <returns>True if the location was found, false if not</returns>
|
||||
/// </summary>
|
||||
public static bool FindLocation(ref int x, ref int y, ref int z, ref int facet)
|
||||
{
|
||||
LocationPointer lp = LocationPointer;
|
||||
ProcessStream pc = ProcessStream;
|
||||
|
||||
if (pc == null || lp == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pc.BeginAccess();
|
||||
|
||||
if (lp.PointerX > 0)
|
||||
{
|
||||
pc.Seek(lp.PointerX, SeekOrigin.Begin);
|
||||
x = Read(pc, lp.SizeX);
|
||||
}
|
||||
|
||||
if (lp.PointerY > 0)
|
||||
{
|
||||
pc.Seek(lp.PointerY, SeekOrigin.Begin);
|
||||
y = Read(pc, lp.SizeY);
|
||||
}
|
||||
|
||||
if (lp.PointerZ > 0)
|
||||
{
|
||||
pc.Seek(lp.PointerZ, SeekOrigin.Begin);
|
||||
z = Read(pc, lp.SizeZ);
|
||||
}
|
||||
|
||||
if (lp.PointerF > 0)
|
||||
{
|
||||
pc.Seek(lp.PointerF, SeekOrigin.Begin);
|
||||
facet = Read(pc, lp.SizeF);
|
||||
}
|
||||
|
||||
pc.EndAccess();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int Read(ProcessStream pc, int bytes)
|
||||
{
|
||||
var buffer = new byte[bytes];
|
||||
|
||||
pc.Read(buffer, 0, bytes);
|
||||
|
||||
switch (bytes)
|
||||
{
|
||||
case 1:
|
||||
return (sbyte)buffer[0];
|
||||
case 2:
|
||||
return (short)(buffer[0] | (buffer[1] << 8));
|
||||
case 4:
|
||||
return (buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24));
|
||||
}
|
||||
|
||||
int val = 0;
|
||||
int bits = 0;
|
||||
|
||||
for (int i = 0; i < buffer.Length; ++i)
|
||||
{
|
||||
val |= buffer[i] << bits;
|
||||
bits += 8;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
public static int Search(ProcessStream pc, byte[] mask, byte[] vals)
|
||||
{
|
||||
if (mask.Length != vals.Length)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
const int chunkSize = 4096;
|
||||
int readSize = chunkSize + mask.Length;
|
||||
|
||||
pc.BeginAccess();
|
||||
|
||||
var read = new byte[readSize];
|
||||
|
||||
for (int i = 0;; ++i)
|
||||
{
|
||||
pc.Seek(0x400000 + (i * chunkSize), SeekOrigin.Begin);
|
||||
int count = pc.Read(read, 0, readSize);
|
||||
|
||||
if (count != readSize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (int j = 0; j < chunkSize; ++j)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
for (int k = 0; ok && k < mask.Length; ++k)
|
||||
{
|
||||
ok = ((read[j + k] & mask[k]) == vals[k]);
|
||||
}
|
||||
|
||||
if (ok)
|
||||
{
|
||||
pc.EndAccess();
|
||||
return 0x400000 + (i * chunkSize) + j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pc.EndAccess();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int Search(ProcessStream pc, byte[] buffer)
|
||||
{
|
||||
const int chunkSize = 4096;
|
||||
int readSize = chunkSize + buffer.Length;
|
||||
|
||||
pc.BeginAccess();
|
||||
|
||||
var read = new byte[readSize];
|
||||
|
||||
for (int i = 0;; ++i)
|
||||
{
|
||||
pc.Seek(0x400000 + (i * chunkSize), SeekOrigin.Begin);
|
||||
int count = pc.Read(read, 0, readSize);
|
||||
|
||||
if (count != readSize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (int j = 0; j < chunkSize; ++j)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
for (int k = 0; ok && k < buffer.Length; ++k)
|
||||
{
|
||||
ok = (buffer[k] == read[j + k]);
|
||||
}
|
||||
|
||||
if (ok)
|
||||
{
|
||||
pc.EndAccess();
|
||||
return 0x400000 + (i * chunkSize) + j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pc.EndAccess();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to calibrate the <see cref="FindLocation" /> method based on an input <paramref name="x" />,
|
||||
/// <paramref
|
||||
/// name="y" />
|
||||
/// , and <paramref name="z" />.
|
||||
/// <seealso cref="FindLocation" />
|
||||
/// <seealso cref="ProcessStream" />
|
||||
/// </summary>
|
||||
/// <returns>The calibrated memory location -or- 0 if it could not be found.</returns>
|
||||
public static void Calibrate(int x, int y, int z)
|
||||
{
|
||||
m_LocationPointer = null;
|
||||
|
||||
ProcessStream pc = ProcessStream;
|
||||
|
||||
if (pc == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var buffer = new byte[12];
|
||||
|
||||
buffer[0] = (byte)z;
|
||||
buffer[1] = (byte)(z >> 8);
|
||||
buffer[2] = (byte)(z >> 16);
|
||||
buffer[3] = (byte)(z >> 24);
|
||||
|
||||
buffer[4] = (byte)y;
|
||||
buffer[5] = (byte)(y >> 8);
|
||||
buffer[6] = (byte)(y >> 16);
|
||||
buffer[7] = (byte)(y >> 24);
|
||||
|
||||
buffer[8] = (byte)x;
|
||||
buffer[9] = (byte)(x >> 8);
|
||||
buffer[10] = (byte)(x >> 16);
|
||||
buffer[11] = (byte)(x >> 24);
|
||||
|
||||
int ptr = Search(pc, buffer);
|
||||
|
||||
if (ptr == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_LocationPointer = new LocationPointer(ptr + 8, ptr + 4, ptr, 0, 4, 4, 4, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to automatically calibrate the <see cref="FindLocation" /> method.
|
||||
/// </summary>
|
||||
/// <returns>The calibrated memory location -or- 0 if it could not be found.</returns>
|
||||
public static void Calibrate()
|
||||
{
|
||||
Calibrate(CalibrationInfo.GetList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to automatically calibrate the <see cref="FindLocation" /> method.
|
||||
/// </summary>
|
||||
/// <returns>The calibrated memory location -or- 0 if it could not be found.</returns>
|
||||
public static void Calibrate(CalibrationInfo[] info)
|
||||
{
|
||||
m_LocationPointer = null;
|
||||
|
||||
ProcessStream pc = ProcessStream;
|
||||
|
||||
if (pc == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int ptrX = 0, sizeX = 0;
|
||||
int ptrY = 0, sizeY = 0;
|
||||
int ptrZ = 0, sizeZ = 0;
|
||||
int ptrF = 0, sizeF = 0;
|
||||
|
||||
for (int i = 0; i < info.Length; ++i)
|
||||
{
|
||||
CalibrationInfo ci = info[i];
|
||||
|
||||
int ptr = Search(pc, ci.Mask, ci.Vals);
|
||||
|
||||
if (ptr == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ptrX == 0 && ci.DetX.Length > 0)
|
||||
{
|
||||
GetCoordDetails(pc, ptr, ci.DetX, out ptrX, out sizeX);
|
||||
}
|
||||
|
||||
if (ptrY == 0 && ci.DetY.Length > 0)
|
||||
{
|
||||
GetCoordDetails(pc, ptr, ci.DetY, out ptrY, out sizeY);
|
||||
}
|
||||
|
||||
if (ptrZ == 0 && ci.DetZ.Length > 0)
|
||||
{
|
||||
GetCoordDetails(pc, ptr, ci.DetZ, out ptrZ, out sizeZ);
|
||||
}
|
||||
|
||||
if (ptrF == 0 && ci.DetF.Length > 0)
|
||||
{
|
||||
GetCoordDetails(pc, ptr, ci.DetF, out ptrF, out sizeF);
|
||||
}
|
||||
|
||||
if (ptrX != 0 && ptrY != 0 && ptrZ != 0 && ptrF != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ptrX != 0 || ptrY != 0 || ptrZ != 0 || ptrF != 0)
|
||||
{
|
||||
m_LocationPointer = new LocationPointer(ptrX, ptrY, ptrZ, ptrF, sizeX, sizeY, sizeZ, sizeF);
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetCoordDetails(ProcessStream pc, int ptr, byte[] dets, out int coordPointer, out int coordSize)
|
||||
{
|
||||
pc.Seek(ptr + dets[0], SeekOrigin.Begin);
|
||||
coordPointer = Read(pc, dets[1]);
|
||||
|
||||
if (dets[2] < 0xFF)
|
||||
{
|
||||
pc.Seek(coordPointer, SeekOrigin.Begin);
|
||||
coordPointer = Read(pc, dets[2]);
|
||||
}
|
||||
|
||||
if (dets[3] < 0xFF)
|
||||
{
|
||||
pc.Seek(ptr + dets[3], SeekOrigin.Begin);
|
||||
coordPointer += Read(pc, dets[4]);
|
||||
}
|
||||
|
||||
/*
|
||||
* arul:
|
||||
* The variable 'dets[6]' represents an offset into the struct that holds an info about players current location.
|
||||
* Added not to break functionality with the older clients (I hope).
|
||||
*
|
||||
* The struct looks as follows:
|
||||
*
|
||||
* DWORD fLoggedIn;
|
||||
* DWORD Z;
|
||||
* DWORD Y;
|
||||
* DWORD X;
|
||||
* DWORD Facet;
|
||||
*
|
||||
*/
|
||||
if (dets.Length == 7 && dets[6] < 0xFF)
|
||||
{
|
||||
coordPointer += dets[6];
|
||||
}
|
||||
coordSize = dets[5];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the memory location currently used for the <see cref="FindLocation" /> method.
|
||||
/// <seealso cref="FindLocation" />
|
||||
/// <seealso cref="Calibrate" />
|
||||
/// </summary>
|
||||
public static LocationPointer LocationPointer { get { return m_LocationPointer; } set { m_LocationPointer = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current window handle. A value of <c>ClientHandle.Invalid</c> is returned if the Client is not currently running.
|
||||
/// <seealso cref="Running" />
|
||||
/// </summary>
|
||||
public static ClientWindowHandle Handle
|
||||
{
|
||||
get
|
||||
{
|
||||
if (NativeMethods.IsWindow(m_Handle) == 0)
|
||||
{
|
||||
m_Handle = FindHandle();
|
||||
}
|
||||
|
||||
return m_Handle;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the Client is currently running.
|
||||
/// <seealso cref="ClientHandle" />
|
||||
/// </summary>
|
||||
public static bool Running { get { return (!Handle.IsInvalid); } }
|
||||
|
||||
/// <summary>
|
||||
/// Is Client Iris2
|
||||
/// </summary>
|
||||
public static bool Is_Iris2 { get { return m_Is_Iris2; } set { m_Is_Iris2 = value; } }
|
||||
|
||||
private static void SendChar(ClientWindowHandle hWnd, char c)
|
||||
{
|
||||
int value = c;
|
||||
int lParam = 1 | ((NativeMethods.OemKeyScan(value) & 0xFF) << 16) | (0x3 << 30);
|
||||
|
||||
NativeMethods.PostMessage(hWnd, WM_CHAR, value, lParam);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Brings the Client window to the foreground.
|
||||
/// </summary>
|
||||
/// <returns>True if the Client is running, false if not.</returns>
|
||||
public static bool BringToTop()
|
||||
{
|
||||
ClientWindowHandle hWnd = Handle;
|
||||
|
||||
if (!hWnd.IsInvalid)
|
||||
{
|
||||
NativeMethods.SetForegroundWindow(hWnd);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a <see cref="string" /> of characters (<paramref name="text" />) to the Client. The string is followed by a carriage return and line feed.
|
||||
/// </summary>
|
||||
/// <returns>True if the Client is running, false if not.</returns>
|
||||
public static bool SendText(string text)
|
||||
{
|
||||
ClientWindowHandle hWnd = Handle;
|
||||
|
||||
if (!hWnd.IsInvalid)
|
||||
{
|
||||
for (int i = 0; i < text.Length; ++i)
|
||||
{
|
||||
SendChar(hWnd, text[i]);
|
||||
}
|
||||
|
||||
SendChar(hWnd, '\r');
|
||||
SendChar(hWnd, '\n');
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a formatted <see cref="string" /> of characters to the Client. The string is followed by a carriage return and line feed. The format functionality is the same as
|
||||
/// <see
|
||||
/// cref="string.Format">
|
||||
/// String.Format
|
||||
/// </see>
|
||||
/// .
|
||||
/// </summary>
|
||||
/// <returns>True if the Client is running, false if not.</returns>
|
||||
public static bool SendText(string format, params object[] args)
|
||||
{
|
||||
return SendText(String.Format(format, args));
|
||||
}
|
||||
|
||||
private static ClientWindowHandle FindHandle()
|
||||
{
|
||||
ClientWindowHandle hWnd;
|
||||
|
||||
if (NativeMethods.IsWindow(hWnd = NativeMethods.FindWindowA("Ultima Online", null)) != 0)
|
||||
{
|
||||
return hWnd;
|
||||
}
|
||||
|
||||
if (NativeMethods.IsWindow(hWnd = NativeMethods.FindWindowA("Ultima Online Third Dawn", null)) != 0)
|
||||
{
|
||||
return hWnd;
|
||||
}
|
||||
if (NativeMethods.IsWindow(hWnd = NativeMethods.FindWindowA("OgreGLWindow", null)) != 0)
|
||||
{
|
||||
m_Is_Iris2 = true;
|
||||
return hWnd;
|
||||
}
|
||||
|
||||
return ClientWindowHandle.Invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Ultima/ClientHandles.cs
Normal file
48
Ultima/ClientHandles.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
#region References
|
||||
using System;
|
||||
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public class ClientWindowHandle : CriticalHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
public static ClientWindowHandle Invalid = new ClientWindowHandle(new IntPtr(-1));
|
||||
|
||||
public ClientWindowHandle()
|
||||
{ }
|
||||
|
||||
public ClientWindowHandle(IntPtr value)
|
||||
{
|
||||
handle = value;
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
if (!IsClosed)
|
||||
{
|
||||
return ReleaseHandle();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class ClientProcessHandle : CriticalHandleZeroOrMinusOneIsInvalid
|
||||
{
|
||||
public static ClientProcessHandle Invalid = new ClientProcessHandle(new IntPtr(-1));
|
||||
|
||||
public ClientProcessHandle()
|
||||
{ }
|
||||
|
||||
public ClientProcessHandle(IntPtr value)
|
||||
{
|
||||
handle = value;
|
||||
}
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
return NativeMethods.CloseHandle(this) == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
564
Ultima/FileIndex.cs
Normal file
564
Ultima/FileIndex.cs
Normal file
@@ -0,0 +1,564 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class FileIndex
|
||||
{
|
||||
public Entry3D[] Index { get; private set; }
|
||||
public Stream Stream { get; private set; }
|
||||
public long IdxLength { get; private set; }
|
||||
private readonly string MulPath;
|
||||
|
||||
public Stream Seek(int index, out int length, out int extra, out bool patched)
|
||||
{
|
||||
if (index < 0 || index >= Index.Length)
|
||||
{
|
||||
length = extra = 0;
|
||||
patched = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
Entry3D e = Index[index];
|
||||
|
||||
if (e.lookup < 0)
|
||||
{
|
||||
length = extra = 0;
|
||||
patched = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
length = e.length & 0x7FFFFFFF;
|
||||
extra = e.extra;
|
||||
|
||||
if ((e.length & (1 << 31)) != 0)
|
||||
{
|
||||
patched = true;
|
||||
Verdata.Seek(e.lookup);
|
||||
return Verdata.Stream;
|
||||
}
|
||||
|
||||
if (e.length < 0)
|
||||
{
|
||||
length = extra = 0;
|
||||
patched = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((Stream == null) || (!Stream.CanRead) || (!Stream.CanSeek))
|
||||
{
|
||||
if (MulPath == null)
|
||||
{
|
||||
Stream = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream = new FileStream(MulPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
}
|
||||
}
|
||||
|
||||
if (Stream == null)
|
||||
{
|
||||
length = extra = 0;
|
||||
patched = false;
|
||||
return null;
|
||||
}
|
||||
else if (Stream.Length < e.lookup)
|
||||
{
|
||||
length = extra = 0;
|
||||
patched = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
patched = false;
|
||||
|
||||
Stream.Seek(e.lookup, SeekOrigin.Begin);
|
||||
return Stream;
|
||||
}
|
||||
|
||||
public bool Valid(int index, out int length, out int extra, out bool patched)
|
||||
{
|
||||
if (index < 0 || index >= Index.Length)
|
||||
{
|
||||
length = extra = 0;
|
||||
patched = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Entry3D e = Index[index];
|
||||
|
||||
if (e.lookup < 0)
|
||||
{
|
||||
length = extra = 0;
|
||||
patched = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
length = e.length & 0x7FFFFFFF;
|
||||
extra = e.extra;
|
||||
|
||||
if ((e.length & (1 << 31)) != 0)
|
||||
{
|
||||
patched = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.length < 0)
|
||||
{
|
||||
length = extra = 0;
|
||||
patched = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((MulPath == null) || !File.Exists(MulPath))
|
||||
{
|
||||
length = extra = 0;
|
||||
patched = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((Stream == null) || (!Stream.CanRead) || (!Stream.CanSeek))
|
||||
{
|
||||
Stream = new FileStream(MulPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
}
|
||||
|
||||
if (Stream.Length < e.lookup)
|
||||
{
|
||||
length = extra = 0;
|
||||
patched = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
patched = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public FileIndex(string idxFile, string mulFile, int length, int file)
|
||||
: this(idxFile, mulFile, null, length, file, ".dat", -1, false)
|
||||
{ }
|
||||
|
||||
public FileIndex(
|
||||
string idxFile,
|
||||
string mulFile,
|
||||
string uopFile,
|
||||
int length,
|
||||
int file,
|
||||
string uopEntryExtension,
|
||||
int idxLength,
|
||||
bool hasExtra)
|
||||
{
|
||||
Index = new Entry3D[length];
|
||||
|
||||
string idxPath = null;
|
||||
MulPath = null;
|
||||
string uopPath = null;
|
||||
|
||||
if (Files.MulPath == null)
|
||||
{
|
||||
Files.LoadMulPath();
|
||||
}
|
||||
|
||||
if (Files.MulPath.Count > 0)
|
||||
{
|
||||
idxPath = Files.MulPath[idxFile.ToLower()];
|
||||
MulPath = Files.MulPath[mulFile.ToLower()];
|
||||
|
||||
if (!String.IsNullOrEmpty(uopFile) && Files.MulPath.ContainsKey(uopFile.ToLower()))
|
||||
{
|
||||
uopPath = Files.MulPath[uopFile.ToLower()];
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(idxPath))
|
||||
{
|
||||
idxPath = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (String.IsNullOrEmpty(Path.GetDirectoryName(idxPath)))
|
||||
{
|
||||
idxPath = Path.Combine(Files.RootDir, idxPath);
|
||||
}
|
||||
|
||||
if (!File.Exists(idxPath))
|
||||
{
|
||||
idxPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(MulPath))
|
||||
{
|
||||
MulPath = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (String.IsNullOrEmpty(Path.GetDirectoryName(MulPath)))
|
||||
{
|
||||
MulPath = Path.Combine(Files.RootDir, MulPath);
|
||||
}
|
||||
|
||||
if (!File.Exists(MulPath))
|
||||
{
|
||||
MulPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(uopPath))
|
||||
{
|
||||
uopPath = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (String.IsNullOrEmpty(Path.GetDirectoryName(uopPath)))
|
||||
{
|
||||
uopPath = Path.Combine(Files.RootDir, uopPath);
|
||||
}
|
||||
|
||||
if (!File.Exists(uopPath))
|
||||
{
|
||||
uopPath = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
MulPath = uopPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* UOP files support code, written by Wyatt (c) www.ruosi.org
|
||||
* idxLength variable was added for compatibility with legacy code for art (see art.cs)
|
||||
* At the moment the only UOP file having entries with extra field is gumpartlegacy.uop,
|
||||
* and it's two dwords in the beginning of the entry.
|
||||
* It's possible that UOP can include some entries with unknown hash: not really unknown for me, but
|
||||
* not useful for reading legacy entries. That's why i removed unknown hash exception throwing from this code
|
||||
*/
|
||||
if (MulPath != null && MulPath.EndsWith(".uop"))
|
||||
{
|
||||
using (var index = new FileStream(MulPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
|
||||
{
|
||||
Stream = new FileStream(MulPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
|
||||
var fi = new FileInfo(MulPath);
|
||||
string uopPattern = fi.Name.Replace(fi.Extension, "").ToLowerInvariant();
|
||||
|
||||
using (var br = new BinaryReader(Stream))
|
||||
{
|
||||
br.BaseStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
if (br.ReadInt32() != 0x50594D)
|
||||
{
|
||||
throw new ArgumentException("Bad UOP file.");
|
||||
}
|
||||
|
||||
br.ReadInt64(); // version + signature
|
||||
long nextBlock = br.ReadInt64();
|
||||
br.ReadInt32(); // block capacity
|
||||
int count = br.ReadInt32();
|
||||
|
||||
if (idxLength > 0)
|
||||
{
|
||||
IdxLength = idxLength * 12;
|
||||
}
|
||||
|
||||
var hashes = new Dictionary<ulong, int>();
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
string entryName = string.Format("build/{0}/{1:D8}{2}", uopPattern, i, uopEntryExtension);
|
||||
ulong hash = HashFileName(entryName);
|
||||
|
||||
if (!hashes.ContainsKey(hash))
|
||||
{
|
||||
hashes.Add(hash, i);
|
||||
}
|
||||
}
|
||||
|
||||
br.BaseStream.Seek(nextBlock, SeekOrigin.Begin);
|
||||
|
||||
do
|
||||
{
|
||||
int filesCount = br.ReadInt32();
|
||||
nextBlock = br.ReadInt64();
|
||||
|
||||
for (int i = 0; i < filesCount; i++)
|
||||
{
|
||||
long offset = br.ReadInt64();
|
||||
int headerLength = br.ReadInt32();
|
||||
int compressedLength = br.ReadInt32();
|
||||
int decompressedLength = br.ReadInt32();
|
||||
ulong hash = br.ReadUInt64();
|
||||
br.ReadUInt32(); // Adler32
|
||||
short flag = br.ReadInt16();
|
||||
|
||||
int entryLength = flag == 1 ? compressedLength : decompressedLength;
|
||||
|
||||
if (offset == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int idx;
|
||||
if (hashes.TryGetValue(hash, out idx))
|
||||
{
|
||||
if (idx < 0 || idx > Index.Length)
|
||||
{
|
||||
throw new IndexOutOfRangeException("hashes dictionary and files collection have different count of entries!");
|
||||
}
|
||||
|
||||
Index[idx].lookup = (int)(offset + headerLength);
|
||||
Index[idx].length = entryLength;
|
||||
|
||||
if (hasExtra)
|
||||
{
|
||||
long curPos = br.BaseStream.Position;
|
||||
|
||||
br.BaseStream.Seek(offset + headerLength, SeekOrigin.Begin);
|
||||
|
||||
byte[] extra = br.ReadBytes(8);
|
||||
|
||||
var extra1 = (ushort)((extra[3] << 24) | (extra[2] << 16) | (extra[1] << 8) | extra[0]);
|
||||
var extra2 = (ushort)((extra[7] << 24) | (extra[6] << 16) | (extra[5] << 8) | extra[4]);
|
||||
|
||||
Index[idx].lookup += 8;
|
||||
Index[idx].extra = extra1 << 16 | extra2;
|
||||
|
||||
br.BaseStream.Seek(curPos, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (br.BaseStream.Seek(nextBlock, SeekOrigin.Begin) != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((idxPath != null) && (MulPath != null))
|
||||
{
|
||||
using (var index = new FileStream(idxPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
|
||||
{
|
||||
Stream = new FileStream(MulPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
var count = (int)(index.Length / 12);
|
||||
IdxLength = index.Length;
|
||||
GCHandle gc = GCHandle.Alloc(Index, GCHandleType.Pinned);
|
||||
var buffer = new byte[index.Length];
|
||||
index.Read(buffer, 0, (int)index.Length);
|
||||
Marshal.Copy(buffer, 0, gc.AddrOfPinnedObject(), (int)Math.Min(IdxLength, length * 12));
|
||||
gc.Free();
|
||||
for (int i = count; i < length; ++i)
|
||||
{
|
||||
Index[i].lookup = -1;
|
||||
Index[i].length = -1;
|
||||
Index[i].extra = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream = null;
|
||||
return;
|
||||
}
|
||||
|
||||
Entry5D[] patches = Verdata.Patches;
|
||||
|
||||
if (file > -1)
|
||||
{
|
||||
for (int i = 0; i < patches.Length; ++i)
|
||||
{
|
||||
Entry5D patch = patches[i];
|
||||
|
||||
if (patch.file == file && patch.index >= 0 && patch.index < length)
|
||||
{
|
||||
Index[patch.index].lookup = patch.lookup;
|
||||
Index[patch.index].length = patch.length | (1 << 31);
|
||||
Index[patch.index].extra = patch.extra;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FileIndex(string idxFile, string mulFile, int file)
|
||||
{
|
||||
string idxPath = null;
|
||||
MulPath = null;
|
||||
if (Files.MulPath == null)
|
||||
{
|
||||
Files.LoadMulPath();
|
||||
}
|
||||
if (Files.MulPath.Count > 0)
|
||||
{
|
||||
idxPath = Files.MulPath[idxFile.ToLower()];
|
||||
MulPath = Files.MulPath[mulFile.ToLower()];
|
||||
if (String.IsNullOrEmpty(idxPath))
|
||||
{
|
||||
idxPath = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (String.IsNullOrEmpty(Path.GetDirectoryName(idxPath)))
|
||||
{
|
||||
idxPath = Path.Combine(Files.RootDir, idxPath);
|
||||
}
|
||||
if (!File.Exists(idxPath))
|
||||
{
|
||||
idxPath = null;
|
||||
}
|
||||
}
|
||||
if (String.IsNullOrEmpty(MulPath))
|
||||
{
|
||||
MulPath = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (String.IsNullOrEmpty(Path.GetDirectoryName(MulPath)))
|
||||
{
|
||||
MulPath = Path.Combine(Files.RootDir, MulPath);
|
||||
}
|
||||
if (!File.Exists(MulPath))
|
||||
{
|
||||
MulPath = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((idxPath != null) && (MulPath != null))
|
||||
{
|
||||
using (var index = new FileStream(idxPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
|
||||
{
|
||||
Stream = new FileStream(MulPath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
var count = (int)(index.Length / 12);
|
||||
IdxLength = index.Length;
|
||||
Index = new Entry3D[count];
|
||||
GCHandle gc = GCHandle.Alloc(Index, GCHandleType.Pinned);
|
||||
var buffer = new byte[index.Length];
|
||||
index.Read(buffer, 0, (int)index.Length);
|
||||
Marshal.Copy(buffer, 0, gc.AddrOfPinnedObject(), (int)index.Length);
|
||||
gc.Free();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream = null;
|
||||
Index = new Entry3D[1];
|
||||
return;
|
||||
}
|
||||
Entry5D[] patches = Verdata.Patches;
|
||||
|
||||
if (file > -1)
|
||||
{
|
||||
for (int i = 0; i < patches.Length; ++i)
|
||||
{
|
||||
Entry5D patch = patches[i];
|
||||
|
||||
if (patch.file == file && patch.index >= 0 && patch.index < Index.Length)
|
||||
{
|
||||
Index[patch.index].lookup = patch.lookup;
|
||||
Index[patch.index].length = patch.length | (1 << 31);
|
||||
Index[patch.index].extra = patch.extra;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method for calculating entry hash by it's name.
|
||||
/// Taken from Mythic.Package.dll
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <returns></returns>
|
||||
public static ulong HashFileName(string s)
|
||||
{
|
||||
uint eax, ecx, edx, ebx, esi, edi;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct Entry3D
|
||||
{
|
||||
public int lookup;
|
||||
public int length;
|
||||
public int extra;
|
||||
}
|
||||
}
|
||||
410
Ultima/Files.cs
Normal file
410
Ultima/Files.cs
Normal file
@@ -0,0 +1,410 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
using Microsoft.Win32;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class Files
|
||||
{
|
||||
public delegate void FileSaveHandler();
|
||||
|
||||
public static event FileSaveHandler FileSaveEvent;
|
||||
|
||||
public static void FireFileSaveEvent()
|
||||
{
|
||||
if (FileSaveEvent != null)
|
||||
{
|
||||
FileSaveEvent();
|
||||
}
|
||||
}
|
||||
|
||||
private static bool m_CacheData = true;
|
||||
private static Dictionary<string, string> m_MulPath;
|
||||
private static string m_Directory;
|
||||
private static string m_RootDir;
|
||||
|
||||
/// <summary>
|
||||
/// Should loaded Data be cached
|
||||
/// </summary>
|
||||
public static bool CacheData { get { return m_CacheData; } set { m_CacheData = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Should a Hashfile be used to speed up loading
|
||||
/// </summary>
|
||||
public static bool UseHashFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Contains the path infos
|
||||
/// </summary>
|
||||
public static Dictionary<string, string> MulPath { get { return m_MulPath; } set { m_MulPath = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of paths to the Client's data files.
|
||||
/// </summary>
|
||||
public static string Directory { get { return m_Directory; } }
|
||||
|
||||
/// <summary>
|
||||
/// Contains the rootDir (so relative values are possible for <see cref="MulPath" />
|
||||
/// </summary>
|
||||
public static string RootDir { get { return m_RootDir; } set { m_RootDir = value; } }
|
||||
|
||||
private static readonly string[] m_Files = new[]
|
||||
{
|
||||
"anim.idx", "anim.mul", "anim2.idx", "anim2.mul", "anim3.idx", "anim3.mul", "anim4.idx", "anim4.mul", "anim5.idx",
|
||||
"anim5.mul", "animdata.mul", "art.mul", "artidx.mul", "artlegacymul.uop", "body.def", "bodyconv.def", "client.exe",
|
||||
"cliloc.custom1", "cliloc.custom2", "cliloc.deu", "cliloc.enu", "equipconv.def", "facet00.mul", "facet01.mul",
|
||||
"facet02.mul", "facet03.mul", "facet04.mul", "facet05.mul", "fonts.mul", "gump.def", "gumpart.mul", "gumpidx.mul",
|
||||
"gumpartlegacymul.uop", "hues.mul", "light.mul", "lightidx.mul", "map0.mul", "map1.mul", "map2.mul", "map3.mul",
|
||||
"map4.mul", "map5.mul", "map0legacymul.uop", "map1legacymul.uop", "map2legacymul.uop", "map3legacymul.uop",
|
||||
"map4legacymul.uop", "map5legacymul.uop", "mapdif0.mul", "mapdif1.mul", "mapdif2.mul", "mapdif3.mul", "mapdif4.mul",
|
||||
"mapdifl0.mul", "mapdifl1.mul", "mapdifl2.mul", "mapdifl3.mul", "mapdifl4.mul", "mobtypes.txt", "multi.idx",
|
||||
"multi.mul", "multimap.rle", "radarcol.mul", "skillgrp.mul", "skills.idx", "skills.mul", "sound.def", "sound.mul",
|
||||
"soundidx.mul", "soundlegacymul.uop", "speech.mul", "stadif0.mul", "stadif1.mul", "stadif2.mul", "stadif3.mul",
|
||||
"stadif4.mul", "stadifi0.mul", "stadifi1.mul", "stadifi2.mul", "stadifi3.mul", "stadifi4.mul", "stadifl0.mul",
|
||||
"stadifl1.mul", "stadifl2.mul", "stadifl3.mul", "stadifl4.mul", "staidx0.mul", "staidx1.mul", "staidx2.mul",
|
||||
"staidx3.mul", "staidx4.mul", "staidx5.mul", "statics0.mul", "statics1.mul", "statics2.mul", "statics3.mul",
|
||||
"statics4.mul", "statics5.mul", "texidx.mul", "texmaps.mul", "tiledata.mul", "unifont.mul", "unifont1.mul",
|
||||
"unifont2.mul", "unifont3.mul", "unifont4.mul", "unifont5.mul", "unifont6.mul", "unifont7.mul", "unifont8.mul",
|
||||
"unifont9.mul", "unifont10.mul", "unifont11.mul", "unifont12.mul", "uotd.exe", "verdata.mul"
|
||||
};
|
||||
|
||||
static Files()
|
||||
{
|
||||
m_Directory = LoadDirectory();
|
||||
LoadMulPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ReReads Registry Client dir
|
||||
/// </summary>
|
||||
public static void ReLoadDirectory()
|
||||
{
|
||||
m_Directory = LoadDirectory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills <see cref="MulPath" /> with <see cref="Files.Directory" />
|
||||
/// </summary>
|
||||
public static void LoadMulPath()
|
||||
{
|
||||
m_MulPath = new Dictionary<string, string>();
|
||||
m_RootDir = Directory;
|
||||
if (m_RootDir == null)
|
||||
{
|
||||
m_RootDir = "";
|
||||
}
|
||||
foreach (string file in m_Files)
|
||||
{
|
||||
string filePath = Path.Combine(m_RootDir, file);
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
m_MulPath[file] = file;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_MulPath[file] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ReSets <see cref="MulPath" /> with given path
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
public static void SetMulPath(string path)
|
||||
{
|
||||
m_RootDir = path;
|
||||
foreach (string file in m_Files)
|
||||
{
|
||||
string filePath;
|
||||
if (!String.IsNullOrEmpty(m_MulPath[file])) //file was set
|
||||
{
|
||||
if (String.IsNullOrEmpty(Path.GetDirectoryName(m_MulPath[file]))) //and relative
|
||||
{
|
||||
filePath = Path.Combine(m_RootDir, m_MulPath[file]);
|
||||
if (File.Exists(filePath)) // exists in new Root?
|
||||
{
|
||||
m_MulPath[file] = filePath;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else // absolut dir ignore
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
filePath = Path.Combine(m_RootDir, file); //file was not set, or relative and non existent
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
m_MulPath[file] = file;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_MulPath[file] = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets <see cref="MulPath" /> key to path
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="key"></param>
|
||||
public static void SetMulPath(string path, string key)
|
||||
{
|
||||
MulPath[key] = path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a given <paramref name="file" /> in <see cref="Files.MulPath" />
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The absolute path to <paramref name="file" /> -or- <c>null</c> if <paramref name="file" /> was not found.
|
||||
/// </returns>
|
||||
public static string GetFilePath(string file)
|
||||
{
|
||||
if (MulPath.Count > 0)
|
||||
{
|
||||
string path = "";
|
||||
if (MulPath.ContainsKey(file.ToLower()))
|
||||
{
|
||||
path = MulPath[file.ToLower()];
|
||||
}
|
||||
if (String.IsNullOrEmpty(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (String.IsNullOrEmpty(Path.GetDirectoryName(path)))
|
||||
{
|
||||
path = Path.Combine(m_RootDir, path);
|
||||
}
|
||||
if (File.Exists(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static string GetFilePath(string format, params object[] args)
|
||||
{
|
||||
return GetFilePath(String.Format(format, args));
|
||||
}
|
||||
|
||||
private static readonly string[] knownRegkeys = new[]
|
||||
{
|
||||
@"Electronic Arts\EA Games\Ultima Online Classic", @"Electronic Arts\EA Games\Ultima Online Stygian Abyss Classic",
|
||||
@"Origin Worlds Online\Ultima Online\KR Legacy Beta", @"Origin Worlds Online\Ultima Online Samurai Empire\3d\1.0",
|
||||
@"Origin Worlds Online\Ultima Online Samurai Empire\2d\1.0",
|
||||
@"Origin Worlds Online\Ultima Online Samurai Empire BETA\3d\1.0",
|
||||
@"Origin Worlds Online\Ultima Online Samurai Empire BETA\2d\1.0",
|
||||
@"EA Games\Ultima Online: Mondain's Legacy\1.0", @"EA Games\Ultima Online: Mondain's Legacy\1.00.0000",
|
||||
@"EA GAMES\Ultima Online: Samurai Empire\1.00.0000", @"EA Games\Ultima Online: Mondain's Legacy",
|
||||
@"EA GAMES\Ultima Online Samurai Empire\1.00.0000", @"EA GAMES\Ultima Online: Samurai Empire\1.0",
|
||||
@"EA GAMES\Ultima Online Samurai Empire", @"EA GAMES\Ultima Online Samurai Empire\1.0",
|
||||
@"Origin Worlds Online\Ultima Online\1.0", @"Origin Worlds Online\Ultima Online Third Dawn\1.0",
|
||||
};
|
||||
|
||||
private static readonly string[] knownRegPathkeys = new[] {"ExePath", "Install Dir", "InstallDir"};
|
||||
|
||||
public static string LoadDirectory()
|
||||
{
|
||||
string dir = null;
|
||||
for (int i = knownRegkeys.Length - 1; i >= 0; i--)
|
||||
{
|
||||
string exePath;
|
||||
|
||||
if (Environment.Is64BitOperatingSystem)
|
||||
{
|
||||
exePath = GetPath(string.Format(@"Wow6432Node\{0}", knownRegkeys[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
exePath = GetPath(knownRegkeys[i]);
|
||||
}
|
||||
|
||||
if (exePath != null)
|
||||
{
|
||||
dir = exePath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
private static string GetPath(string regkey)
|
||||
{
|
||||
try
|
||||
{
|
||||
RegistryKey key = Registry.LocalMachine.OpenSubKey(string.Format(@"SOFTWARE\{0}", regkey));
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
key = Registry.CurrentUser.OpenSubKey(string.Format(@"SOFTWARE\{0}", regkey));
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
string path = null;
|
||||
foreach (string pathkey in knownRegPathkeys)
|
||||
{
|
||||
path = key.GetValue(pathkey) as string;
|
||||
|
||||
if ((path == null) || (path.Length <= 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pathkey == "InstallDir")
|
||||
{
|
||||
path = path + @"\";
|
||||
}
|
||||
|
||||
if (!System.IO.Directory.Exists(path) && !File.Exists(path))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!System.IO.Directory.Exists(path))
|
||||
{
|
||||
path = Path.GetDirectoryName(path);
|
||||
}
|
||||
|
||||
if ((path == null) || (!System.IO.Directory.Exists(path)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares given MD5 hash with hash of given file
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <param name="hash"></param>
|
||||
/// <returns></returns>
|
||||
public static bool CompareMD5(string file, string hash)
|
||||
{
|
||||
if (file == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
FileStream FileCheck = File.OpenRead(file);
|
||||
using (MD5 md5 = new MD5CryptoServiceProvider())
|
||||
{
|
||||
byte[] md5Hash = md5.ComputeHash(FileCheck);
|
||||
FileCheck.Close();
|
||||
string md5string = BitConverter.ToString(md5Hash).Replace("-", "").ToLower();
|
||||
if (md5string == hash)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns MD5 hash from given file
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
public static byte[] GetMD5(string file)
|
||||
{
|
||||
if (file == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
FileStream FileCheck = File.OpenRead(file);
|
||||
using (MD5 md5 = new MD5CryptoServiceProvider())
|
||||
{
|
||||
byte[] md5Hash = md5.ComputeHash(FileCheck);
|
||||
FileCheck.Close();
|
||||
return md5Hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares MD5 hash from given mul file with hash in responsible hash-file
|
||||
/// </summary>
|
||||
/// <param name="what"></param>
|
||||
/// <returns></returns>
|
||||
public static bool CompareHashFile(string what, string path)
|
||||
{
|
||||
string FileName = Path.Combine(path, String.Format("UOFiddler{0}.hash", what));
|
||||
if (File.Exists(FileName))
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var bin = new BinaryReader(new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.Read)))
|
||||
{
|
||||
int length = bin.ReadInt32();
|
||||
var buffer = new byte[length];
|
||||
bin.Read(buffer, 0, length);
|
||||
string hashold = BitConverter.ToString(buffer).Replace("-", "").ToLower();
|
||||
return CompareMD5(GetFilePath(String.Format("{0}.mul", what)), hashold);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if map1.mul exists and sets <see cref="Ultima.Map" />
|
||||
/// </summary>
|
||||
public static void CheckForNewMapSize()
|
||||
{
|
||||
if (GetFilePath("map1.mul") != null)
|
||||
{
|
||||
if (Map.Trammel.Width == 7168)
|
||||
{
|
||||
Map.Trammel = new Map(1, 1, 7168, 4096);
|
||||
}
|
||||
else
|
||||
{
|
||||
Map.Trammel = new Map(1, 1, 6144, 4096);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Map.Trammel.Width == 7168)
|
||||
{
|
||||
Map.Trammel = new Map(0, 1, 7168, 4096);
|
||||
}
|
||||
else
|
||||
{
|
||||
Map.Trammel = new Map(0, 1, 6144, 4096);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
531
Ultima/Gumps.cs
Normal file
531
Ultima/Gumps.cs
Normal file
@@ -0,0 +1,531 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class Gumps
|
||||
{
|
||||
private static FileIndex m_FileIndex = new FileIndex(
|
||||
"Gumpidx.mul", "Gumpart.mul", "gumpartLegacyMUL.uop", 0xFFFF, 12, ".tga", -1, true);
|
||||
|
||||
private static Bitmap[] m_Cache;
|
||||
private static bool[] m_Removed;
|
||||
private static readonly Hashtable m_patched = new Hashtable();
|
||||
|
||||
private static byte[] m_PixelBuffer;
|
||||
private static byte[] m_StreamBuffer;
|
||||
private static byte[] m_ColorTable;
|
||||
|
||||
static Gumps()
|
||||
{
|
||||
if (m_FileIndex != null)
|
||||
{
|
||||
m_Cache = new Bitmap[m_FileIndex.Index.Length];
|
||||
m_Removed = new bool[m_FileIndex.Index.Length];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Cache = new Bitmap[0xFFFF];
|
||||
m_Removed = new bool[0xFFFF];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ReReads gumpart
|
||||
/// </summary>
|
||||
public static void Reload()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_FileIndex = new FileIndex("Gumpidx.mul", "Gumpart.mul", "gumpartLegacyMUL.uop", 12, -1, ".tga", -1, true);
|
||||
m_Cache = new Bitmap[m_FileIndex.Index.Length];
|
||||
m_Removed = new bool[m_FileIndex.Index.Length];
|
||||
}
|
||||
catch
|
||||
{
|
||||
m_FileIndex = null;
|
||||
m_Cache = new Bitmap[0xFFFF];
|
||||
m_Removed = new bool[0xFFFF];
|
||||
}
|
||||
|
||||
m_PixelBuffer = null;
|
||||
m_StreamBuffer = null;
|
||||
m_ColorTable = null;
|
||||
m_patched.Clear();
|
||||
}
|
||||
|
||||
public static int GetCount()
|
||||
{
|
||||
return m_Cache.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces Gump <see cref="m_Cache" />
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="bmp"></param>
|
||||
public static void ReplaceGump(int index, Bitmap bmp)
|
||||
{
|
||||
m_Cache[index] = bmp;
|
||||
m_Removed[index] = false;
|
||||
if (m_patched.Contains(index))
|
||||
{
|
||||
m_patched.Remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes Gumpindex <see cref="m_Removed" />
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
public static void RemoveGump(int index)
|
||||
{
|
||||
m_Removed[index] = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if index is definied
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsValidIndex(int index)
|
||||
{
|
||||
if (m_FileIndex == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (index > m_Cache.Length - 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_Removed[index])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_Cache[index] != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
int length, extra;
|
||||
bool patched;
|
||||
|
||||
if (!m_FileIndex.Valid(index, out length, out extra, out patched))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (extra == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int width = (extra >> 16) & 0xFFFF;
|
||||
int height = extra & 0xFFFF;
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static byte[] GetRawGump(int index, out int width, out int height)
|
||||
{
|
||||
width = -1;
|
||||
height = -1;
|
||||
int length, extra;
|
||||
bool patched;
|
||||
Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (extra == -1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
width = (extra >> 16) & 0xFFFF;
|
||||
height = extra & 0xFFFF;
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var buffer = new byte[length];
|
||||
stream.Read(buffer, 0, length);
|
||||
stream.Close();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Bitmap of index and applies Hue
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="hue"></param>
|
||||
/// <param name="onlyHueGrayPixels"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe Bitmap GetGump(int index, Hue hue, bool onlyHueGrayPixels, out bool patched)
|
||||
{
|
||||
int length, extra;
|
||||
Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (extra == -1)
|
||||
{
|
||||
stream.Close();
|
||||
return null;
|
||||
}
|
||||
|
||||
int width = (extra >> 16) & 0xFFFF;
|
||||
int height = extra & 0xFFFF;
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
stream.Close();
|
||||
return null;
|
||||
}
|
||||
|
||||
int bytesPerLine = width << 1;
|
||||
int bytesPerStride = (bytesPerLine + 3) & ~3;
|
||||
int bytesForImage = height * bytesPerStride;
|
||||
|
||||
int pixelsPerStride = (width + 1) & ~1;
|
||||
int pixelsPerStrideDelta = pixelsPerStride - width;
|
||||
|
||||
byte[] pixelBuffer = m_PixelBuffer;
|
||||
|
||||
if (pixelBuffer == null || pixelBuffer.Length < bytesForImage)
|
||||
{
|
||||
m_PixelBuffer = pixelBuffer = new byte[(bytesForImage + 2047) & ~2047];
|
||||
}
|
||||
|
||||
byte[] streamBuffer = m_StreamBuffer;
|
||||
|
||||
if (streamBuffer == null || streamBuffer.Length < length)
|
||||
{
|
||||
m_StreamBuffer = streamBuffer = new byte[(length + 2047) & ~2047];
|
||||
}
|
||||
|
||||
byte[] colorTable = m_ColorTable;
|
||||
|
||||
if (colorTable == null)
|
||||
{
|
||||
m_ColorTable = colorTable = new byte[128];
|
||||
}
|
||||
|
||||
stream.Read(streamBuffer, 0, length);
|
||||
|
||||
fixed (short* psHueColors = hue.Colors)
|
||||
{
|
||||
fixed (byte* pbStream = streamBuffer)
|
||||
{
|
||||
fixed (byte* pbPixels = pixelBuffer)
|
||||
{
|
||||
fixed (byte* pbColorTable = colorTable)
|
||||
{
|
||||
var pHueColors = (ushort*)psHueColors;
|
||||
ushort* pHueColorsEnd = pHueColors + 32;
|
||||
|
||||
var pColorTable = (ushort*)pbColorTable;
|
||||
|
||||
ushort* pColorTableOpaque = pColorTable;
|
||||
|
||||
while (pHueColors < pHueColorsEnd)
|
||||
{
|
||||
*pColorTableOpaque++ = *pHueColors++;
|
||||
}
|
||||
|
||||
var pPixelDataStart = (ushort*)pbPixels;
|
||||
|
||||
var pLookup = (int*)pbStream;
|
||||
int* pLookupEnd = pLookup + height;
|
||||
int* pPixelRleStart = pLookup;
|
||||
int* pPixelRle;
|
||||
|
||||
ushort* pPixel = pPixelDataStart;
|
||||
ushort* pRleEnd = pPixel;
|
||||
ushort* pPixelEnd = pPixel + width;
|
||||
|
||||
ushort color, count;
|
||||
|
||||
if (onlyHueGrayPixels)
|
||||
{
|
||||
while (pLookup < pLookupEnd)
|
||||
{
|
||||
pPixelRle = pPixelRleStart + *pLookup++;
|
||||
pRleEnd = pPixel;
|
||||
|
||||
while (pPixel < pPixelEnd)
|
||||
{
|
||||
color = *(ushort*)pPixelRle;
|
||||
count = *(1 + (ushort*)pPixelRle);
|
||||
++pPixelRle;
|
||||
|
||||
pRleEnd += count;
|
||||
|
||||
if (color != 0 && (color & 0x1F) == ((color >> 5) & 0x1F) && (color & 0x1F) == ((color >> 10) & 0x1F))
|
||||
{
|
||||
color = pColorTable[color >> 10];
|
||||
}
|
||||
else if (color != 0)
|
||||
{
|
||||
color ^= 0x8000;
|
||||
}
|
||||
|
||||
while (pPixel < pRleEnd)
|
||||
{
|
||||
*pPixel++ = color;
|
||||
}
|
||||
}
|
||||
|
||||
pPixel += pixelsPerStrideDelta;
|
||||
pPixelEnd += pixelsPerStride;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (pLookup < pLookupEnd)
|
||||
{
|
||||
pPixelRle = pPixelRleStart + *pLookup++;
|
||||
pRleEnd = pPixel;
|
||||
|
||||
while (pPixel < pPixelEnd)
|
||||
{
|
||||
color = *(ushort*)pPixelRle;
|
||||
count = *(1 + (ushort*)pPixelRle);
|
||||
++pPixelRle;
|
||||
|
||||
pRleEnd += count;
|
||||
|
||||
if (color != 0)
|
||||
{
|
||||
color = pColorTable[color >> 10];
|
||||
}
|
||||
|
||||
while (pPixel < pRleEnd)
|
||||
{
|
||||
*pPixel++ = color;
|
||||
}
|
||||
}
|
||||
|
||||
pPixel += pixelsPerStrideDelta;
|
||||
pPixelEnd += pixelsPerStride;
|
||||
}
|
||||
}
|
||||
stream.Close();
|
||||
return new Bitmap(width, height, bytesPerStride, Settings.PixelFormat, (IntPtr)pPixelDataStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Bitmap of index
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap GetGump(int index)
|
||||
{
|
||||
bool patched;
|
||||
return GetGump(index, out patched);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Bitmap of index and if verdata patched
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="patched"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe Bitmap GetGump(int index, out bool patched)
|
||||
{
|
||||
if (m_patched.Contains(index))
|
||||
{
|
||||
patched = (bool)m_patched[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
patched = false;
|
||||
}
|
||||
if (index > m_Cache.Length - 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (m_Removed[index])
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (m_Cache[index] != null)
|
||||
{
|
||||
return m_Cache[index];
|
||||
}
|
||||
int length, extra;
|
||||
Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (extra == -1)
|
||||
{
|
||||
stream.Close();
|
||||
return null;
|
||||
}
|
||||
if (patched)
|
||||
{
|
||||
m_patched[index] = true;
|
||||
}
|
||||
|
||||
int width = (extra >> 16) & 0xFFFF;
|
||||
int height = extra & 0xFFFF;
|
||||
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var bmp = new Bitmap(width, height, Settings.PixelFormat);
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, Settings.PixelFormat);
|
||||
|
||||
if (m_StreamBuffer == null || m_StreamBuffer.Length < length)
|
||||
{
|
||||
m_StreamBuffer = new byte[length];
|
||||
}
|
||||
stream.Read(m_StreamBuffer, 0, length);
|
||||
|
||||
fixed (byte* data = m_StreamBuffer)
|
||||
{
|
||||
var lookup = (int*)data;
|
||||
var dat = (ushort*)data;
|
||||
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
int count = 0;
|
||||
for (int y = 0; y < height; ++y, line += delta)
|
||||
{
|
||||
count = (*lookup++ * 2);
|
||||
|
||||
ushort* cur = line;
|
||||
ushort* end = line + bd.Width;
|
||||
|
||||
while (cur < end)
|
||||
{
|
||||
ushort color = dat[count++];
|
||||
ushort* next = cur + dat[count++];
|
||||
|
||||
if (color == 0)
|
||||
{
|
||||
cur = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
color ^= 0x8000;
|
||||
while (cur < next)
|
||||
{
|
||||
*cur++ = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bmp.UnlockBits(bd);
|
||||
if (Files.CacheData)
|
||||
{
|
||||
return m_Cache[index] = bmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bmp;
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe void Save(string path)
|
||||
{
|
||||
string idx = Path.Combine(path, "Gumpidx.mul");
|
||||
string mul = Path.Combine(path, "Gumpart.mul");
|
||||
using (
|
||||
FileStream fsidx = new FileStream(idx, FileMode.Create, FileAccess.Write, FileShare.Write),
|
||||
fsmul = new FileStream(mul, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
using (BinaryWriter binidx = new BinaryWriter(fsidx), binmul = new BinaryWriter(fsmul))
|
||||
{
|
||||
for (int index = 0; index < m_Cache.Length; index++)
|
||||
{
|
||||
if (m_Cache[index] == null)
|
||||
{
|
||||
m_Cache[index] = GetGump(index);
|
||||
}
|
||||
|
||||
Bitmap bmp = m_Cache[index];
|
||||
if ((bmp == null) || (m_Removed[index]))
|
||||
{
|
||||
binidx.Write(-1); // lookup
|
||||
binidx.Write(-1); // length
|
||||
binidx.Write(-1); // extra
|
||||
}
|
||||
else
|
||||
{
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, Settings.PixelFormat);
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
|
||||
binidx.Write((int)fsmul.Position); //lookup
|
||||
var length = (int)fsmul.Position;
|
||||
int fill = 0;
|
||||
for (int i = 0; i < bmp.Height; ++i)
|
||||
{
|
||||
binmul.Write(fill);
|
||||
}
|
||||
for (int Y = 0; Y < bmp.Height; ++Y, line += delta)
|
||||
{
|
||||
ushort* cur = line;
|
||||
|
||||
int X = 0;
|
||||
var current = (int)fsmul.Position;
|
||||
fsmul.Seek(length + Y * 4, SeekOrigin.Begin);
|
||||
int offset = (current - length) / 4;
|
||||
binmul.Write(offset);
|
||||
fsmul.Seek(length + offset * 4, SeekOrigin.Begin);
|
||||
|
||||
while (X < bd.Width)
|
||||
{
|
||||
int Run = 1;
|
||||
ushort c = cur[X];
|
||||
while ((X + Run) < bd.Width)
|
||||
{
|
||||
if (c != cur[X + Run])
|
||||
{
|
||||
break;
|
||||
}
|
||||
++Run;
|
||||
}
|
||||
if (c == 0)
|
||||
{
|
||||
binmul.Write(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
binmul.Write((ushort)(c ^ 0x8000));
|
||||
}
|
||||
binmul.Write((short)Run);
|
||||
X += Run;
|
||||
}
|
||||
}
|
||||
length = (int)fsmul.Position - length;
|
||||
binidx.Write(length);
|
||||
binidx.Write((bmp.Width << 16) + bmp.Height);
|
||||
bmp.UnlockBits(bd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
464
Ultima/Hues.cs
Normal file
464
Ultima/Hues.cs
Normal file
@@ -0,0 +1,464 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class Hues
|
||||
{
|
||||
private static int[] m_Header;
|
||||
|
||||
public static Hue[] List { get; private set; }
|
||||
|
||||
static Hues()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads hues.mul and fills <see cref="List" />
|
||||
/// </summary>
|
||||
public static void Initialize()
|
||||
{
|
||||
string path = Files.GetFilePath("hues.mul");
|
||||
int index = 0;
|
||||
|
||||
List = new Hue[3000];
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
int blockCount = (int)fs.Length / 708;
|
||||
|
||||
if (blockCount > 375)
|
||||
{
|
||||
blockCount = 375;
|
||||
}
|
||||
m_Header = new int[blockCount];
|
||||
int structsize = Marshal.SizeOf(typeof(HueDataMul));
|
||||
var buffer = new byte[blockCount * (4 + 8 * structsize)];
|
||||
GCHandle gc = GCHandle.Alloc(buffer, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
fs.Read(buffer, 0, buffer.Length);
|
||||
long currpos = 0;
|
||||
|
||||
for (int i = 0; i < blockCount; ++i)
|
||||
{
|
||||
var ptrheader = new IntPtr((long)gc.AddrOfPinnedObject() + currpos);
|
||||
currpos += 4;
|
||||
m_Header[i] = (int)Marshal.PtrToStructure(ptrheader, typeof(int));
|
||||
|
||||
for (int j = 0; j < 8; ++j, ++index)
|
||||
{
|
||||
var ptr = new IntPtr((long)gc.AddrOfPinnedObject() + currpos);
|
||||
currpos += structsize;
|
||||
var cur = (HueDataMul)Marshal.PtrToStructure(ptr, typeof(HueDataMul));
|
||||
List[index] = new Hue(index, cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
gc.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (; index < List.Length; ++index)
|
||||
{
|
||||
List[index] = new Hue(index);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Save(string path)
|
||||
{
|
||||
string mul = Path.Combine(path, "hues.mul");
|
||||
using (var fsmul = new FileStream(mul, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
using (var binmul = new BinaryWriter(fsmul))
|
||||
{
|
||||
int index = 0;
|
||||
for (int i = 0; i < m_Header.Length; ++i)
|
||||
{
|
||||
binmul.Write(m_Header[i]);
|
||||
for (int j = 0; j < 8; ++j, ++index)
|
||||
{
|
||||
for (int c = 0; c < 32; ++c)
|
||||
{
|
||||
binmul.Write((short)(List[index].Colors[c] ^ 0x8000));
|
||||
}
|
||||
|
||||
binmul.Write((short)(List[index].TableStart ^ 0x8000));
|
||||
binmul.Write((short)(List[index].TableEnd ^ 0x8000));
|
||||
var b = new byte[20];
|
||||
if (List[index].Name != null)
|
||||
{
|
||||
byte[] bb = Encoding.Default.GetBytes(List[index].Name);
|
||||
if (bb.Length > 20)
|
||||
{
|
||||
Array.Resize(ref bb, 20);
|
||||
}
|
||||
bb.CopyTo(b, 0);
|
||||
}
|
||||
binmul.Write(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see cref="Hue" />
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static Hue GetHue(int index)
|
||||
{
|
||||
index &= 0x3FFF;
|
||||
|
||||
if (index >= 0 && index < 3000)
|
||||
{
|
||||
return List[index];
|
||||
}
|
||||
|
||||
return List[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts RGB value to Huecolor
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
public static short ColorToHue(Color c)
|
||||
{
|
||||
ushort origred = c.R;
|
||||
ushort origgreen = c.G;
|
||||
ushort origblue = c.B;
|
||||
const double scale = 31.0 / 255;
|
||||
var newred = (ushort)(origred * scale);
|
||||
if (newred == 0 && origred != 0)
|
||||
{
|
||||
newred = 1;
|
||||
}
|
||||
var newgreen = (ushort)(origgreen * scale);
|
||||
if (newgreen == 0 && origgreen != 0)
|
||||
{
|
||||
newgreen = 1;
|
||||
}
|
||||
var newblue = (ushort)(origblue * scale);
|
||||
if (newblue == 0 && origblue != 0)
|
||||
{
|
||||
newblue = 1;
|
||||
}
|
||||
|
||||
return (short)((newred << 10) | (newgreen << 5) | (newblue));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts Huecolor to RGBColor
|
||||
/// </summary>
|
||||
/// <param name="hue"></param>
|
||||
/// <returns></returns>
|
||||
public static Color HueToColor(short hue)
|
||||
{
|
||||
const int scale = 255 / 31;
|
||||
return Color.FromArgb((((hue & 0x7c00) >> 10) * scale), (((hue & 0x3e0) >> 5) * scale), ((hue & 0x1f) * scale));
|
||||
}
|
||||
|
||||
public static int HueToColorR(short hue)
|
||||
{
|
||||
return (((hue & 0x7c00) >> 10) * (255 / 31));
|
||||
}
|
||||
|
||||
public static int HueToColorG(short hue)
|
||||
{
|
||||
return (((hue & 0x3e0) >> 5) * (255 / 31));
|
||||
}
|
||||
|
||||
public static int HueToColorB(short hue)
|
||||
{
|
||||
return ((hue & 0x1f) * (255 / 31));
|
||||
}
|
||||
|
||||
public static unsafe void ApplyTo(Bitmap bmp, short[] Colors, bool onlyHueGrayPixels)
|
||||
{
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, Settings.PixelFormat);
|
||||
|
||||
int stride = bd.Stride >> 1;
|
||||
int width = bd.Width;
|
||||
int height = bd.Height;
|
||||
int delta = stride - width;
|
||||
|
||||
var pBuffer = (ushort*)bd.Scan0;
|
||||
ushort* pLineEnd = pBuffer + width;
|
||||
ushort* pImageEnd = pBuffer + (stride * height);
|
||||
|
||||
if (onlyHueGrayPixels)
|
||||
{
|
||||
int c;
|
||||
int r;
|
||||
int g;
|
||||
int b;
|
||||
|
||||
while (pBuffer < pImageEnd)
|
||||
{
|
||||
while (pBuffer < pLineEnd)
|
||||
{
|
||||
c = *pBuffer;
|
||||
if (c != 0)
|
||||
{
|
||||
r = (c >> 10) & 0x1F;
|
||||
g = (c >> 5) & 0x1F;
|
||||
b = c & 0x1F;
|
||||
if (r == g && r == b)
|
||||
{
|
||||
*pBuffer = (ushort)Colors[(c >> 10) & 0x1F];
|
||||
}
|
||||
}
|
||||
++pBuffer;
|
||||
}
|
||||
|
||||
pBuffer += delta;
|
||||
pLineEnd += stride;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (pBuffer < pImageEnd)
|
||||
{
|
||||
while (pBuffer < pLineEnd)
|
||||
{
|
||||
if (*pBuffer != 0)
|
||||
{
|
||||
*pBuffer = (ushort)Colors[(*pBuffer >> 10) & 0x1F];
|
||||
}
|
||||
++pBuffer;
|
||||
}
|
||||
|
||||
pBuffer += delta;
|
||||
pLineEnd += stride;
|
||||
}
|
||||
}
|
||||
|
||||
bmp.UnlockBits(bd);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Hue
|
||||
{
|
||||
public int Index { get; private set; }
|
||||
public short[] Colors { get; set; }
|
||||
public string Name { get; set; }
|
||||
public short TableStart { get; set; }
|
||||
public short TableEnd { get; set; }
|
||||
|
||||
public Hue(int index)
|
||||
{
|
||||
Name = "Null";
|
||||
Index = index;
|
||||
Colors = new short[32];
|
||||
TableStart = 0;
|
||||
TableEnd = 0;
|
||||
}
|
||||
|
||||
public Color GetColor(int index)
|
||||
{
|
||||
return Hues.HueToColor(Colors[index]);
|
||||
}
|
||||
|
||||
private static readonly byte[] m_StringBuffer = new byte[20];
|
||||
private static byte[] m_Buffer = new byte[88];
|
||||
|
||||
public Hue(int index, BinaryReader bin)
|
||||
{
|
||||
Index = index;
|
||||
Colors = new short[32];
|
||||
|
||||
m_Buffer = bin.ReadBytes(88);
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* buffer = m_Buffer)
|
||||
{
|
||||
var buf = (ushort*)buffer;
|
||||
for (int i = 0; i < 32; ++i)
|
||||
{
|
||||
Colors[i] = (short)(*buf++ | 0x8000);
|
||||
}
|
||||
TableStart = (short)(*buf++ | 0x8000);
|
||||
TableEnd = (short)(*buf++ | 0x8000);
|
||||
var sbuf = (byte*)buf;
|
||||
int count;
|
||||
for (count = 0; count < 20 && *sbuf != 0; ++count)
|
||||
{
|
||||
m_StringBuffer[count] = *sbuf++;
|
||||
}
|
||||
Name = Encoding.Default.GetString(m_StringBuffer, 0, count);
|
||||
Name = Name.Replace("\n", " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Hue(int index, HueDataMul mulstruct)
|
||||
{
|
||||
Index = index;
|
||||
Colors = new short[32];
|
||||
for (int i = 0; i < 32; ++i)
|
||||
{
|
||||
Colors[i] = (short)(mulstruct.colors[i] | 0x8000);
|
||||
}
|
||||
TableStart = (short)(mulstruct.tablestart | 0x8000);
|
||||
TableEnd = (short)(mulstruct.tableend | 0x8000);
|
||||
Name = NativeMethods.ReadNameString(mulstruct.name, 20);
|
||||
Name = Name.Replace("\n", " ");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies Hue to Bitmap
|
||||
/// </summary>
|
||||
/// <param name="bmp"></param>
|
||||
/// <param name="onlyHueGrayPixels"></param>
|
||||
public unsafe void ApplyTo(Bitmap bmp, bool onlyHueGrayPixels)
|
||||
{
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, Settings.PixelFormat);
|
||||
|
||||
int stride = bd.Stride >> 1;
|
||||
int width = bd.Width;
|
||||
int height = bd.Height;
|
||||
int delta = stride - width;
|
||||
|
||||
var pBuffer = (ushort*)bd.Scan0;
|
||||
ushort* pLineEnd = pBuffer + width;
|
||||
ushort* pImageEnd = pBuffer + (stride * height);
|
||||
|
||||
if (onlyHueGrayPixels)
|
||||
{
|
||||
int c;
|
||||
int r;
|
||||
int g;
|
||||
int b;
|
||||
|
||||
while (pBuffer < pImageEnd)
|
||||
{
|
||||
while (pBuffer < pLineEnd)
|
||||
{
|
||||
c = *pBuffer;
|
||||
if (c != 0)
|
||||
{
|
||||
r = (c >> 10) & 0x1F;
|
||||
g = (c >> 5) & 0x1F;
|
||||
b = c & 0x1F;
|
||||
if (r == g && r == b)
|
||||
{
|
||||
*pBuffer = (ushort)Colors[(c >> 10) & 0x1F];
|
||||
}
|
||||
}
|
||||
++pBuffer;
|
||||
}
|
||||
|
||||
pBuffer += delta;
|
||||
pLineEnd += stride;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (pBuffer < pImageEnd)
|
||||
{
|
||||
while (pBuffer < pLineEnd)
|
||||
{
|
||||
if (*pBuffer != 0)
|
||||
{
|
||||
*pBuffer = (ushort)Colors[(*pBuffer >> 10) & 0x1F];
|
||||
}
|
||||
++pBuffer;
|
||||
}
|
||||
|
||||
pBuffer += delta;
|
||||
pLineEnd += stride;
|
||||
}
|
||||
}
|
||||
|
||||
bmp.UnlockBits(bd);
|
||||
}
|
||||
|
||||
public void Export(string FileName)
|
||||
{
|
||||
using (
|
||||
var Tex = new StreamWriter(
|
||||
new FileStream(FileName, FileMode.Create, FileAccess.ReadWrite), Encoding.GetEncoding(1252)))
|
||||
{
|
||||
Tex.WriteLine(Name);
|
||||
Tex.WriteLine(((short)(TableStart ^ 0x8000)).ToString());
|
||||
Tex.WriteLine(((short)(TableEnd ^ 0x8000)).ToString());
|
||||
for (int i = 0; i < Colors.Length; ++i)
|
||||
{
|
||||
Tex.WriteLine(((short)(Colors[i] ^ 0x8000)).ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Import(string FileName)
|
||||
{
|
||||
if (!File.Exists(FileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
using (var sr = new StreamReader(FileName))
|
||||
{
|
||||
string line;
|
||||
int i = -3;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
line = line.Trim();
|
||||
try
|
||||
{
|
||||
if (i >= Colors.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (i == -3)
|
||||
{
|
||||
Name = line;
|
||||
}
|
||||
else if (i == -2)
|
||||
{
|
||||
TableStart = (short)(ushort.Parse(line) | 0x8000);
|
||||
}
|
||||
else if (i == -1)
|
||||
{
|
||||
TableEnd = (short)(ushort.Parse(line) | 0x8000);
|
||||
}
|
||||
else
|
||||
{
|
||||
Colors[i] = (short)(ushort.Parse(line) | 0x8000);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct HueDataMul
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
|
||||
public ushort[] colors;
|
||||
|
||||
public ushort tablestart;
|
||||
public ushort tableend;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
|
||||
public byte[] name;
|
||||
}
|
||||
}
|
||||
253
Ultima/Light.cs
Normal file
253
Ultima/Light.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
#region References
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class Light
|
||||
{
|
||||
private static FileIndex m_FileIndex = new FileIndex("lightidx.mul", "light.mul", 100, -1);
|
||||
private static Bitmap[] m_Cache = new Bitmap[100];
|
||||
private static bool[] m_Removed = new bool[100];
|
||||
private static byte[] m_StreamBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// ReReads light.mul
|
||||
/// </summary>
|
||||
public static void Reload()
|
||||
{
|
||||
m_FileIndex = new FileIndex("lightidx.mul", "light.mul", 100, -1);
|
||||
m_Cache = new Bitmap[100];
|
||||
m_Removed = new bool[100];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets count of definied lights
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static int GetCount()
|
||||
{
|
||||
string idxPath = Files.GetFilePath("lightidx.mul");
|
||||
if (idxPath == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
using (var index = new FileStream(idxPath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
return (int)(index.Length / 12);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if given index is valid
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TestLight(int index)
|
||||
{
|
||||
if (m_Removed[index])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_Cache[index] != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int length, extra;
|
||||
bool patched;
|
||||
|
||||
Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
stream.Close();
|
||||
int width = (extra & 0xFFFF);
|
||||
int height = ((extra >> 16) & 0xFFFF);
|
||||
if ((width > 0) && (height > 0))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes Light <see cref="m_Removed" />
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
public static void Remove(int index)
|
||||
{
|
||||
m_Removed[index] = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces Light
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="bmp"></param>
|
||||
public static void Replace(int index, Bitmap bmp)
|
||||
{
|
||||
m_Cache[index] = bmp;
|
||||
m_Removed[index] = false;
|
||||
}
|
||||
|
||||
public static byte[] GetRawLight(int index, out int width, out int height)
|
||||
{
|
||||
width = 0;
|
||||
height = 0;
|
||||
if (m_Removed[index])
|
||||
{
|
||||
return null;
|
||||
}
|
||||
int length, extra;
|
||||
bool patched;
|
||||
|
||||
Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
width = (extra & 0xFFFF);
|
||||
height = ((extra >> 16) & 0xFFFF);
|
||||
var buffer = new byte[length];
|
||||
stream.Read(buffer, 0, length);
|
||||
stream.Close();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Bitmap of given index
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe Bitmap GetLight(int index)
|
||||
{
|
||||
if (m_Removed[index])
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (m_Cache[index] != null)
|
||||
{
|
||||
return m_Cache[index];
|
||||
}
|
||||
|
||||
int length, extra;
|
||||
bool patched;
|
||||
|
||||
Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int width = (extra & 0xFFFF);
|
||||
int height = ((extra >> 16) & 0xFFFF);
|
||||
|
||||
if (m_StreamBuffer == null || m_StreamBuffer.Length < length)
|
||||
{
|
||||
m_StreamBuffer = new byte[length];
|
||||
}
|
||||
stream.Read(m_StreamBuffer, 0, length);
|
||||
|
||||
var bmp = new Bitmap(width, height, Settings.PixelFormat);
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, Settings.PixelFormat);
|
||||
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
|
||||
fixed (byte* data = m_StreamBuffer)
|
||||
{
|
||||
var bindat = (sbyte*)data;
|
||||
for (int y = 0; y < height; ++y, line += delta)
|
||||
{
|
||||
ushort* cur = line;
|
||||
ushort* end = cur + width;
|
||||
|
||||
while (cur < end)
|
||||
{
|
||||
sbyte value = *bindat++;
|
||||
*cur++ = (ushort)(((0x1f + value) << 10) + ((0x1F + value) << 5) + (0x1F + value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bmp.UnlockBits(bd);
|
||||
stream.Close();
|
||||
if (!Files.CacheData)
|
||||
{
|
||||
return m_Cache[index] = bmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bmp;
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe void Save(string path)
|
||||
{
|
||||
string idx = Path.Combine(path, "lightidx.mul");
|
||||
string mul = Path.Combine(path, "light.mul");
|
||||
using (
|
||||
FileStream fsidx = new FileStream(idx, FileMode.Create, FileAccess.Write, FileShare.Write),
|
||||
fsmul = new FileStream(mul, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
using (BinaryWriter binidx = new BinaryWriter(fsidx), binmul = new BinaryWriter(fsmul))
|
||||
{
|
||||
for (int index = 0; index < m_Cache.Length; index++)
|
||||
{
|
||||
if (m_Cache[index] == null)
|
||||
{
|
||||
m_Cache[index] = GetLight(index);
|
||||
}
|
||||
Bitmap bmp = m_Cache[index];
|
||||
|
||||
if ((bmp == null) || (m_Removed[index]))
|
||||
{
|
||||
binidx.Write(-1); // lookup
|
||||
binidx.Write(-1); // length
|
||||
binidx.Write(-1); // extra
|
||||
}
|
||||
else
|
||||
{
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, Settings.PixelFormat);
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
|
||||
binidx.Write((int)fsmul.Position); //lookup
|
||||
var length = (int)fsmul.Position;
|
||||
|
||||
for (int Y = 0; Y < bmp.Height; ++Y, line += delta)
|
||||
{
|
||||
ushort* cur = line;
|
||||
ushort* end = cur + bmp.Width;
|
||||
while (cur < end)
|
||||
{
|
||||
var value = (sbyte)(((*cur++ >> 10) & 0xffff) - 0x1f);
|
||||
if (value > 0) // wtf? but it works...
|
||||
{
|
||||
--value;
|
||||
}
|
||||
binmul.Write(value);
|
||||
}
|
||||
}
|
||||
length = (int)fsmul.Position - length;
|
||||
binidx.Write(length);
|
||||
binidx.Write((bmp.Width << 16) + bmp.Height);
|
||||
bmp.UnlockBits(bd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Ultima/LocationPointer.cs
Normal file
26
Ultima/LocationPointer.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class LocationPointer
|
||||
{
|
||||
public int PointerX { get; set; }
|
||||
public int PointerY { get; set; }
|
||||
public int PointerZ { get; set; }
|
||||
public int PointerF { get; set; }
|
||||
public int SizeX { get; set; }
|
||||
public int SizeY { get; set; }
|
||||
public int SizeZ { get; set; }
|
||||
public int SizeF { get; set; }
|
||||
|
||||
public LocationPointer(int ptrX, int ptrY, int ptrZ, int ptrF, int sizeX, int sizeY, int sizeZ, int sizeF)
|
||||
{
|
||||
PointerX = ptrX;
|
||||
PointerY = ptrY;
|
||||
PointerZ = ptrZ;
|
||||
PointerF = ptrF;
|
||||
SizeX = sizeX;
|
||||
SizeY = sizeY;
|
||||
SizeZ = sizeZ;
|
||||
SizeF = sizeF;
|
||||
}
|
||||
}
|
||||
}
|
||||
951
Ultima/Map.cs
Normal file
951
Ultima/Map.cs
Normal file
@@ -0,0 +1,951 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class Map
|
||||
{
|
||||
private TileMatrix m_Tiles;
|
||||
private readonly int m_FileIndex;
|
||||
private readonly int m_MapID;
|
||||
private int m_Width;
|
||||
private readonly int m_Height;
|
||||
private readonly string m_path;
|
||||
|
||||
private static bool m_UseDiff;
|
||||
|
||||
public static bool UseDiff
|
||||
{
|
||||
get { return m_UseDiff; }
|
||||
set
|
||||
{
|
||||
m_UseDiff = value;
|
||||
Reload();
|
||||
}
|
||||
}
|
||||
|
||||
public static Map Felucca = new Map(0, 0, 6144, 4096);
|
||||
public static Map Trammel = new Map(0, 1, 6144, 4096);
|
||||
public static readonly Map Ilshenar = new Map(2, 2, 2304, 1600);
|
||||
public static readonly Map Malas = new Map(3, 3, 2560, 2048);
|
||||
public static readonly Map Tokuno = new Map(4, 4, 1448, 1448);
|
||||
public static readonly Map TerMur = new Map(5, 5, 1280, 4096);
|
||||
public static Map Custom;
|
||||
|
||||
public static void StartUpSetDiff(bool value)
|
||||
{
|
||||
m_UseDiff = value;
|
||||
}
|
||||
|
||||
public Map(int fileIndex, int mapID, int width, int height)
|
||||
{
|
||||
m_FileIndex = fileIndex;
|
||||
m_MapID = mapID;
|
||||
m_Width = width;
|
||||
m_Height = height;
|
||||
m_path = null;
|
||||
}
|
||||
|
||||
public Map(string path, int fileIndex, int mapID, int width, int height)
|
||||
{
|
||||
m_FileIndex = fileIndex;
|
||||
m_MapID = mapID;
|
||||
m_Width = width;
|
||||
m_Height = height;
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets cache-vars to null
|
||||
/// </summary>
|
||||
public static void Reload()
|
||||
{
|
||||
Felucca.Tiles.Dispose();
|
||||
Trammel.Tiles.Dispose();
|
||||
Ilshenar.Tiles.Dispose();
|
||||
Malas.Tiles.Dispose();
|
||||
Tokuno.Tiles.Dispose();
|
||||
TerMur.Tiles.Dispose();
|
||||
Felucca.Tiles.StaticIndexInit = false;
|
||||
Trammel.Tiles.StaticIndexInit = false;
|
||||
Ilshenar.Tiles.StaticIndexInit = false;
|
||||
Malas.Tiles.StaticIndexInit = false;
|
||||
Tokuno.Tiles.StaticIndexInit = false;
|
||||
TerMur.Tiles.StaticIndexInit = false;
|
||||
Felucca.m_Cache = Trammel.m_Cache = Ilshenar.m_Cache = Malas.m_Cache = Tokuno.m_Cache = TerMur.m_Cache = null;
|
||||
Felucca.m_Tiles = Trammel.m_Tiles = Ilshenar.m_Tiles = Malas.m_Tiles = Tokuno.m_Tiles = TerMur.m_Tiles = null;
|
||||
Felucca.m_Cache_NoStatics =
|
||||
Trammel.m_Cache_NoStatics =
|
||||
Ilshenar.m_Cache_NoStatics = Malas.m_Cache_NoStatics = Tokuno.m_Cache_NoStatics = TerMur.m_Cache_NoStatics = null;
|
||||
Felucca.m_Cache_NoPatch =
|
||||
Trammel.m_Cache_NoPatch =
|
||||
Ilshenar.m_Cache_NoPatch = Malas.m_Cache_NoPatch = Tokuno.m_Cache_NoPatch = TerMur.m_Cache_NoPatch = null;
|
||||
Felucca.m_Cache_NoStatics_NoPatch =
|
||||
Trammel.m_Cache_NoStatics_NoPatch =
|
||||
Ilshenar.m_Cache_NoStatics_NoPatch =
|
||||
Malas.m_Cache_NoStatics_NoPatch = Tokuno.m_Cache_NoStatics_NoPatch = TerMur.m_Cache_NoStatics_NoPatch = null;
|
||||
}
|
||||
|
||||
public void ResetCache()
|
||||
{
|
||||
m_Cache = null;
|
||||
m_Cache_NoPatch = null;
|
||||
m_Cache_NoStatics = null;
|
||||
m_Cache_NoStatics_NoPatch = null;
|
||||
IsCached_Default = false;
|
||||
IsCached_NoStatics = false;
|
||||
IsCached_NoPatch = false;
|
||||
IsCached_NoStatics_NoPatch = false;
|
||||
}
|
||||
|
||||
public bool LoadedMatrix { get { return (m_Tiles != null); } }
|
||||
|
||||
public TileMatrix Tiles
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_Tiles == null)
|
||||
{
|
||||
m_Tiles = new TileMatrix(m_FileIndex, m_MapID, m_Width, m_Height, m_path);
|
||||
}
|
||||
|
||||
return m_Tiles;
|
||||
}
|
||||
}
|
||||
|
||||
public int Width { get { return m_Width; } set { m_Width = value; } }
|
||||
|
||||
public int Height { get { return m_Height; } }
|
||||
|
||||
public int FileIndex { get { return m_FileIndex; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns Bitmap with Statics
|
||||
/// </summary>
|
||||
/// <param name="x">8x8 Block</param>
|
||||
/// <param name="y">8x8 Block</param>
|
||||
/// <param name="width">8x8 Block</param>
|
||||
/// <param name="height">8x8 Block</param>
|
||||
/// <returns></returns>
|
||||
public Bitmap GetImage(int x, int y, int width, int height)
|
||||
{
|
||||
return GetImage(x, y, width, height, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Bitmap
|
||||
/// </summary>
|
||||
/// <param name="x">8x8 Block</param>
|
||||
/// <param name="y">8x8 Block</param>
|
||||
/// <param name="width">8x8 Block</param>
|
||||
/// <param name="height">8x8 Block</param>
|
||||
/// <param name="statics">8x8 Block</param>
|
||||
/// <returns></returns>
|
||||
public Bitmap GetImage(int x, int y, int width, int height, bool statics)
|
||||
{
|
||||
var bmp = new Bitmap(width << 3, height << 3, PixelFormat.Format16bppRgb555);
|
||||
|
||||
GetImage(x, y, width, height, bmp, statics);
|
||||
|
||||
return bmp;
|
||||
}
|
||||
|
||||
private bool IsCached_Default;
|
||||
private bool IsCached_NoStatics;
|
||||
private bool IsCached_NoPatch;
|
||||
private bool IsCached_NoStatics_NoPatch;
|
||||
|
||||
private short[][][] m_Cache;
|
||||
private short[][][] m_Cache_NoStatics;
|
||||
private short[][][] m_Cache_NoPatch;
|
||||
private short[][][] m_Cache_NoStatics_NoPatch;
|
||||
private short[] m_Black;
|
||||
|
||||
public bool IsCached(bool statics)
|
||||
{
|
||||
if (UseDiff)
|
||||
{
|
||||
if (!statics)
|
||||
{
|
||||
return IsCached_NoStatics;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IsCached_Default;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!statics)
|
||||
{
|
||||
return IsCached_NoStatics_NoPatch;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IsCached_NoPatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PreloadRenderedBlock(int x, int y, bool statics)
|
||||
{
|
||||
TileMatrix matrix = Tiles;
|
||||
|
||||
if (x < 0 || y < 0 || x >= matrix.BlockWidth || y >= matrix.BlockHeight)
|
||||
{
|
||||
if (m_Black == null)
|
||||
{
|
||||
m_Black = new short[64];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
short[][][] cache;
|
||||
if (UseDiff)
|
||||
{
|
||||
if (statics)
|
||||
{
|
||||
IsCached_Default = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsCached_NoStatics = true;
|
||||
}
|
||||
cache = (statics ? m_Cache : m_Cache_NoStatics);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (statics)
|
||||
{
|
||||
IsCached_NoPatch = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsCached_NoStatics_NoPatch = true;
|
||||
}
|
||||
cache = (statics ? m_Cache_NoPatch : m_Cache_NoStatics_NoPatch);
|
||||
}
|
||||
|
||||
if (cache == null)
|
||||
{
|
||||
if (UseDiff)
|
||||
{
|
||||
if (statics)
|
||||
{
|
||||
m_Cache = cache = new short[m_Tiles.BlockHeight][][];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Cache_NoStatics = cache = new short[m_Tiles.BlockHeight][][];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (statics)
|
||||
{
|
||||
m_Cache_NoPatch = cache = new short[m_Tiles.BlockHeight][][];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Cache_NoStatics_NoPatch = cache = new short[m_Tiles.BlockHeight][][];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cache[y] == null)
|
||||
{
|
||||
cache[y] = new short[m_Tiles.BlockWidth][];
|
||||
}
|
||||
|
||||
if (cache[y][x] == null)
|
||||
{
|
||||
cache[y][x] = RenderBlock(x, y, statics, UseDiff);
|
||||
}
|
||||
|
||||
m_Tiles.CloseStreams();
|
||||
}
|
||||
|
||||
private short[] GetRenderedBlock(int x, int y, bool statics)
|
||||
{
|
||||
TileMatrix matrix = Tiles;
|
||||
|
||||
if (x < 0 || y < 0 || x >= matrix.BlockWidth || y >= matrix.BlockHeight)
|
||||
{
|
||||
if (m_Black == null)
|
||||
{
|
||||
m_Black = new short[64];
|
||||
}
|
||||
|
||||
return m_Black;
|
||||
}
|
||||
|
||||
short[][][] cache;
|
||||
if (UseDiff)
|
||||
{
|
||||
cache = (statics ? m_Cache : m_Cache_NoStatics);
|
||||
}
|
||||
else
|
||||
{
|
||||
cache = (statics ? m_Cache_NoPatch : m_Cache_NoStatics_NoPatch);
|
||||
}
|
||||
|
||||
if (cache == null)
|
||||
{
|
||||
if (UseDiff)
|
||||
{
|
||||
if (statics)
|
||||
{
|
||||
m_Cache = cache = new short[m_Tiles.BlockHeight][][];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Cache_NoStatics = cache = new short[m_Tiles.BlockHeight][][];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (statics)
|
||||
{
|
||||
m_Cache_NoPatch = cache = new short[m_Tiles.BlockHeight][][];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Cache_NoStatics_NoPatch = cache = new short[m_Tiles.BlockHeight][][];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cache[y] == null)
|
||||
{
|
||||
cache[y] = new short[m_Tiles.BlockWidth][];
|
||||
}
|
||||
|
||||
short[] data = cache[y][x];
|
||||
|
||||
if (data == null)
|
||||
{
|
||||
cache[y][x] = data = RenderBlock(x, y, statics, UseDiff);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private unsafe short[] RenderBlock(int x, int y, bool drawStatics, bool diff)
|
||||
{
|
||||
var data = new short[64];
|
||||
|
||||
Tile[] tiles = m_Tiles.GetLandBlock(x, y, diff);
|
||||
|
||||
fixed (short* pColors = RadarCol.Colors)
|
||||
{
|
||||
fixed (int* pHeight = TileData.HeightTable)
|
||||
{
|
||||
fixed (Tile* ptTiles = tiles)
|
||||
{
|
||||
Tile* pTiles = ptTiles;
|
||||
|
||||
fixed (short* pData = data)
|
||||
{
|
||||
short* pvData = pData;
|
||||
|
||||
if (drawStatics)
|
||||
{
|
||||
HuedTile[][][] statics = drawStatics ? m_Tiles.GetStaticBlock(x, y, diff) : null;
|
||||
|
||||
for (int k = 0, v = 0; k < 8; ++k, v += 8)
|
||||
{
|
||||
for (int p = 0; p < 8; ++p)
|
||||
{
|
||||
int highTop = -255;
|
||||
int highZ = -255;
|
||||
int highID = 0;
|
||||
int highHue = 0;
|
||||
int z, top;
|
||||
bool highstatic = false;
|
||||
|
||||
HuedTile[] curStatics = statics[p][k];
|
||||
|
||||
if (curStatics.Length > 0)
|
||||
{
|
||||
fixed (HuedTile* phtStatics = curStatics)
|
||||
{
|
||||
HuedTile* pStatics = phtStatics;
|
||||
HuedTile* pStaticsEnd = pStatics + curStatics.Length;
|
||||
|
||||
while (pStatics < pStaticsEnd)
|
||||
{
|
||||
z = pStatics->m_Z;
|
||||
top = z + pHeight[pStatics->ID];
|
||||
|
||||
if (top > highTop || (z > highZ && top >= highTop))
|
||||
{
|
||||
highTop = top;
|
||||
highZ = z;
|
||||
highID = pStatics->ID;
|
||||
highHue = pStatics->Hue;
|
||||
highstatic = true;
|
||||
}
|
||||
|
||||
++pStatics;
|
||||
}
|
||||
}
|
||||
}
|
||||
StaticTile[] pending = m_Tiles.GetPendingStatics(x, y);
|
||||
if (pending != null)
|
||||
{
|
||||
foreach (StaticTile penS in pending)
|
||||
{
|
||||
if (penS.m_X == p)
|
||||
{
|
||||
if (penS.m_Y == k)
|
||||
{
|
||||
z = penS.m_Z;
|
||||
top = z + pHeight[penS.m_ID];
|
||||
|
||||
if (top > highTop || (z > highZ && top >= highTop))
|
||||
{
|
||||
highTop = top;
|
||||
highZ = z;
|
||||
highID = penS.m_ID;
|
||||
highHue = penS.m_Hue;
|
||||
highstatic = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
top = pTiles->m_Z;
|
||||
|
||||
if (top > highTop)
|
||||
{
|
||||
highID = pTiles->m_ID;
|
||||
highHue = 0;
|
||||
highstatic = false;
|
||||
}
|
||||
|
||||
if (highHue == 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (highstatic)
|
||||
{
|
||||
*pvData++ = pColors[highID + 0x4000];
|
||||
}
|
||||
else
|
||||
{
|
||||
*pvData++ = pColors[highID];
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
}
|
||||
else
|
||||
{
|
||||
*pvData++ = Hues.GetHue(highHue - 1).Colors[(pColors[highID + 0x4000] >> 10) & 0x1F];
|
||||
}
|
||||
|
||||
++pTiles;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Tile* pEnd = pTiles + 64;
|
||||
|
||||
while (pTiles < pEnd)
|
||||
{
|
||||
*pvData++ = pColors[(pTiles++)->m_ID];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws in given Bitmap with Statics
|
||||
/// </summary>
|
||||
/// <param name="x">8x8 Block</param>
|
||||
/// <param name="y">8x8 Block</param>
|
||||
/// <param name="width">8x8 Block</param>
|
||||
/// <param name="height">8x8 Block</param>
|
||||
/// <param name="bmp">8x8 Block</param>
|
||||
public void GetImage(int x, int y, int width, int height, Bitmap bmp)
|
||||
{
|
||||
GetImage(x, y, width, height, bmp, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws in given Bitmap
|
||||
/// </summary>
|
||||
/// <param name="x">8x8 Block</param>
|
||||
/// <param name="y">8x8 Block</param>
|
||||
/// <param name="width">8x8 Block</param>
|
||||
/// <param name="height">8x8 Block</param>
|
||||
/// <param name="bmp"></param>
|
||||
/// <param name="statics"></param>
|
||||
public unsafe void GetImage(int x, int y, int width, int height, Bitmap bmp, bool statics)
|
||||
{
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, width << 3, height << 3), ImageLockMode.WriteOnly, PixelFormat.Format16bppRgb555);
|
||||
int stride = bd.Stride;
|
||||
int blockStride = stride << 3;
|
||||
|
||||
var pStart = (byte*)bd.Scan0;
|
||||
|
||||
for (int oy = 0, by = y; oy < height; ++oy, ++by, pStart += blockStride)
|
||||
{
|
||||
var pRow0 = (int*)(pStart + (0 * stride));
|
||||
var pRow1 = (int*)(pStart + (1 * stride));
|
||||
var pRow2 = (int*)(pStart + (2 * stride));
|
||||
var pRow3 = (int*)(pStart + (3 * stride));
|
||||
var pRow4 = (int*)(pStart + (4 * stride));
|
||||
var pRow5 = (int*)(pStart + (5 * stride));
|
||||
var pRow6 = (int*)(pStart + (6 * stride));
|
||||
var pRow7 = (int*)(pStart + (7 * stride));
|
||||
|
||||
for (int ox = 0, bx = x; ox < width; ++ox, ++bx)
|
||||
{
|
||||
short[] data = GetRenderedBlock(bx, by, statics);
|
||||
|
||||
fixed (short* pData = data)
|
||||
{
|
||||
var pvData = (int*)pData;
|
||||
|
||||
*pRow0++ = *pvData++;
|
||||
*pRow0++ = *pvData++;
|
||||
*pRow0++ = *pvData++;
|
||||
*pRow0++ = *pvData++;
|
||||
|
||||
*pRow1++ = *pvData++;
|
||||
*pRow1++ = *pvData++;
|
||||
*pRow1++ = *pvData++;
|
||||
*pRow1++ = *pvData++;
|
||||
|
||||
*pRow2++ = *pvData++;
|
||||
*pRow2++ = *pvData++;
|
||||
*pRow2++ = *pvData++;
|
||||
*pRow2++ = *pvData++;
|
||||
|
||||
*pRow3++ = *pvData++;
|
||||
*pRow3++ = *pvData++;
|
||||
*pRow3++ = *pvData++;
|
||||
*pRow3++ = *pvData++;
|
||||
|
||||
*pRow4++ = *pvData++;
|
||||
*pRow4++ = *pvData++;
|
||||
*pRow4++ = *pvData++;
|
||||
*pRow4++ = *pvData++;
|
||||
|
||||
*pRow5++ = *pvData++;
|
||||
*pRow5++ = *pvData++;
|
||||
*pRow5++ = *pvData++;
|
||||
*pRow5++ = *pvData++;
|
||||
|
||||
*pRow6++ = *pvData++;
|
||||
*pRow6++ = *pvData++;
|
||||
*pRow6++ = *pvData++;
|
||||
*pRow6++ = *pvData++;
|
||||
|
||||
*pRow7++ = *pvData++;
|
||||
*pRow7++ = *pvData++;
|
||||
*pRow7++ = *pvData++;
|
||||
*pRow7++ = *pvData++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bmp.UnlockBits(bd);
|
||||
m_Tiles.CloseStreams();
|
||||
}
|
||||
|
||||
public static void DefragStatics(string path, Map map, int width, int height, bool remove)
|
||||
{
|
||||
string indexPath = Files.GetFilePath("staidx{0}.mul", map.FileIndex);
|
||||
FileStream m_Index;
|
||||
BinaryReader m_IndexReader;
|
||||
if (indexPath != null)
|
||||
{
|
||||
m_Index = new FileStream(indexPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
m_IndexReader = new BinaryReader(m_Index);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string staticsPath = Files.GetFilePath("statics{0}.mul", map.FileIndex);
|
||||
|
||||
FileStream m_Statics;
|
||||
BinaryReader m_StaticsReader;
|
||||
if (staticsPath != null)
|
||||
{
|
||||
m_Statics = new FileStream(staticsPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
m_StaticsReader = new BinaryReader(m_Statics);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int blockx = width >> 3;
|
||||
int blocky = height >> 3;
|
||||
|
||||
string idx = Path.Combine(path, String.Format("staidx{0}.mul", map.FileIndex));
|
||||
string mul = Path.Combine(path, String.Format("statics{0}.mul", map.FileIndex));
|
||||
using (
|
||||
FileStream fsidx = new FileStream(idx, FileMode.Create, FileAccess.Write, FileShare.Write),
|
||||
fsmul = new FileStream(mul, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
var memidx = new MemoryStream();
|
||||
var memmul = new MemoryStream();
|
||||
using (BinaryWriter binidx = new BinaryWriter(memidx), binmul = new BinaryWriter(memmul))
|
||||
{
|
||||
for (int x = 0; x < blockx; ++x)
|
||||
{
|
||||
for (int y = 0; y < blocky; ++y)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_IndexReader.BaseStream.Seek(((x * blocky) + y) * 12, SeekOrigin.Begin);
|
||||
int lookup = m_IndexReader.ReadInt32();
|
||||
int length = m_IndexReader.ReadInt32();
|
||||
int extra = m_IndexReader.ReadInt32();
|
||||
|
||||
if (((lookup < 0 || length <= 0) && (!map.Tiles.PendingStatic(x, y))) || (map.Tiles.IsStaticBlockRemoved(x, y)))
|
||||
{
|
||||
binidx.Write(-1); // lookup
|
||||
binidx.Write(-1); // length
|
||||
binidx.Write(-1); // extra
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((lookup >= 0) && (length > 0))
|
||||
{
|
||||
m_Statics.Seek(lookup, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
var fsmullength = (int)binmul.BaseStream.Position;
|
||||
int count = length / 7;
|
||||
if (!remove) //without duplicate remove
|
||||
{
|
||||
bool firstitem = true;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
ushort graphic = m_StaticsReader.ReadUInt16();
|
||||
byte sx = m_StaticsReader.ReadByte();
|
||||
byte sy = m_StaticsReader.ReadByte();
|
||||
sbyte sz = m_StaticsReader.ReadSByte();
|
||||
short shue = m_StaticsReader.ReadInt16();
|
||||
if ((graphic >= 0) && (graphic <= Art.GetMaxItemID()))
|
||||
{
|
||||
if (shue < 0)
|
||||
{
|
||||
shue = 0;
|
||||
}
|
||||
if (firstitem)
|
||||
{
|
||||
binidx.Write((int)binmul.BaseStream.Position); //lookup
|
||||
firstitem = false;
|
||||
}
|
||||
binmul.Write(graphic);
|
||||
binmul.Write(sx);
|
||||
binmul.Write(sy);
|
||||
binmul.Write(sz);
|
||||
binmul.Write(shue);
|
||||
}
|
||||
}
|
||||
StaticTile[] tilelist = map.Tiles.GetPendingStatics(x, y);
|
||||
if (tilelist != null)
|
||||
{
|
||||
for (int i = 0; i < tilelist.Length; ++i)
|
||||
{
|
||||
if ((tilelist[i].m_ID >= 0) && (tilelist[i].m_ID <= Art.GetMaxItemID()))
|
||||
{
|
||||
if (tilelist[i].m_Hue < 0)
|
||||
{
|
||||
tilelist[i].m_Hue = 0;
|
||||
}
|
||||
if (firstitem)
|
||||
{
|
||||
binidx.Write((int)binmul.BaseStream.Position); //lookup
|
||||
firstitem = false;
|
||||
}
|
||||
binmul.Write(tilelist[i].m_ID);
|
||||
binmul.Write(tilelist[i].m_X);
|
||||
binmul.Write(tilelist[i].m_Y);
|
||||
binmul.Write(tilelist[i].m_Z);
|
||||
binmul.Write(tilelist[i].m_Hue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else //with duplicate remove
|
||||
{
|
||||
var tilelist = new StaticTile[count];
|
||||
int j = 0;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var tile = new StaticTile();
|
||||
tile.m_ID = m_StaticsReader.ReadUInt16();
|
||||
tile.m_X = m_StaticsReader.ReadByte();
|
||||
tile.m_Y = m_StaticsReader.ReadByte();
|
||||
tile.m_Z = m_StaticsReader.ReadSByte();
|
||||
tile.m_Hue = m_StaticsReader.ReadInt16();
|
||||
|
||||
if ((tile.m_ID >= 0) && (tile.m_ID <= Art.GetMaxItemID()))
|
||||
{
|
||||
if (tile.m_Hue < 0)
|
||||
{
|
||||
tile.m_Hue = 0;
|
||||
}
|
||||
bool first = true;
|
||||
for (int k = 0; k < j; ++k)
|
||||
{
|
||||
if ((tilelist[k].m_ID == tile.m_ID) && ((tilelist[k].m_X == tile.m_X) && (tilelist[k].m_Y == tile.m_Y)) &&
|
||||
(tilelist[k].m_Z == tile.m_Z) && (tilelist[k].m_Hue == tile.m_Hue))
|
||||
{
|
||||
first = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (first)
|
||||
{
|
||||
tilelist[j] = tile;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (map.Tiles.PendingStatic(x, y))
|
||||
{
|
||||
StaticTile[] pending = map.Tiles.GetPendingStatics(x, y);
|
||||
StaticTile[] old = tilelist;
|
||||
tilelist = new StaticTile[old.Length + pending.Length];
|
||||
old.CopyTo(tilelist, 0);
|
||||
for (int i = 0; i < pending.Length; ++i)
|
||||
{
|
||||
if ((pending[i].m_ID >= 0) && (pending[i].m_ID <= Art.GetMaxItemID()))
|
||||
{
|
||||
if (pending[i].m_Hue < 0)
|
||||
{
|
||||
pending[i].m_Hue = 0;
|
||||
}
|
||||
bool first = true;
|
||||
for (int k = 0; k < j; ++k)
|
||||
{
|
||||
if ((tilelist[k].m_ID == pending[i].m_ID) &&
|
||||
((tilelist[k].m_X == pending[i].m_X) && (tilelist[k].m_Y == pending[i].m_Y)) &&
|
||||
(tilelist[k].m_Z == pending[i].m_Z) && (tilelist[k].m_Hue == pending[i].m_Hue))
|
||||
{
|
||||
first = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (first)
|
||||
{
|
||||
tilelist[j++] = pending[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (j > 0)
|
||||
{
|
||||
binidx.Write((int)binmul.BaseStream.Position); //lookup
|
||||
for (int i = 0; i < j; ++i)
|
||||
{
|
||||
binmul.Write(tilelist[i].m_ID);
|
||||
binmul.Write(tilelist[i].m_X);
|
||||
binmul.Write(tilelist[i].m_Y);
|
||||
binmul.Write(tilelist[i].m_Z);
|
||||
binmul.Write(tilelist[i].m_Hue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fsmullength = (int)binmul.BaseStream.Position - fsmullength;
|
||||
if (fsmullength > 0)
|
||||
{
|
||||
binidx.Write(fsmullength); //length
|
||||
if (extra == -1)
|
||||
{
|
||||
extra = 0;
|
||||
}
|
||||
binidx.Write(extra); //extra
|
||||
}
|
||||
else
|
||||
{
|
||||
binidx.Write(-1); //lookup
|
||||
binidx.Write(-1); //length
|
||||
binidx.Write(-1); //extra
|
||||
}
|
||||
}
|
||||
}
|
||||
catch // fill the rest
|
||||
{
|
||||
binidx.BaseStream.Seek(((x * blocky) + y) * 12, SeekOrigin.Begin);
|
||||
for (; x < blockx; ++x)
|
||||
{
|
||||
for (; y < blocky; ++y)
|
||||
{
|
||||
binidx.Write(-1); //lookup
|
||||
binidx.Write(-1); //length
|
||||
binidx.Write(-1); //extra
|
||||
}
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
memidx.WriteTo(fsidx);
|
||||
memmul.WriteTo(fsmul);
|
||||
}
|
||||
}
|
||||
m_IndexReader.Close();
|
||||
m_StaticsReader.Close();
|
||||
}
|
||||
|
||||
public static void RewriteMap(string path, int map, int width, int height)
|
||||
{
|
||||
string mapPath = Files.GetFilePath("map{0}.mul", map);
|
||||
FileStream m_map;
|
||||
BinaryReader m_mapReader;
|
||||
if (mapPath != null)
|
||||
{
|
||||
m_map = new FileStream(mapPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
m_mapReader = new BinaryReader(m_map);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int blockx = width >> 3;
|
||||
int blocky = height >> 3;
|
||||
|
||||
string mul = Path.Combine(path, String.Format("map{0}.mul", map));
|
||||
using (var fsmul = new FileStream(mul, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
var memmul = new MemoryStream();
|
||||
using (var binmul = new BinaryWriter(memmul))
|
||||
{
|
||||
for (int x = 0; x < blockx; ++x)
|
||||
{
|
||||
for (int y = 0; y < blocky; ++y)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_mapReader.BaseStream.Seek(((x * blocky) + y) * 196, SeekOrigin.Begin);
|
||||
int header = m_mapReader.ReadInt32();
|
||||
binmul.Write(header);
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
short tileid = m_mapReader.ReadInt16();
|
||||
sbyte z = m_mapReader.ReadSByte();
|
||||
if ((tileid < 0) || (tileid >= 0x4000))
|
||||
{
|
||||
tileid = 0;
|
||||
}
|
||||
if (z < -128)
|
||||
{
|
||||
z = -128;
|
||||
}
|
||||
if (z > 127)
|
||||
{
|
||||
z = 127;
|
||||
}
|
||||
binmul.Write(tileid);
|
||||
binmul.Write(z);
|
||||
}
|
||||
}
|
||||
catch //fill rest
|
||||
{
|
||||
binmul.BaseStream.Seek(((x * blocky) + y) * 196, SeekOrigin.Begin);
|
||||
for (; x < blockx; ++x)
|
||||
{
|
||||
for (; y < blocky; ++y)
|
||||
{
|
||||
binmul.Write(0);
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
binmul.Write((short)0);
|
||||
binmul.Write((sbyte)0);
|
||||
}
|
||||
}
|
||||
y = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
memmul.WriteTo(fsmul);
|
||||
}
|
||||
}
|
||||
m_mapReader.Close();
|
||||
}
|
||||
|
||||
public void ReportInvisStatics(string reportfile)
|
||||
{
|
||||
reportfile = Path.Combine(reportfile, String.Format("staticReport-{0}.csv", m_MapID));
|
||||
using (
|
||||
var Tex = new StreamWriter(
|
||||
new FileStream(reportfile, FileMode.Create, FileAccess.ReadWrite), Encoding.GetEncoding(1252)))
|
||||
{
|
||||
Tex.WriteLine("x;y;z;Static");
|
||||
for (int x = 0; x < m_Width; ++x)
|
||||
{
|
||||
for (int y = 0; y < m_Height; ++y)
|
||||
{
|
||||
Tile currtile = Tiles.GetLandTile(x, y);
|
||||
foreach (HuedTile currstatic in Tiles.GetStaticTiles(x, y))
|
||||
{
|
||||
if (currstatic.Z < currtile.Z)
|
||||
{
|
||||
if (TileData.ItemTable[currstatic.ID].Height + currstatic.Z < currtile.Z)
|
||||
{
|
||||
Tex.WriteLine(String.Format("{0};{1};{2};0x{3:X}", x, y, currstatic.Z, currstatic.ID));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ReportInvalidMapIDs(string reportfile)
|
||||
{
|
||||
reportfile = Path.Combine(reportfile, String.Format("ReportInvalidMapIDs-{0}.csv", m_MapID));
|
||||
using (
|
||||
var Tex = new StreamWriter(
|
||||
new FileStream(reportfile, FileMode.Create, FileAccess.ReadWrite), Encoding.GetEncoding(1252)))
|
||||
{
|
||||
Tex.WriteLine("x;y;z;Static;LandTile");
|
||||
for (int x = 0; x < m_Width; ++x)
|
||||
{
|
||||
for (int y = 0; y < m_Height; ++y)
|
||||
{
|
||||
Tile currtile = Tiles.GetLandTile(x, y);
|
||||
if (!Art.IsValidLand(currtile.ID))
|
||||
{
|
||||
Tex.WriteLine(String.Format("{0};{1};{2};0;0x{3:X}", x, y, currtile.Z, currtile.ID));
|
||||
}
|
||||
foreach (HuedTile currstatic in Tiles.GetStaticTiles(x, y))
|
||||
{
|
||||
if (!Art.IsValidStatic(currstatic.ID))
|
||||
{
|
||||
Tex.WriteLine(String.Format("{0};{1};{2};0x{3:X};0", x, y, currstatic.Z, currstatic.ID));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
253
Ultima/MultiMap.cs
Normal file
253
Ultima/MultiMap.cs
Normal file
@@ -0,0 +1,253 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class MultiMap
|
||||
{
|
||||
private static byte[] m_StreamBuffer;
|
||||
|
||||
/// <summary>
|
||||
/// Returns Bitmap
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static unsafe Bitmap GetMultiMap()
|
||||
{
|
||||
string path = Files.GetFilePath("Multimap.rle");
|
||||
if (path != null)
|
||||
{
|
||||
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
using (var bin = new BinaryReader(fs))
|
||||
{
|
||||
int width, height;
|
||||
byte pixel;
|
||||
int count;
|
||||
int x, i;
|
||||
x = 0;
|
||||
ushort c = 0;
|
||||
width = bin.ReadInt32();
|
||||
height = bin.ReadInt32();
|
||||
var multimap = new Bitmap(width, height, Settings.PixelFormat);
|
||||
BitmapData bd = multimap.LockBits(
|
||||
new Rectangle(0, 0, multimap.Width, multimap.Height), ImageLockMode.WriteOnly, Settings.PixelFormat);
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
|
||||
ushort* cur = line;
|
||||
var len = (int)(bin.BaseStream.Length - bin.BaseStream.Position);
|
||||
if (m_StreamBuffer == null || m_StreamBuffer.Length < len)
|
||||
{
|
||||
m_StreamBuffer = new byte[len];
|
||||
}
|
||||
bin.Read(m_StreamBuffer, 0, len);
|
||||
int j = 0;
|
||||
while (j != len)
|
||||
{
|
||||
pixel = m_StreamBuffer[j++];
|
||||
count = (pixel & 0x7f);
|
||||
|
||||
if ((pixel & 0x80) != 0)
|
||||
{
|
||||
c = 0x8000; //Color.Black;
|
||||
}
|
||||
else
|
||||
{
|
||||
c = 0xffff; //Color.White;
|
||||
}
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
cur[x++] = c;
|
||||
if (x >= width)
|
||||
{
|
||||
cur += delta;
|
||||
x = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
multimap.UnlockBits(bd);
|
||||
return multimap;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves Bitmap to rle Format
|
||||
/// </summary>
|
||||
/// <param name="image"></param>
|
||||
/// <param name="bin"></param>
|
||||
public static unsafe void SaveMultiMap(Bitmap image, BinaryWriter bin)
|
||||
{
|
||||
bin.Write(2560); // width
|
||||
bin.Write(2048); // height
|
||||
byte data = 1;
|
||||
byte mask = 0x0;
|
||||
ushort curcolor = 0;
|
||||
BitmapData bd = image.LockBits(
|
||||
new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, Settings.PixelFormat);
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
ushort* cur = line;
|
||||
curcolor = cur[0]; //init
|
||||
for (int y = 0; y < image.Height; ++y, line += delta)
|
||||
{
|
||||
cur = line;
|
||||
for (int x = 0; x < image.Width; ++x)
|
||||
{
|
||||
ushort c = cur[x];
|
||||
|
||||
if (c == curcolor)
|
||||
{
|
||||
++data;
|
||||
if (data == 0x7f)
|
||||
{
|
||||
if (curcolor == 0xffff)
|
||||
{
|
||||
mask = 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = 0x80;
|
||||
}
|
||||
data |= mask;
|
||||
bin.Write(data);
|
||||
data = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (curcolor == 0xffff)
|
||||
{
|
||||
mask = 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = 0x80;
|
||||
}
|
||||
data |= mask;
|
||||
bin.Write(data);
|
||||
curcolor = c;
|
||||
data = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (curcolor == 0xffff)
|
||||
{
|
||||
mask = 0x0;
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = 0x80;
|
||||
}
|
||||
data |= mask;
|
||||
bin.Write(data);
|
||||
image.UnlockBits(bd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// reads facet0*.mul into Bitmap
|
||||
/// </summary>
|
||||
/// <param name="id">facet id</param>
|
||||
/// <returns>Bitmap</returns>
|
||||
public static unsafe Bitmap GetFacetImage(int id)
|
||||
{
|
||||
Bitmap bmp;
|
||||
string path = Files.GetFilePath(String.Format("facet0{0}.mul", id));
|
||||
if (path != null)
|
||||
{
|
||||
using (var reader = new BinaryReader(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)))
|
||||
{
|
||||
int width = reader.ReadInt16();
|
||||
int height = reader.ReadInt16();
|
||||
|
||||
bmp = new Bitmap(width, height);
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, Settings.PixelFormat);
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
|
||||
for (int y = 0; y < height; y++, line += delta)
|
||||
{
|
||||
int colorsCount = reader.ReadInt32() / 3;
|
||||
ushort* endline = line + delta;
|
||||
ushort* cur = line;
|
||||
ushort* end;
|
||||
for (int c = 0; c < colorsCount; c++)
|
||||
{
|
||||
byte count = reader.ReadByte();
|
||||
short color = reader.ReadInt16();
|
||||
end = cur + count;
|
||||
while (cur < end)
|
||||
{
|
||||
if (cur > endline)
|
||||
{
|
||||
break;
|
||||
}
|
||||
*cur++ = (ushort)(color ^ 0x8000);
|
||||
}
|
||||
}
|
||||
}
|
||||
bmp.UnlockBits(bd);
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stores Image into facet.mul format
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="sourceBitmap"></param>
|
||||
public static unsafe void SaveFacetImage(string path, Bitmap sourceBitmap)
|
||||
{
|
||||
int width = sourceBitmap.Width;
|
||||
int height = sourceBitmap.Height;
|
||||
|
||||
using (
|
||||
var writer = new BinaryWriter(new FileStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)))
|
||||
{
|
||||
writer.Write((short)width);
|
||||
writer.Write((short)height);
|
||||
BitmapData bd = sourceBitmap.LockBits(
|
||||
new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, Settings.PixelFormat);
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
for (int y = 0; y < height; y++, line += delta)
|
||||
{
|
||||
long pos = writer.BaseStream.Position;
|
||||
writer.Write(0); //bytes count for current line
|
||||
|
||||
int colorsAtLine = 0;
|
||||
int colorsCount = 0;
|
||||
int x = 0;
|
||||
|
||||
while (x < width)
|
||||
{
|
||||
ushort hue = line[x];
|
||||
while (x < width && colorsCount < byte.MaxValue && hue == line[x])
|
||||
{
|
||||
++colorsCount;
|
||||
++x;
|
||||
}
|
||||
writer.Write((byte)colorsCount);
|
||||
writer.Write((ushort)(hue ^ 0x8000));
|
||||
|
||||
colorsAtLine++;
|
||||
colorsCount = 0;
|
||||
}
|
||||
long currpos = writer.BaseStream.Position;
|
||||
writer.BaseStream.Seek(pos, SeekOrigin.Begin);
|
||||
writer.Write(colorsAtLine * 3); //byte count
|
||||
writer.BaseStream.Seek(currpos, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1305
Ultima/Multis.cs
Normal file
1305
Ultima/Multis.cs
Normal file
File diff suppressed because it is too large
Load Diff
89
Ultima/NativeMethods.cs
Normal file
89
Ultima/NativeMethods.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
#region References
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public static class NativeMethods
|
||||
{
|
||||
[DllImport("User32")]
|
||||
public static extern int IsWindow(ClientWindowHandle window);
|
||||
|
||||
[DllImport("User32")]
|
||||
public static extern int GetWindowThreadProcessId(ClientWindowHandle window, ref ClientProcessHandle processID);
|
||||
|
||||
[DllImport("Kernel32")]
|
||||
public static extern unsafe int _lread(SafeFileHandle hFile, void* lpBuffer, int wBytes);
|
||||
|
||||
[DllImport("Kernel32")]
|
||||
public static extern ClientProcessHandle OpenProcess(
|
||||
int desiredAccess, int inheritClientHandle, ClientProcessHandle processID);
|
||||
|
||||
[DllImport("Kernel32")]
|
||||
public static extern int CloseHandle(ClientProcessHandle handle);
|
||||
|
||||
[DllImport("Kernel32")]
|
||||
public static extern unsafe int ReadProcessMemory(
|
||||
ClientProcessHandle process, int baseAddress, void* buffer, int size, ref int op);
|
||||
|
||||
[DllImport("Kernel32")]
|
||||
public static extern unsafe int WriteProcessMemory(
|
||||
ClientProcessHandle process, int baseAddress, void* buffer, int size, int nullMe);
|
||||
|
||||
[DllImport("User32")]
|
||||
public static extern int SetForegroundWindow(ClientWindowHandle hWnd);
|
||||
|
||||
[DllImport("User32")]
|
||||
public static extern int SendMessage(ClientWindowHandle hWnd, int wMsg, int wParam, int lParam);
|
||||
|
||||
[DllImport("User32")]
|
||||
public static extern bool PostMessage(ClientWindowHandle hWnd, int wMsg, int wParam, int lParam);
|
||||
|
||||
[DllImport("User32")]
|
||||
public static extern int OemKeyScan(int wOemChar);
|
||||
|
||||
[DllImport("user32")]
|
||||
public static extern ClientWindowHandle FindWindowA(string lpClassName, string lpWindowName);
|
||||
|
||||
/// <summary>
|
||||
/// Swaps from Big to LittleEndian and vise versa
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <returns></returns>
|
||||
public static short SwapEndian(short x)
|
||||
{
|
||||
var y = (ushort)x;
|
||||
return (short)((y >> 8) | (y << 8));
|
||||
}
|
||||
|
||||
private static byte[] m_StringBuffer;
|
||||
|
||||
public static unsafe string ReadNameString(byte* buffer, int len)
|
||||
{
|
||||
if ((m_StringBuffer == null) || (m_StringBuffer.Length < len))
|
||||
{
|
||||
m_StringBuffer = new byte[20];
|
||||
}
|
||||
int count;
|
||||
for (count = 0; count < len && *buffer != 0; ++count)
|
||||
{
|
||||
m_StringBuffer[count] = *buffer++;
|
||||
}
|
||||
|
||||
return Encoding.Default.GetString(m_StringBuffer, 0, count);
|
||||
}
|
||||
|
||||
public static string ReadNameString(byte[] buffer, int len)
|
||||
{
|
||||
int count;
|
||||
for (count = 0; count < 20 && buffer[count] != 0; ++count)
|
||||
{
|
||||
;
|
||||
}
|
||||
return Encoding.Default.GetString(buffer, 0, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
113
Ultima/ProcessStream.cs
Normal file
113
Ultima/ProcessStream.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.IO;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public abstract unsafe class ProcessStream : Stream
|
||||
{
|
||||
private const int ProcessAllAccess = 0x1F0FFF;
|
||||
|
||||
protected bool m_Open;
|
||||
protected ClientProcessHandle m_Process;
|
||||
|
||||
protected int m_Position;
|
||||
|
||||
public abstract ClientProcessHandle ProcessID { get; }
|
||||
|
||||
public virtual bool BeginAccess()
|
||||
{
|
||||
if (m_Open)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_Process = NativeMethods.OpenProcess(ProcessAllAccess, 0, ProcessID);
|
||||
m_Open = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void EndAccess()
|
||||
{
|
||||
if (!m_Open)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_Process.Close();
|
||||
m_Open = false;
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{ }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
bool end = !BeginAccess();
|
||||
|
||||
int res = 0;
|
||||
|
||||
fixed (byte* p = buffer)
|
||||
{
|
||||
NativeMethods.ReadProcessMemory(m_Process, m_Position, p + offset, count, ref res);
|
||||
}
|
||||
|
||||
m_Position += count;
|
||||
|
||||
if (end)
|
||||
{
|
||||
EndAccess();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
bool end = !BeginAccess();
|
||||
|
||||
fixed (byte* p = buffer)
|
||||
{
|
||||
NativeMethods.WriteProcessMemory(m_Process, m_Position, p + offset, count, 0);
|
||||
}
|
||||
|
||||
m_Position += count;
|
||||
|
||||
if (end)
|
||||
{
|
||||
EndAccess();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead { get { return true; } }
|
||||
public override bool CanWrite { get { return true; } }
|
||||
public override bool CanSeek { get { return true; } }
|
||||
|
||||
public override long Length { get { throw new NotSupportedException(); } }
|
||||
public override long Position { get { return m_Position; } set { m_Position = (int)value; } }
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
switch (origin)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
m_Position = (int)offset;
|
||||
break;
|
||||
case SeekOrigin.Current:
|
||||
m_Position += (int)offset;
|
||||
break;
|
||||
case SeekOrigin.End:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
return m_Position;
|
||||
}
|
||||
}
|
||||
}
|
||||
170
Ultima/RadarCol.cs
Normal file
170
Ultima/RadarCol.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class RadarCol
|
||||
{
|
||||
static RadarCol()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
private static short[] m_Colors;
|
||||
public static short[] Colors { get { return m_Colors; } }
|
||||
|
||||
public static short GetItemColor(int index)
|
||||
{
|
||||
if (index + 0x4000 < m_Colors.Length)
|
||||
{
|
||||
return m_Colors[index + 0x4000];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static short GetLandColor(int index)
|
||||
{
|
||||
if (index < m_Colors.Length)
|
||||
{
|
||||
return m_Colors[index];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void SetItemColor(int index, short value)
|
||||
{
|
||||
m_Colors[index + 0x4000] = value;
|
||||
}
|
||||
|
||||
public static void SetLandColor(int index, short value)
|
||||
{
|
||||
m_Colors[index] = value;
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
string path = Files.GetFilePath("radarcol.mul");
|
||||
if (path != null)
|
||||
{
|
||||
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
m_Colors = new short[fs.Length / 2];
|
||||
GCHandle gc = GCHandle.Alloc(m_Colors, GCHandleType.Pinned);
|
||||
var buffer = new byte[(int)fs.Length];
|
||||
fs.Read(buffer, 0, (int)fs.Length);
|
||||
Marshal.Copy(buffer, 0, gc.AddrOfPinnedObject(), (int)fs.Length);
|
||||
gc.Free();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Colors = new short[0x8000];
|
||||
}
|
||||
}
|
||||
|
||||
public static void Save(string FileName)
|
||||
{
|
||||
using (var fs = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
using (var bin = new BinaryWriter(fs))
|
||||
{
|
||||
for (int i = 0; i < m_Colors.Length; ++i)
|
||||
{
|
||||
bin.Write(m_Colors[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExportToCSV(string FileName)
|
||||
{
|
||||
using (
|
||||
var Tex = new StreamWriter(
|
||||
new FileStream(FileName, FileMode.Create, FileAccess.ReadWrite), Encoding.GetEncoding(1252)))
|
||||
{
|
||||
Tex.WriteLine("ID;Color");
|
||||
|
||||
for (int i = 0; i < m_Colors.Length; ++i)
|
||||
{
|
||||
Tex.WriteLine(String.Format("0x{0:X4};{1}", i, m_Colors[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ImportFromCSV(string FileName)
|
||||
{
|
||||
if (!File.Exists(FileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
using (var sr = new StreamReader(FileName))
|
||||
{
|
||||
string line;
|
||||
int count = 0;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
if ((line = line.Trim()).Length == 0 || line.StartsWith("#"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (line.StartsWith("ID;"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
++count;
|
||||
}
|
||||
m_Colors = new short[count];
|
||||
}
|
||||
using (var sr = new StreamReader(FileName))
|
||||
{
|
||||
string line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
if ((line = line.Trim()).Length == 0 || line.StartsWith("#"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (line.StartsWith("ID;"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
string[] split = line.Split(';');
|
||||
if (split.Length < 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int id = ConvertStringToInt(split[0]);
|
||||
int color = ConvertStringToInt(split[1]);
|
||||
m_Colors[id] = (short)color;
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int ConvertStringToInt(string text)
|
||||
{
|
||||
int result;
|
||||
if (text.Contains("0x"))
|
||||
{
|
||||
string convert = text.Replace("0x", "");
|
||||
int.TryParse(convert, NumberStyles.HexNumber, null, out result);
|
||||
}
|
||||
else
|
||||
{
|
||||
int.TryParse(text, NumberStyles.Integer, null, out result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Ultima/Settings.cs
Normal file
15
Ultima/Settings.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
#region References
|
||||
using System.Drawing.Imaging;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public static class Settings
|
||||
{
|
||||
#if MONO
|
||||
public const PixelFormat PixelFormat = System.Drawing.Imaging.PixelFormat.Format16bppRgb555;
|
||||
#else
|
||||
public const PixelFormat PixelFormat = System.Drawing.Imaging.PixelFormat.Format16bppArgb1555;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
151
Ultima/SkillGroups.cs
Normal file
151
Ultima/SkillGroups.cs
Normal file
@@ -0,0 +1,151 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class SkillGroup
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public SkillGroup(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SkillGroups
|
||||
{
|
||||
public static List<SkillGroup> List { get; private set; }
|
||||
public static List<int> SkillList { get; private set; }
|
||||
private static bool unicode;
|
||||
|
||||
static SkillGroups()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
string path = Files.GetFilePath("skillgrp.mul");
|
||||
|
||||
List = new List<SkillGroup>();
|
||||
SkillList = new List<int>();
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
using (var bin = new BinaryReader(fs))
|
||||
{
|
||||
int start = 4;
|
||||
int strlen = 17;
|
||||
int count = bin.ReadInt32();
|
||||
if (count == -1)
|
||||
{
|
||||
unicode = true;
|
||||
count = bin.ReadInt32();
|
||||
start *= 2;
|
||||
strlen *= 2;
|
||||
}
|
||||
|
||||
List.Add(new SkillGroup("Misc"));
|
||||
for (int i = 0; i < count - 1; ++i)
|
||||
{
|
||||
int strbuild;
|
||||
fs.Seek((start + (i * strlen)), SeekOrigin.Begin);
|
||||
var builder2 = new StringBuilder(17);
|
||||
if (unicode)
|
||||
{
|
||||
while ((strbuild = bin.ReadInt16()) != 0)
|
||||
{
|
||||
builder2.Append((char)strbuild);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((strbuild = bin.ReadByte()) != 0)
|
||||
{
|
||||
builder2.Append((char)strbuild);
|
||||
}
|
||||
}
|
||||
List.Add(new SkillGroup(builder2.ToString()));
|
||||
}
|
||||
fs.Seek((start + ((count - 1) * strlen)), SeekOrigin.Begin);
|
||||
try
|
||||
{
|
||||
while (bin.BaseStream.Length != bin.BaseStream.Position)
|
||||
{
|
||||
SkillList.Add(bin.ReadInt32());
|
||||
}
|
||||
}
|
||||
catch // just for safety
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Save(string path)
|
||||
{
|
||||
string mul = Path.Combine(path, "skillgrp.mul");
|
||||
using (var fs = new FileStream(mul, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
using (var bin = new BinaryWriter(fs))
|
||||
{
|
||||
if (unicode)
|
||||
{
|
||||
bin.Write(-1);
|
||||
}
|
||||
bin.Write(List.Count);
|
||||
|
||||
foreach (SkillGroup group in List)
|
||||
{
|
||||
if (group.Name == "Misc")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
byte[] name;
|
||||
if (unicode)
|
||||
{
|
||||
name = new byte[34];
|
||||
}
|
||||
else
|
||||
{
|
||||
name = new byte[17];
|
||||
}
|
||||
if (group.Name != null)
|
||||
{
|
||||
if (unicode)
|
||||
{
|
||||
byte[] bb = Encoding.Unicode.GetBytes(group.Name);
|
||||
if (bb.Length > 34)
|
||||
{
|
||||
Array.Resize(ref bb, 34);
|
||||
}
|
||||
bb.CopyTo(name, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] bb = Encoding.Default.GetBytes(group.Name);
|
||||
if (bb.Length > 17)
|
||||
{
|
||||
Array.Resize(ref bb, 17);
|
||||
}
|
||||
bb.CopyTo(name, 0);
|
||||
}
|
||||
}
|
||||
bin.Write(name);
|
||||
}
|
||||
foreach (int group in SkillList)
|
||||
{
|
||||
bin.Write(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
169
Ultima/Skills.cs
Normal file
169
Ultima/Skills.cs
Normal file
@@ -0,0 +1,169 @@
|
||||
#region References
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class Skills
|
||||
{
|
||||
private static FileIndex m_FileIndex = new FileIndex("skills.idx", "skills.mul", 16);
|
||||
|
||||
private static List<SkillInfo> m_SkillEntries;
|
||||
|
||||
public static List<SkillInfo> SkillEntries
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_SkillEntries == null)
|
||||
{
|
||||
m_SkillEntries = new List<SkillInfo>();
|
||||
for (int i = 0; i < m_FileIndex.Index.Length; ++i)
|
||||
{
|
||||
SkillInfo info = GetSkill(i);
|
||||
if (info == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_SkillEntries.Add(info);
|
||||
}
|
||||
}
|
||||
return m_SkillEntries;
|
||||
}
|
||||
set { m_SkillEntries = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ReReads skills.mul
|
||||
/// </summary>
|
||||
public static void Reload()
|
||||
{
|
||||
m_FileIndex = new FileIndex("skills.idx", "skills.mul", 16);
|
||||
m_SkillEntries = new List<SkillInfo>();
|
||||
for (int i = 0; i < m_FileIndex.Index.Length; ++i)
|
||||
{
|
||||
SkillInfo info = GetSkill(i);
|
||||
if (info == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_SkillEntries.Add(info);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see cref="SkillInfo" /> of index
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static SkillInfo GetSkill(int index)
|
||||
{
|
||||
int length, extra;
|
||||
bool patched;
|
||||
|
||||
Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var bin = new BinaryReader(stream))
|
||||
{
|
||||
bool action = bin.ReadBoolean();
|
||||
string name = ReadNameString(bin, length - 1);
|
||||
return new SkillInfo(index, name, action, extra);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly byte[] m_StringBuffer = new byte[1024];
|
||||
|
||||
private static string ReadNameString(BinaryReader bin, int length)
|
||||
{
|
||||
bin.Read(m_StringBuffer, 0, length);
|
||||
int count;
|
||||
for (count = 0; count < length && m_StringBuffer[count] != 0; ++count)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
return Encoding.Default.GetString(m_StringBuffer, 0, count);
|
||||
}
|
||||
|
||||
public static void Save(string path)
|
||||
{
|
||||
string idx = Path.Combine(path, "skills.idx");
|
||||
string mul = Path.Combine(path, "skills.mul");
|
||||
using (
|
||||
FileStream fsidx = new FileStream(idx, FileMode.Create, FileAccess.Write, FileShare.Write),
|
||||
fsmul = new FileStream(mul, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
using (BinaryWriter binidx = new BinaryWriter(fsidx), binmul = new BinaryWriter(fsmul))
|
||||
{
|
||||
for (int i = 0; i < m_FileIndex.Index.Length; ++i)
|
||||
{
|
||||
SkillInfo skill = (i < m_SkillEntries.Count) ? m_SkillEntries[i] : null;
|
||||
if (skill == null)
|
||||
{
|
||||
binidx.Write(-1); // lookup
|
||||
binidx.Write(0); // length
|
||||
binidx.Write(0); // extra
|
||||
}
|
||||
else
|
||||
{
|
||||
binidx.Write((int)fsmul.Position); //lookup
|
||||
var length = (int)fsmul.Position;
|
||||
binmul.Write(skill.IsAction);
|
||||
|
||||
byte[] namebytes = Encoding.Default.GetBytes(skill.Name);
|
||||
binmul.Write(namebytes);
|
||||
binmul.Write((byte)0); //nullterminated
|
||||
|
||||
length = (int)fsmul.Position - length;
|
||||
binidx.Write(length);
|
||||
binidx.Write(skill.Extra);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class SkillInfo
|
||||
{
|
||||
private string m_Name;
|
||||
|
||||
public int Index { get; set; }
|
||||
public bool IsAction { get; set; }
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return m_Name; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
m_Name = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Name = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Extra { get; private set; }
|
||||
|
||||
public SkillInfo(int nr, string name, bool action, int extra)
|
||||
{
|
||||
Index = nr;
|
||||
m_Name = name;
|
||||
IsAction = action;
|
||||
Extra = extra;
|
||||
}
|
||||
}
|
||||
}
|
||||
371
Ultima/Sound.cs
Normal file
371
Ultima/Sound.cs
Normal file
@@ -0,0 +1,371 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class UOSound
|
||||
{
|
||||
public string Name;
|
||||
public int ID;
|
||||
public byte[] buffer;
|
||||
|
||||
public UOSound(string name, int id, byte[] buff)
|
||||
{
|
||||
Name = name;
|
||||
ID = id;
|
||||
buffer = buff;
|
||||
}
|
||||
};
|
||||
|
||||
public static class Sounds
|
||||
{
|
||||
private static Dictionary<int, int> m_Translations;
|
||||
private static FileIndex m_FileIndex;
|
||||
private static UOSound[] m_Cache;
|
||||
private static bool[] m_Removed;
|
||||
|
||||
static Sounds()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads Sounds and def
|
||||
/// </summary>
|
||||
public static void Initialize()
|
||||
{
|
||||
m_Cache = new UOSound[0xFFF];
|
||||
m_Removed = new bool[0xFFF];
|
||||
m_FileIndex = new FileIndex("soundidx.mul", "sound.mul", "soundLegacyMUL.uop", 0xFFF, 8, ".dat", -1, false);
|
||||
var reg = new Regex(@"(\d{1,3}) \x7B(\d{1,3})\x7D (\d{1,3})", RegexOptions.Compiled);
|
||||
|
||||
m_Translations = new Dictionary<int, int>();
|
||||
|
||||
string line;
|
||||
string path = Files.GetFilePath("Sound.def");
|
||||
if (path == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
using (var reader = new StreamReader(path))
|
||||
{
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
if (((line = line.Trim()).Length != 0) && !line.StartsWith("#"))
|
||||
{
|
||||
Match match = reg.Match(line);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
m_Translations.Add(int.Parse(match.Groups[1].Value), int.Parse(match.Groups[2].Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see cref="UOSound" /> of ID
|
||||
/// </summary>
|
||||
/// <param name="soundID"></param>
|
||||
/// <returns></returns>
|
||||
public static UOSound GetSound(int soundID)
|
||||
{
|
||||
bool translated;
|
||||
return GetSound(soundID, out translated);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns <see cref="UOSound" /> of ID with bool translated in .def
|
||||
/// </summary>
|
||||
/// <param name="soundID"></param>
|
||||
/// <param name="translated"></param>
|
||||
/// <returns></returns>
|
||||
public static UOSound GetSound(int soundID, out bool translated)
|
||||
{
|
||||
translated = false;
|
||||
if (soundID < 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (m_Cache[soundID] != null)
|
||||
{
|
||||
return m_Cache[soundID];
|
||||
}
|
||||
|
||||
int length, extra;
|
||||
bool patched;
|
||||
Stream stream = m_FileIndex.Seek(soundID, out length, out extra, out patched);
|
||||
|
||||
if ((m_FileIndex.Index[soundID].lookup < 0) || (length <= 0))
|
||||
{
|
||||
if (!m_Translations.TryGetValue(soundID, out soundID))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
translated = true;
|
||||
stream = m_FileIndex.Seek(soundID, out length, out extra, out patched);
|
||||
}
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
length -= 32;
|
||||
int[] waveHeader = WaveHeader(length);
|
||||
|
||||
var stringBuffer = new byte[32];
|
||||
var buffer = new byte[length];
|
||||
|
||||
stream.Read(stringBuffer, 0, 32);
|
||||
stream.Read(buffer, 0, length);
|
||||
stream.Close();
|
||||
|
||||
var resultBuffer = new byte[buffer.Length + (waveHeader.Length << 2)];
|
||||
|
||||
Buffer.BlockCopy(waveHeader, 0, resultBuffer, 0, (waveHeader.Length << 2));
|
||||
Buffer.BlockCopy(buffer, 0, resultBuffer, (waveHeader.Length << 2), buffer.Length);
|
||||
|
||||
string str = Encoding.ASCII.GetString(stringBuffer);
|
||||
// seems that the null terminator's not being properly recognized :/
|
||||
if (str.IndexOf('\0') > 0)
|
||||
{
|
||||
str = str.Substring(0, str.IndexOf('\0'));
|
||||
}
|
||||
var sound = new UOSound(str, soundID, resultBuffer);
|
||||
|
||||
if (Files.CacheData)
|
||||
{
|
||||
if (!translated) // no .def definition
|
||||
{
|
||||
m_Cache[soundID] = sound;
|
||||
}
|
||||
}
|
||||
|
||||
return sound;
|
||||
}
|
||||
|
||||
private static int[] WaveHeader(int length)
|
||||
{
|
||||
/* ====================
|
||||
* = WAVE File layout =
|
||||
* ====================
|
||||
* char[4] = 'RIFF' \
|
||||
* int - chunk size |- Riff Header
|
||||
* char[4] = 'WAVE' /
|
||||
* char[4] = 'fmt ' \
|
||||
* int - chunk size |
|
||||
* short - format |
|
||||
* short - channels |
|
||||
* int - samples p/s|- Format header
|
||||
* int - avg bytes |
|
||||
* short - align |
|
||||
* short - bits p/s /
|
||||
* char[4] - data \
|
||||
* int - chunk size | - Data header
|
||||
* short[..] - data /
|
||||
* ====================
|
||||
* */
|
||||
return new[]
|
||||
{0x46464952, (length + 36), 0x45564157, 0x20746D66, 0x10, 0x010001, 0x5622, 0xAC44, 0x100002, 0x61746164, length};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Soundname and tests if valid
|
||||
/// </summary>
|
||||
/// <param name="soundID"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsValidSound(int soundID, out string name)
|
||||
{
|
||||
name = "";
|
||||
if (soundID < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int length, extra;
|
||||
bool patched;
|
||||
Stream stream = m_FileIndex.Seek(soundID, out length, out extra, out patched);
|
||||
|
||||
if ((m_FileIndex.Index[soundID].lookup < 0) || (length <= 0))
|
||||
{
|
||||
if (!m_Translations.TryGetValue(soundID, out soundID))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
stream = m_FileIndex.Seek(soundID, out length, out extra, out patched);
|
||||
}
|
||||
if (stream == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var stringBuffer = new byte[32];
|
||||
stream.Read(stringBuffer, 0, 32);
|
||||
stream.Close();
|
||||
name = Encoding.ASCII.GetString(stringBuffer); // seems that the null terminator's not being properly recognized :/
|
||||
if (name.IndexOf('\0') > 0)
|
||||
{
|
||||
name = name.Substring(0, name.IndexOf('\0'));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns length of SoundID
|
||||
/// </summary>
|
||||
/// <param name="soundID"></param>
|
||||
/// <returns></returns>
|
||||
public static double GetSoundLength(int soundID)
|
||||
{
|
||||
if (soundID < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
double len;
|
||||
if (m_Cache[soundID] != null)
|
||||
{
|
||||
len = m_Cache[soundID].buffer.Length;
|
||||
len -= 44; //wavheaderlength
|
||||
}
|
||||
else
|
||||
{
|
||||
int length, extra;
|
||||
bool patched;
|
||||
Stream stream = m_FileIndex.Seek(soundID, out length, out extra, out patched);
|
||||
if ((m_FileIndex.Index[soundID].lookup < 0) || (length <= 0))
|
||||
{
|
||||
if (!m_Translations.TryGetValue(soundID, out soundID))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream = m_FileIndex.Seek(soundID, out length, out extra, out patched);
|
||||
}
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
stream.Close();
|
||||
length -= 32; //mulheaderlength
|
||||
len = length;
|
||||
}
|
||||
len /= 0x5622; // Sample Rate
|
||||
len /= 2;
|
||||
return len;
|
||||
}
|
||||
|
||||
public static void Add(int id, string name, string file)
|
||||
{
|
||||
using (var wav = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
var resultBuffer = new byte[wav.Length];
|
||||
wav.Seek(0, SeekOrigin.Begin);
|
||||
wav.Read(resultBuffer, 0, (int)wav.Length);
|
||||
|
||||
m_Cache[id] = new UOSound(name, id, resultBuffer);
|
||||
m_Removed[id] = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Remove(int id)
|
||||
{
|
||||
m_Removed[id] = true;
|
||||
m_Cache[id] = null;
|
||||
}
|
||||
|
||||
public static void Save(string path)
|
||||
{
|
||||
string idx = Path.Combine(path, "soundidx.mul");
|
||||
string mul = Path.Combine(path, "sound.mul");
|
||||
int Headerlength = 44;
|
||||
using (
|
||||
FileStream fsidx = new FileStream(idx, FileMode.Create, FileAccess.Write, FileShare.Write),
|
||||
fsmul = new FileStream(mul, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
using (BinaryWriter binidx = new BinaryWriter(fsidx), binmul = new BinaryWriter(fsmul))
|
||||
{
|
||||
for (int i = 0; i < m_Cache.Length; ++i)
|
||||
{
|
||||
UOSound sound = m_Cache[i];
|
||||
if ((sound == null) && (!m_Removed[i]))
|
||||
{
|
||||
bool trans;
|
||||
sound = GetSound(i, out trans);
|
||||
if (!trans)
|
||||
{
|
||||
m_Cache[i] = sound;
|
||||
}
|
||||
else
|
||||
{
|
||||
sound = null;
|
||||
}
|
||||
}
|
||||
if ((sound == null) || (m_Removed[i]))
|
||||
{
|
||||
binidx.Write(-1); // lookup
|
||||
binidx.Write(-1); // length
|
||||
binidx.Write(-1); // extra
|
||||
}
|
||||
else
|
||||
{
|
||||
binidx.Write((int)fsmul.Position); //lookup
|
||||
var length = (int)fsmul.Position;
|
||||
|
||||
var b = new byte[32];
|
||||
if (sound.Name != null)
|
||||
{
|
||||
byte[] bb = Encoding.Default.GetBytes(sound.Name);
|
||||
if (bb.Length > 32)
|
||||
{
|
||||
Array.Resize(ref bb, 32);
|
||||
}
|
||||
bb.CopyTo(b, 0);
|
||||
}
|
||||
binmul.Write(b);
|
||||
using (var m = new MemoryStream(sound.buffer))
|
||||
{
|
||||
m.Seek(Headerlength, SeekOrigin.Begin);
|
||||
var resultBuffer = new byte[m.Length - Headerlength];
|
||||
m.Read(resultBuffer, 0, (int)m.Length - Headerlength);
|
||||
binmul.Write(resultBuffer);
|
||||
}
|
||||
|
||||
length = (int)fsmul.Position - length;
|
||||
binidx.Write(length);
|
||||
binidx.Write(i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveSoundListToCSV(string FileName)
|
||||
{
|
||||
using (
|
||||
var Tex = new StreamWriter(
|
||||
new FileStream(FileName, FileMode.Create, FileAccess.ReadWrite), Encoding.GetEncoding(1252)))
|
||||
{
|
||||
Tex.WriteLine("ID;Name;Length");
|
||||
string name = "";
|
||||
for (int i = 1; i <= 0xFFF; ++i)
|
||||
{
|
||||
if (IsValidSound(i - 1, out name))
|
||||
{
|
||||
Tex.Write(String.Format("0x{0:X3}", i));
|
||||
Tex.Write(String.Format(";{0}", name));
|
||||
Tex.WriteLine(String.Format(";{0:f}", GetSoundLength(i - 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
249
Ultima/SpeechList.cs
Normal file
249
Ultima/SpeechList.cs
Normal file
@@ -0,0 +1,249 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class SpeechList
|
||||
{
|
||||
public static List<SpeechEntry> Entries { get; set; }
|
||||
|
||||
private static readonly byte[] m_Buffer = new byte[128];
|
||||
|
||||
static SpeechList()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads speech.mul in <see cref="SpeechList.Entries" />
|
||||
/// </summary>
|
||||
public static void Initialize()
|
||||
{
|
||||
string path = Files.GetFilePath("speech.mul");
|
||||
if (path == null)
|
||||
{
|
||||
Entries = new List<SpeechEntry>(0);
|
||||
return;
|
||||
}
|
||||
Entries = new List<SpeechEntry>();
|
||||
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
var buffer = new byte[fs.Length];
|
||||
unsafe
|
||||
{
|
||||
int order = 0;
|
||||
fs.Read(buffer, 0, buffer.Length);
|
||||
fixed (byte* data = buffer)
|
||||
{
|
||||
byte* bindat = data;
|
||||
byte* bindatend = bindat + buffer.Length;
|
||||
|
||||
while (bindat != bindatend)
|
||||
{
|
||||
var id = (short)((*bindat++ >> 8) | (*bindat++)); //Swapped Endian
|
||||
var length = (short)((*bindat++ >> 8) | (*bindat++));
|
||||
if (length > 128)
|
||||
{
|
||||
length = 128;
|
||||
}
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
m_Buffer[i] = *bindat++;
|
||||
}
|
||||
string keyword = Encoding.UTF8.GetString(m_Buffer, 0, length);
|
||||
Entries.Add(new SpeechEntry(id, keyword, order));
|
||||
++order;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves speech.mul to <see cref="FileName" />
|
||||
/// </summary>
|
||||
/// <param name="FileName"></param>
|
||||
public static void SaveSpeechList(string FileName)
|
||||
{
|
||||
Entries.Sort(new OrderComparer());
|
||||
using (var fs = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
using (var bin = new BinaryWriter(fs))
|
||||
{
|
||||
foreach (SpeechEntry entry in Entries)
|
||||
{
|
||||
bin.Write(NativeMethods.SwapEndian(entry.ID));
|
||||
byte[] utf8String = Encoding.UTF8.GetBytes(entry.KeyWord);
|
||||
var length = (short)utf8String.Length;
|
||||
bin.Write(NativeMethods.SwapEndian(length));
|
||||
bin.Write(utf8String);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExportToCSV(string FileName)
|
||||
{
|
||||
using (var Tex = new StreamWriter(new FileStream(FileName, FileMode.Create, FileAccess.ReadWrite), Encoding.Unicode))
|
||||
{
|
||||
Tex.WriteLine("Order;ID;KeyWord");
|
||||
foreach (SpeechEntry entry in Entries)
|
||||
{
|
||||
Tex.WriteLine(String.Format("{0};{1};{2}", entry.Order, entry.ID, entry.KeyWord));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ImportFromCSV(string FileName)
|
||||
{
|
||||
Entries = new List<SpeechEntry>(0);
|
||||
if (!File.Exists(FileName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
using (var sr = new StreamReader(FileName))
|
||||
{
|
||||
string line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
if ((line = line.Trim()).Length == 0 || line.StartsWith("#"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ((line.Contains("Order")) && (line.Contains("KeyWord")))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
string[] split = line.Split(';');
|
||||
if (split.Length < 3)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int order = ConvertStringToInt(split[0]);
|
||||
int id = ConvertStringToInt(split[1]);
|
||||
string word = split[2];
|
||||
word = word.Replace("\"", "");
|
||||
Entries.Add(new SpeechEntry((short)id, word, order));
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int ConvertStringToInt(string text)
|
||||
{
|
||||
int result;
|
||||
if (text.Contains("0x"))
|
||||
{
|
||||
string convert = text.Replace("0x", "");
|
||||
int.TryParse(convert, NumberStyles.HexNumber, null, out result);
|
||||
}
|
||||
else
|
||||
{
|
||||
int.TryParse(text, NumberStyles.Integer, null, out result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#region SortComparer
|
||||
public class IDComparer : IComparer<SpeechEntry>
|
||||
{
|
||||
private readonly bool m_desc;
|
||||
|
||||
public IDComparer(bool desc)
|
||||
{
|
||||
m_desc = desc;
|
||||
}
|
||||
|
||||
public int Compare(SpeechEntry objA, SpeechEntry objB)
|
||||
{
|
||||
if (objA.ID == objB.ID)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (m_desc)
|
||||
{
|
||||
return (objA.ID < objB.ID) ? 1 : -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (objA.ID < objB.ID) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class KeyWordComparer : IComparer<SpeechEntry>
|
||||
{
|
||||
private readonly bool m_desc;
|
||||
|
||||
public KeyWordComparer(bool desc)
|
||||
{
|
||||
m_desc = desc;
|
||||
}
|
||||
|
||||
public int Compare(SpeechEntry objA, SpeechEntry objB)
|
||||
{
|
||||
if (m_desc)
|
||||
{
|
||||
return String.Compare(objB.KeyWord, objA.KeyWord);
|
||||
}
|
||||
else
|
||||
{
|
||||
return String.Compare(objA.KeyWord, objB.KeyWord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class OrderComparer : IComparer<SpeechEntry>
|
||||
{
|
||||
public int Compare(SpeechEntry objA, SpeechEntry objB)
|
||||
{
|
||||
if (objA.Order == objB.Order)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (objA.Order < objB.Order) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public sealed class SpeechEntry
|
||||
{
|
||||
public short ID { get; set; }
|
||||
public string KeyWord { get; set; }
|
||||
|
||||
[Browsable(false)]
|
||||
public int Order { get; private set; }
|
||||
|
||||
public SpeechEntry(short id, string keyword, int order)
|
||||
{
|
||||
ID = id;
|
||||
KeyWord = keyword;
|
||||
Order = order;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct SpeechMul
|
||||
{
|
||||
public short id;
|
||||
public short length;
|
||||
public byte[] keyword;
|
||||
}
|
||||
}
|
||||
100
Ultima/StringEntry.cs
Normal file
100
Ultima/StringEntry.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class StringEntry
|
||||
{
|
||||
[Flags]
|
||||
public enum CliLocFlag
|
||||
{
|
||||
Original = 0x0,
|
||||
Custom = 0x1,
|
||||
Modified = 0x2
|
||||
}
|
||||
|
||||
private string m_Text;
|
||||
|
||||
public int Number { get; private set; }
|
||||
|
||||
public string Text
|
||||
{
|
||||
get { return m_Text; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
m_Text = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Text = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CliLocFlag Flag { get; set; }
|
||||
|
||||
public StringEntry(int number, string text, byte flag)
|
||||
{
|
||||
Number = number;
|
||||
m_Text = text;
|
||||
Flag = (CliLocFlag)flag;
|
||||
}
|
||||
|
||||
public StringEntry(int number, string text, CliLocFlag flag)
|
||||
{
|
||||
Number = number;
|
||||
m_Text = text;
|
||||
Flag = flag;
|
||||
}
|
||||
|
||||
// Razor
|
||||
private static readonly Regex m_RegEx = new Regex(
|
||||
@"~(\d+)[_\w]+~",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.CultureInvariant);
|
||||
|
||||
private string m_FmtTxt;
|
||||
private static readonly object[] m_Args = new object[] {"", "", "", "", "", "", "", "", "", "", ""};
|
||||
|
||||
public string Format(params object[] args)
|
||||
{
|
||||
if (m_FmtTxt == null)
|
||||
{
|
||||
m_FmtTxt = m_RegEx.Replace(m_Text, @"{$1}");
|
||||
}
|
||||
for (int i = 0; i < args.Length && i < 10; i++)
|
||||
{
|
||||
m_Args[i + 1] = args[i];
|
||||
}
|
||||
return String.Format(m_FmtTxt, m_Args);
|
||||
}
|
||||
|
||||
public string SplitFormat(string argstr)
|
||||
{
|
||||
if (m_FmtTxt == null)
|
||||
{
|
||||
m_FmtTxt = m_RegEx.Replace(m_Text, @"{$1}");
|
||||
}
|
||||
string[] args = argstr.Split('\t'); // adds an extra on to the args array
|
||||
for (int i = 0; i < args.Length && i < 10; i++)
|
||||
{
|
||||
m_Args[i + 1] = args[i];
|
||||
}
|
||||
return String.Format(m_FmtTxt, m_Args);
|
||||
/*
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append( m_FmtTxt );
|
||||
for(int i=0;i<args.Length;i++)
|
||||
{
|
||||
sb.Append( "|" );
|
||||
sb.Append( args[i] == null ? "-null-" : args[i] );
|
||||
}
|
||||
throw new Exception( sb.ToString() );
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
217
Ultima/StringList.cs
Normal file
217
Ultima/StringList.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class StringList
|
||||
{
|
||||
private int m_Header1;
|
||||
private short m_Header2;
|
||||
|
||||
public List<StringEntry> Entries { get; set; }
|
||||
public string Language { get; private set; }
|
||||
|
||||
private Dictionary<int, string> m_StringTable;
|
||||
private Dictionary<int, StringEntry> m_EntryTable;
|
||||
|
||||
private static byte[] m_Buffer = new byte[1024];
|
||||
|
||||
/// <summary>
|
||||
/// Initialize <see cref="StringList" /> of Language
|
||||
/// </summary>
|
||||
/// <param name="language"></param>
|
||||
public StringList(string language)
|
||||
{
|
||||
Language = language;
|
||||
LoadEntry(Files.GetFilePath(String.Format("cliloc.{0}", language)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize <see cref="StringList" /> of Language from path
|
||||
/// </summary>
|
||||
/// <param name="language"></param>
|
||||
/// <param name="path"></param>
|
||||
public StringList(string language, string path)
|
||||
{
|
||||
Language = language;
|
||||
LoadEntry(path);
|
||||
}
|
||||
|
||||
private void LoadEntry(string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
Entries = new List<StringEntry>(0);
|
||||
return;
|
||||
}
|
||||
|
||||
Entries = new List<StringEntry>();
|
||||
m_StringTable = new Dictionary<int, string>();
|
||||
m_EntryTable = new Dictionary<int, StringEntry>();
|
||||
|
||||
using (var bin = new BinaryReader(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)))
|
||||
{
|
||||
m_Header1 = bin.ReadInt32();
|
||||
m_Header2 = bin.ReadInt16();
|
||||
|
||||
while (bin.BaseStream.Length != bin.BaseStream.Position)
|
||||
{
|
||||
int number = bin.ReadInt32();
|
||||
byte flag = bin.ReadByte();
|
||||
int length = bin.ReadInt16();
|
||||
|
||||
if (length > m_Buffer.Length)
|
||||
{
|
||||
m_Buffer = new byte[(length + 1023) & ~1023];
|
||||
}
|
||||
|
||||
bin.Read(m_Buffer, 0, length);
|
||||
string text = Encoding.UTF8.GetString(m_Buffer, 0, length);
|
||||
|
||||
var se = new StringEntry(number, text, flag);
|
||||
Entries.Add(se);
|
||||
|
||||
m_StringTable[number] = text;
|
||||
m_EntryTable[number] = se;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves <see cref="SaveStringList" /> to FileName
|
||||
/// </summary>
|
||||
/// <param name="FileName"></param>
|
||||
public void SaveStringList(string FileName)
|
||||
{
|
||||
using (var fs = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
using (var bin = new BinaryWriter(fs))
|
||||
{
|
||||
bin.Write(m_Header1);
|
||||
bin.Write(m_Header2);
|
||||
Entries.Sort(new NumberComparer(false));
|
||||
foreach (StringEntry entry in Entries)
|
||||
{
|
||||
bin.Write(entry.Number);
|
||||
bin.Write((byte)entry.Flag);
|
||||
byte[] utf8String = Encoding.UTF8.GetBytes(entry.Text);
|
||||
var length = (ushort)utf8String.Length;
|
||||
bin.Write(length);
|
||||
bin.Write(utf8String);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string GetString(int number)
|
||||
{
|
||||
if (m_StringTable == null || !m_StringTable.ContainsKey(number))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return m_StringTable[number];
|
||||
}
|
||||
|
||||
public StringEntry GetEntry(int number)
|
||||
{
|
||||
if (m_EntryTable == null || !m_EntryTable.ContainsKey(number))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return m_EntryTable[number];
|
||||
}
|
||||
|
||||
#region SortComparer
|
||||
public class NumberComparer : IComparer<StringEntry>
|
||||
{
|
||||
private readonly bool m_desc;
|
||||
|
||||
public NumberComparer(bool desc)
|
||||
{
|
||||
m_desc = desc;
|
||||
}
|
||||
|
||||
public int Compare(StringEntry objA, StringEntry objB)
|
||||
{
|
||||
if (objA.Number == objB.Number)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (m_desc)
|
||||
{
|
||||
return (objA.Number < objB.Number) ? 1 : -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (objA.Number < objB.Number) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class FlagComparer : IComparer<StringEntry>
|
||||
{
|
||||
private readonly bool m_desc;
|
||||
|
||||
public FlagComparer(bool desc)
|
||||
{
|
||||
m_desc = desc;
|
||||
}
|
||||
|
||||
public int Compare(StringEntry objA, StringEntry objB)
|
||||
{
|
||||
if ((byte)objA.Flag == (byte)objB.Flag)
|
||||
{
|
||||
if (objA.Number == objB.Number)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (m_desc)
|
||||
{
|
||||
return (objA.Number < objB.Number) ? 1 : -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (objA.Number < objB.Number) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
else if (m_desc)
|
||||
{
|
||||
return ((byte)objA.Flag < (byte)objB.Flag) ? 1 : -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((byte)objA.Flag < (byte)objB.Flag) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TextComparer : IComparer<StringEntry>
|
||||
{
|
||||
private readonly bool m_desc;
|
||||
|
||||
public TextComparer(bool desc)
|
||||
{
|
||||
m_desc = desc;
|
||||
}
|
||||
|
||||
public int Compare(StringEntry objA, StringEntry objB)
|
||||
{
|
||||
if (m_desc)
|
||||
{
|
||||
return String.Compare(objB.Text, objA.Text);
|
||||
}
|
||||
else
|
||||
{
|
||||
return String.Compare(objA.Text, objB.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
302
Ultima/Textures.cs
Normal file
302
Ultima/Textures.cs
Normal file
@@ -0,0 +1,302 @@
|
||||
#region References
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class Textures
|
||||
{
|
||||
private static FileIndex m_FileIndex = new FileIndex("Texidx.mul", "Texmaps.mul", 0x4000, 10);
|
||||
private static Bitmap[] m_Cache = new Bitmap[0x4000];
|
||||
private static bool[] m_Removed = new bool[0x4000];
|
||||
private static readonly Hashtable m_patched = new Hashtable();
|
||||
|
||||
private static byte[] m_StreamBuffer;
|
||||
|
||||
private struct CheckSums
|
||||
{
|
||||
public byte[] checksum;
|
||||
public int pos;
|
||||
public int length;
|
||||
public int index;
|
||||
}
|
||||
|
||||
private static List<CheckSums> checksums;
|
||||
|
||||
/// <summary>
|
||||
/// ReReads texmaps
|
||||
/// </summary>
|
||||
public static void Reload()
|
||||
{
|
||||
m_FileIndex = new FileIndex("Texidx.mul", "Texmaps.mul", 0x4000, 10);
|
||||
m_Cache = new Bitmap[0x4000];
|
||||
m_Removed = new bool[0x4000];
|
||||
m_patched.Clear();
|
||||
}
|
||||
|
||||
public static int GetIdxLength()
|
||||
{
|
||||
return (int)(m_FileIndex.IdxLength / 12);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes Texture <see cref="m_Removed" />
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
public static void Remove(int index)
|
||||
{
|
||||
m_Removed[index] = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces Texture
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="bmp"></param>
|
||||
public static void Replace(int index, Bitmap bmp)
|
||||
{
|
||||
m_Cache[index] = bmp;
|
||||
m_Removed[index] = false;
|
||||
if (m_patched.Contains(index))
|
||||
{
|
||||
m_patched.Remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if index is valid Texture
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TestTexture(int index)
|
||||
{
|
||||
int length, extra;
|
||||
bool patched;
|
||||
if (m_Removed[index])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_Cache[index] != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool valid = m_FileIndex.Valid(index, out length, out extra, out patched);
|
||||
if ((!valid) || (length == 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Bitmap of Texture
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap GetTexture(int index)
|
||||
{
|
||||
bool patched;
|
||||
return GetTexture(index, out patched);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns Bitmap of Texture with verdata bool
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="patched"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe Bitmap GetTexture(int index, out bool patched)
|
||||
{
|
||||
if (m_patched.Contains(index))
|
||||
{
|
||||
patched = (bool)m_patched[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
patched = false;
|
||||
}
|
||||
if (m_Removed[index])
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (m_Cache[index] != null)
|
||||
{
|
||||
return m_Cache[index];
|
||||
}
|
||||
|
||||
int length, extra;
|
||||
Stream stream = m_FileIndex.Seek(index, out length, out extra, out patched);
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (patched)
|
||||
{
|
||||
m_patched[index] = true;
|
||||
}
|
||||
|
||||
int size = extra == 0 ? 64 : 128;
|
||||
|
||||
var bmp = new Bitmap(size, size, Settings.PixelFormat);
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, size, size), ImageLockMode.WriteOnly, Settings.PixelFormat);
|
||||
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
|
||||
int max = size * size * 2;
|
||||
|
||||
if (m_StreamBuffer == null || m_StreamBuffer.Length < max)
|
||||
{
|
||||
m_StreamBuffer = new byte[max];
|
||||
}
|
||||
stream.Read(m_StreamBuffer, 0, max);
|
||||
|
||||
fixed (byte* data = m_StreamBuffer)
|
||||
{
|
||||
var bindat = (ushort*)data;
|
||||
for (int y = 0; y < size; ++y, line += delta)
|
||||
{
|
||||
ushort* cur = line;
|
||||
ushort* end = cur + size;
|
||||
|
||||
while (cur < end)
|
||||
{
|
||||
*cur++ = (ushort)(*bindat++ ^ 0x8000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bmp.UnlockBits(bd);
|
||||
|
||||
stream.Close();
|
||||
if (!Files.CacheData)
|
||||
{
|
||||
return m_Cache[index] = bmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return bmp;
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe void Save(string path)
|
||||
{
|
||||
string idx = Path.Combine(path, "texidx.mul");
|
||||
string mul = Path.Combine(path, "texmaps.mul");
|
||||
checksums = new List<CheckSums>();
|
||||
using (
|
||||
FileStream fsidx = new FileStream(idx, FileMode.Create, FileAccess.Write, FileShare.Write),
|
||||
fsmul = new FileStream(mul, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
var memidx = new MemoryStream();
|
||||
var memmul = new MemoryStream();
|
||||
using (BinaryWriter binidx = new BinaryWriter(memidx), binmul = new BinaryWriter(memmul))
|
||||
{
|
||||
var sha = new SHA256Managed();
|
||||
//StreamWriter Tex = new StreamWriter(new FileStream("d:/texlog.txt", FileMode.Create, FileAccess.ReadWrite));
|
||||
for (int index = 0; index < GetIdxLength(); ++index)
|
||||
{
|
||||
if (m_Cache[index] == null)
|
||||
{
|
||||
m_Cache[index] = GetTexture(index);
|
||||
}
|
||||
|
||||
Bitmap bmp = m_Cache[index];
|
||||
if ((bmp == null) || (m_Removed[index]))
|
||||
{
|
||||
binidx.Write(-1); // lookup
|
||||
binidx.Write(0); // length
|
||||
binidx.Write(-1); // extra
|
||||
}
|
||||
else
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
bmp.Save(ms, ImageFormat.Bmp);
|
||||
byte[] checksum = sha.ComputeHash(ms.ToArray());
|
||||
CheckSums sum;
|
||||
if (compareSaveImages(checksum, out sum))
|
||||
{
|
||||
binidx.Write(sum.pos); //lookup
|
||||
binidx.Write(sum.length);
|
||||
binidx.Write(0);
|
||||
//Tex.WriteLine(System.String.Format("0x{0:X4} : 0x{1:X4} 0x{2:X4}", index, (int)sum.pos, (int)sum.length));
|
||||
//Tex.WriteLine(System.String.Format("0x{0:X4} -> 0x{1:X4}", sum.index, index));
|
||||
continue;
|
||||
}
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, Settings.PixelFormat);
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
|
||||
binidx.Write((int)binmul.BaseStream.Position); //lookup
|
||||
var length = (int)binmul.BaseStream.Position;
|
||||
|
||||
for (int Y = 0; Y < bmp.Height; ++Y, line += delta)
|
||||
{
|
||||
ushort* cur = line;
|
||||
for (int X = 0; X < bmp.Width; ++X)
|
||||
{
|
||||
binmul.Write((ushort)(cur[X] ^ 0x8000));
|
||||
}
|
||||
}
|
||||
int start = length;
|
||||
length = (int)binmul.BaseStream.Position - length;
|
||||
binidx.Write(length);
|
||||
binidx.Write((bmp.Width == 64 ? 0 : 1));
|
||||
bmp.UnlockBits(bd);
|
||||
var s = new CheckSums
|
||||
{
|
||||
pos = start,
|
||||
length = length,
|
||||
checksum = checksum,
|
||||
index = index
|
||||
};
|
||||
//Tex.WriteLine(System.String.Format("0x{0:X4} : 0x{1:X4} 0x{2:X4}", index, start, length));
|
||||
checksums.Add(s);
|
||||
}
|
||||
}
|
||||
memidx.WriteTo(fsidx);
|
||||
memmul.WriteTo(fsmul);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool compareSaveImages(byte[] newchecksum, out CheckSums sum)
|
||||
{
|
||||
sum = new CheckSums();
|
||||
for (int i = 0; i < checksums.Count; ++i)
|
||||
{
|
||||
byte[] cmp = checksums[i].checksum;
|
||||
if (((cmp == null) || (newchecksum == null)) || (cmp.Length != newchecksum.Length))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool valid = true;
|
||||
for (int j = 0; j < cmp.Length; ++j)
|
||||
{
|
||||
if (cmp[j] != newchecksum[j])
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (valid)
|
||||
{
|
||||
sum = checksums[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
1295
Ultima/TileData.cs
Normal file
1295
Ultima/TileData.cs
Normal file
File diff suppressed because it is too large
Load Diff
139
Ultima/TileList.cs
Normal file
139
Ultima/TileList.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
#region References
|
||||
using System.Collections.Generic;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class HuedTileList
|
||||
{
|
||||
private readonly List<HuedTile> m_Tiles;
|
||||
|
||||
public HuedTileList()
|
||||
{
|
||||
m_Tiles = new List<HuedTile>();
|
||||
}
|
||||
|
||||
public int Count { get { return m_Tiles.Count; } }
|
||||
|
||||
public void Add(ushort id, short hue, sbyte z)
|
||||
{
|
||||
m_Tiles.Add(new HuedTile(id, hue, z));
|
||||
}
|
||||
|
||||
public HuedTile[] ToArray()
|
||||
{
|
||||
var tiles = new HuedTile[Count];
|
||||
|
||||
if (m_Tiles.Count > 0)
|
||||
{
|
||||
m_Tiles.CopyTo(tiles);
|
||||
}
|
||||
m_Tiles.Clear();
|
||||
|
||||
return tiles;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TileList
|
||||
{
|
||||
private readonly List<Tile> m_Tiles;
|
||||
|
||||
public TileList()
|
||||
{
|
||||
m_Tiles = new List<Tile>();
|
||||
}
|
||||
|
||||
public int Count { get { return m_Tiles.Count; } }
|
||||
|
||||
public void Add(ushort id, sbyte z)
|
||||
{
|
||||
m_Tiles.Add(new Tile(id, z));
|
||||
}
|
||||
|
||||
public void Add(ushort id, sbyte z, sbyte flag)
|
||||
{
|
||||
m_Tiles.Add(new Tile(id, z, flag));
|
||||
}
|
||||
|
||||
public Tile[] ToArray()
|
||||
{
|
||||
var tiles = new Tile[Count];
|
||||
if (m_Tiles.Count > 0)
|
||||
{
|
||||
m_Tiles.CopyTo(tiles);
|
||||
}
|
||||
m_Tiles.Clear();
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
public Tile Get(int i)
|
||||
{
|
||||
return m_Tiles[i];
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class MTileList
|
||||
{
|
||||
private readonly List<MTile> m_Tiles;
|
||||
|
||||
public MTileList()
|
||||
{
|
||||
m_Tiles = new List<MTile>();
|
||||
}
|
||||
|
||||
public int Count { get { return m_Tiles.Count; } }
|
||||
|
||||
public void Add(ushort id, sbyte z)
|
||||
{
|
||||
m_Tiles.Add(new MTile(id, z));
|
||||
}
|
||||
|
||||
public void Add(ushort id, sbyte z, TileFlag flag)
|
||||
{
|
||||
m_Tiles.Add(new MTile(id, z, flag));
|
||||
}
|
||||
|
||||
public MTile[] ToArray()
|
||||
{
|
||||
var tiles = new MTile[Count];
|
||||
|
||||
if (m_Tiles.Count > 0)
|
||||
{
|
||||
m_Tiles.CopyTo(tiles);
|
||||
}
|
||||
m_Tiles.Clear();
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
public MTile Get(int i)
|
||||
{
|
||||
return m_Tiles[i];
|
||||
}
|
||||
|
||||
public void Set(int i, ushort id, sbyte z)
|
||||
{
|
||||
if (i < Count)
|
||||
{
|
||||
m_Tiles[i].Set(id, z);
|
||||
}
|
||||
}
|
||||
|
||||
public void Set(int i, ushort id, sbyte z, TileFlag flag)
|
||||
{
|
||||
if (i < Count)
|
||||
{
|
||||
m_Tiles[i].Set(id, z, flag);
|
||||
}
|
||||
}
|
||||
|
||||
public void Remove(int i)
|
||||
{
|
||||
if (i < Count)
|
||||
{
|
||||
m_Tiles.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
902
Ultima/TileMatrix.cs
Normal file
902
Ultima/TileMatrix.cs
Normal file
@@ -0,0 +1,902 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class TileMatrix
|
||||
{
|
||||
private readonly HuedTile[][][][][] m_StaticTiles;
|
||||
private readonly Tile[][][] m_LandTiles;
|
||||
private bool[][] m_RemovedStaticBlock;
|
||||
private List<StaticTile>[][] m_StaticTiles_ToAdd;
|
||||
|
||||
public static Tile[] InvalidLandBlock { get; private set; }
|
||||
public static HuedTile[][][] EmptyStaticBlock { get; private set; }
|
||||
|
||||
private FileStream m_Map;
|
||||
private BinaryReader m_UOPReader;
|
||||
private FileStream m_Statics;
|
||||
private Entry3D[] m_StaticIndex;
|
||||
|
||||
public Entry3D[] StaticIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!StaticIndexInit)
|
||||
{
|
||||
InitStatics();
|
||||
}
|
||||
return m_StaticIndex;
|
||||
}
|
||||
}
|
||||
|
||||
public bool StaticIndexInit;
|
||||
|
||||
public TileMatrixPatch Patch { get; private set; }
|
||||
|
||||
public int BlockWidth { get; private set; }
|
||||
|
||||
public int BlockHeight { get; private set; }
|
||||
|
||||
public int Width { get; private set; }
|
||||
|
||||
public int Height { get; private set; }
|
||||
|
||||
private readonly string mapPath;
|
||||
private readonly string indexPath;
|
||||
private readonly string staticsPath;
|
||||
|
||||
public void CloseStreams()
|
||||
{
|
||||
if (m_Map != null)
|
||||
{
|
||||
m_Map.Close();
|
||||
}
|
||||
|
||||
if (m_UOPReader != null)
|
||||
{
|
||||
m_UOPReader.Close();
|
||||
}
|
||||
|
||||
if (m_Statics != null)
|
||||
{
|
||||
m_Statics.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public TileMatrix(int fileIndex, int mapID, int width, int height, string path)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
BlockWidth = width >> 3;
|
||||
BlockHeight = height >> 3;
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
mapPath = Files.GetFilePath("map{0}.mul", fileIndex);
|
||||
|
||||
if (String.IsNullOrEmpty(mapPath) || !File.Exists(mapPath))
|
||||
{
|
||||
mapPath = Files.GetFilePath("map{0}LegacyMUL.uop", fileIndex);
|
||||
}
|
||||
|
||||
if (mapPath != null && mapPath.EndsWith(".uop"))
|
||||
{
|
||||
IsUOPFormat = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mapPath = Path.Combine(path, String.Format("map{0}.mul", fileIndex));
|
||||
|
||||
if (!File.Exists(mapPath))
|
||||
{
|
||||
mapPath = Path.Combine(path, String.Format("map{0}LegacyMUL.uop", fileIndex));
|
||||
}
|
||||
|
||||
if (!File.Exists(mapPath))
|
||||
{
|
||||
mapPath = null;
|
||||
}
|
||||
else if (mapPath != null && mapPath.EndsWith(".uop"))
|
||||
{
|
||||
IsUOPFormat = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
indexPath = Files.GetFilePath("staidx{0}.mul", fileIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
indexPath = Path.Combine(path, String.Format("staidx{0}.mul", fileIndex));
|
||||
if (!File.Exists(indexPath))
|
||||
{
|
||||
indexPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
staticsPath = Files.GetFilePath("statics{0}.mul", fileIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
staticsPath = Path.Combine(path, String.Format("statics{0}.mul", fileIndex));
|
||||
if (!File.Exists(staticsPath))
|
||||
{
|
||||
staticsPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
EmptyStaticBlock = new HuedTile[8][][];
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
EmptyStaticBlock[i] = new HuedTile[8][];
|
||||
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
EmptyStaticBlock[i][j] = new HuedTile[0];
|
||||
}
|
||||
}
|
||||
|
||||
InvalidLandBlock = new Tile[196];
|
||||
|
||||
m_LandTiles = new Tile[BlockWidth][][];
|
||||
m_StaticTiles = new HuedTile[BlockWidth][][][][];
|
||||
|
||||
Patch = new TileMatrixPatch(this, mapID, path);
|
||||
}
|
||||
|
||||
public void SetStaticBlock(int x, int y, HuedTile[][][] value)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_StaticTiles[x] == null)
|
||||
{
|
||||
m_StaticTiles[x] = new HuedTile[BlockHeight][][][];
|
||||
}
|
||||
|
||||
m_StaticTiles[x][y] = value;
|
||||
}
|
||||
|
||||
public HuedTile[][][] GetStaticBlock(int x, int y)
|
||||
{
|
||||
return GetStaticBlock(x, y, true);
|
||||
}
|
||||
|
||||
public HuedTile[][][] GetStaticBlock(int x, int y, bool patch)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
|
||||
{
|
||||
return EmptyStaticBlock;
|
||||
}
|
||||
|
||||
if (m_StaticTiles[x] == null)
|
||||
{
|
||||
m_StaticTiles[x] = new HuedTile[BlockHeight][][][];
|
||||
}
|
||||
|
||||
HuedTile[][][] tiles = m_StaticTiles[x][y];
|
||||
|
||||
if (tiles == null)
|
||||
{
|
||||
tiles = m_StaticTiles[x][y] = ReadStaticBlock(x, y);
|
||||
}
|
||||
|
||||
if ((Map.UseDiff) && (patch))
|
||||
{
|
||||
if (Patch.StaticBlocksCount > 0)
|
||||
{
|
||||
if (Patch.StaticBlocks[x] != null)
|
||||
{
|
||||
if (Patch.StaticBlocks[x][y] != null)
|
||||
{
|
||||
tiles = Patch.StaticBlocks[x][y];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tiles;
|
||||
}
|
||||
|
||||
public HuedTile[] GetStaticTiles(int x, int y, bool patch)
|
||||
{
|
||||
return GetStaticBlock(x >> 3, y >> 3, patch)[x & 0x7][y & 0x7];
|
||||
}
|
||||
|
||||
public HuedTile[] GetStaticTiles(int x, int y)
|
||||
{
|
||||
return GetStaticBlock(x >> 3, y >> 3)[x & 0x7][y & 0x7];
|
||||
}
|
||||
|
||||
public void SetLandBlock(int x, int y, Tile[] value)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_LandTiles[x] == null)
|
||||
{
|
||||
m_LandTiles[x] = new Tile[BlockHeight][];
|
||||
}
|
||||
|
||||
m_LandTiles[x][y] = value;
|
||||
}
|
||||
|
||||
public Tile[] GetLandBlock(int x, int y)
|
||||
{
|
||||
return GetLandBlock(x, y, true);
|
||||
}
|
||||
|
||||
public Tile[] GetLandBlock(int x, int y, bool patch)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
|
||||
{
|
||||
return InvalidLandBlock;
|
||||
}
|
||||
|
||||
if (m_LandTiles[x] == null)
|
||||
{
|
||||
m_LandTiles[x] = new Tile[BlockHeight][];
|
||||
}
|
||||
|
||||
Tile[] tiles = m_LandTiles[x][y];
|
||||
|
||||
if (tiles == null)
|
||||
{
|
||||
tiles = m_LandTiles[x][y] = ReadLandBlock(x, y);
|
||||
}
|
||||
|
||||
if ((Map.UseDiff) && (patch))
|
||||
{
|
||||
if (Patch.LandBlocksCount > 0)
|
||||
{
|
||||
if (Patch.LandBlocks[x] != null)
|
||||
{
|
||||
if (Patch.LandBlocks[x][y] != null)
|
||||
{
|
||||
tiles = Patch.LandBlocks[x][y];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tiles;
|
||||
}
|
||||
|
||||
public Tile GetLandTile(int x, int y, bool patch)
|
||||
{
|
||||
return GetLandBlock(x >> 3, y >> 3, patch)[((y & 0x7) << 3) + (x & 0x7)];
|
||||
}
|
||||
|
||||
public Tile GetLandTile(int x, int y)
|
||||
{
|
||||
return GetLandBlock(x >> 3, y >> 3)[((y & 0x7) << 3) + (x & 0x7)];
|
||||
}
|
||||
|
||||
private void InitStatics()
|
||||
{
|
||||
m_StaticIndex = new Entry3D[BlockHeight * BlockWidth];
|
||||
if (indexPath == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
using (var index = new FileStream(indexPath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
m_Statics = new FileStream(staticsPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
var count = (int)(index.Length / 12);
|
||||
GCHandle gc = GCHandle.Alloc(m_StaticIndex, GCHandleType.Pinned);
|
||||
var buffer = new byte[index.Length];
|
||||
index.Read(buffer, 0, (int)index.Length);
|
||||
Marshal.Copy(buffer, 0, gc.AddrOfPinnedObject(), (int)Math.Min(index.Length, BlockHeight * BlockWidth * 12));
|
||||
gc.Free();
|
||||
for (var i = (int)Math.Min(index.Length, BlockHeight * BlockWidth); i < BlockHeight * BlockWidth; ++i)
|
||||
{
|
||||
m_StaticIndex[i].lookup = -1;
|
||||
m_StaticIndex[i].length = -1;
|
||||
m_StaticIndex[i].extra = -1;
|
||||
}
|
||||
StaticIndexInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static HuedTileList[][] m_Lists;
|
||||
private static byte[] m_Buffer;
|
||||
|
||||
private unsafe HuedTile[][][] ReadStaticBlock(int x, int y)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!StaticIndexInit)
|
||||
{
|
||||
InitStatics();
|
||||
}
|
||||
if (m_Statics == null || !m_Statics.CanRead || !m_Statics.CanSeek)
|
||||
{
|
||||
if (staticsPath == null)
|
||||
{
|
||||
m_Statics = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Statics = new FileStream(staticsPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
}
|
||||
}
|
||||
if (m_Statics == null)
|
||||
{
|
||||
return EmptyStaticBlock;
|
||||
}
|
||||
|
||||
int lookup = m_StaticIndex[(x * BlockHeight) + y].lookup;
|
||||
int length = m_StaticIndex[(x * BlockHeight) + y].length;
|
||||
|
||||
if (lookup < 0 || length <= 0)
|
||||
{
|
||||
return EmptyStaticBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = length / 7;
|
||||
|
||||
m_Statics.Seek(lookup, SeekOrigin.Begin);
|
||||
|
||||
if (m_Buffer == null || m_Buffer.Length < length)
|
||||
{
|
||||
m_Buffer = new byte[length];
|
||||
}
|
||||
|
||||
GCHandle gc = GCHandle.Alloc(m_Buffer, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
m_Statics.Read(m_Buffer, 0, length);
|
||||
|
||||
if (m_Lists == null)
|
||||
{
|
||||
m_Lists = new HuedTileList[8][];
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
m_Lists[i] = new HuedTileList[8];
|
||||
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
m_Lists[i][j] = new HuedTileList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HuedTileList[][] lists = m_Lists;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var ptr = new IntPtr((long)gc.AddrOfPinnedObject() + i * sizeof(StaticTile));
|
||||
var cur = (StaticTile)Marshal.PtrToStructure(ptr, typeof(StaticTile));
|
||||
lists[cur.m_X & 0x7][cur.m_Y & 0x7].Add(Art.GetLegalItemID(cur.m_ID), cur.m_Hue, cur.m_Z);
|
||||
}
|
||||
|
||||
var tiles = new HuedTile[8][][];
|
||||
|
||||
for (int i = 0; i < 8; ++i)
|
||||
{
|
||||
tiles[i] = new HuedTile[8][];
|
||||
|
||||
for (int j = 0; j < 8; ++j)
|
||||
{
|
||||
tiles[i][j] = lists[i][j].ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
return tiles;
|
||||
}
|
||||
finally
|
||||
{
|
||||
gc.Free();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
//if (m_Statics != null)
|
||||
// m_Statics.Close();
|
||||
}
|
||||
}
|
||||
|
||||
/* UOP map files support code, written by Wyatt (c) www.ruosi.org
|
||||
* It's not possible if some entry has unknown hash. Throwed exception
|
||||
* means that EA changed maps UOPs again.
|
||||
*/
|
||||
|
||||
#region UOP
|
||||
public bool IsUOPFormat { get; set; }
|
||||
public bool IsUOPAlreadyRead { get; set; }
|
||||
|
||||
private struct UOPFile
|
||||
{
|
||||
public readonly long Offset;
|
||||
public readonly int Length;
|
||||
|
||||
public UOPFile(long offset, int length)
|
||||
{
|
||||
Offset = offset;
|
||||
Length = length;
|
||||
}
|
||||
}
|
||||
|
||||
private UOPFile[] UOPFiles { get; set; }
|
||||
private long UOPLength { get { return m_Map.Length; } }
|
||||
|
||||
private void ReadUOPFiles(string pattern)
|
||||
{
|
||||
m_UOPReader = new BinaryReader(m_Map);
|
||||
|
||||
m_UOPReader.BaseStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
if (m_UOPReader.ReadInt32() != 0x50594D)
|
||||
{
|
||||
throw new ArgumentException("Bad UOP file.");
|
||||
}
|
||||
|
||||
m_UOPReader.ReadInt64(); // version + signature
|
||||
long nextBlock = m_UOPReader.ReadInt64();
|
||||
m_UOPReader.ReadInt32(); // block capacity
|
||||
int count = m_UOPReader.ReadInt32();
|
||||
|
||||
UOPFiles = new UOPFile[count];
|
||||
|
||||
var hashes = new Dictionary<ulong, int>();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
string file = string.Format("build/{0}/{1:D8}.dat", pattern, i);
|
||||
ulong hash = FileIndex.HashFileName(file);
|
||||
|
||||
if (!hashes.ContainsKey(hash))
|
||||
{
|
||||
hashes.Add(hash, i);
|
||||
}
|
||||
}
|
||||
|
||||
m_UOPReader.BaseStream.Seek(nextBlock, SeekOrigin.Begin);
|
||||
|
||||
do
|
||||
{
|
||||
int filesCount = m_UOPReader.ReadInt32();
|
||||
nextBlock = m_UOPReader.ReadInt64();
|
||||
|
||||
for (int i = 0; i < filesCount; i++)
|
||||
{
|
||||
long offset = m_UOPReader.ReadInt64();
|
||||
int headerLength = m_UOPReader.ReadInt32();
|
||||
int compressedLength = m_UOPReader.ReadInt32();
|
||||
int decompressedLength = m_UOPReader.ReadInt32();
|
||||
ulong hash = m_UOPReader.ReadUInt64();
|
||||
m_UOPReader.ReadUInt32(); // Adler32
|
||||
short flag = m_UOPReader.ReadInt16();
|
||||
|
||||
int length = flag == 1 ? compressedLength : decompressedLength;
|
||||
|
||||
if (offset == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int idx;
|
||||
if (hashes.TryGetValue(hash, out idx))
|
||||
{
|
||||
if (idx < 0 || idx > UOPFiles.Length)
|
||||
{
|
||||
throw new IndexOutOfRangeException("hashes dictionary and files collection have different count of entries!");
|
||||
}
|
||||
|
||||
UOPFiles[idx] = new UOPFile(offset + headerLength, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format("File with hash 0x{0:X8} was not found in hashes dictionary! EA Mythic changed UOP format!", hash));
|
||||
}
|
||||
}
|
||||
}
|
||||
while (m_UOPReader.BaseStream.Seek(nextBlock, SeekOrigin.Begin) != 0);
|
||||
}
|
||||
|
||||
private long CalculateOffsetFromUOP(long offset)
|
||||
{
|
||||
long pos = 0;
|
||||
|
||||
foreach (UOPFile t in UOPFiles)
|
||||
{
|
||||
long currPos = pos + t.Length;
|
||||
|
||||
if (offset < currPos)
|
||||
{
|
||||
return t.Offset + (offset - pos);
|
||||
}
|
||||
|
||||
pos = currPos;
|
||||
}
|
||||
|
||||
return UOPLength;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private Tile[] ReadLandBlock(int x, int y)
|
||||
{
|
||||
if (m_Map == null || !m_Map.CanRead || !m_Map.CanSeek)
|
||||
{
|
||||
if (mapPath == null)
|
||||
{
|
||||
m_Map = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Map = new FileStream(mapPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
}
|
||||
|
||||
if (IsUOPFormat && mapPath != null && !IsUOPAlreadyRead)
|
||||
{
|
||||
var fi = new FileInfo(mapPath);
|
||||
string uopPattern = fi.Name.Replace(fi.Extension, "").ToLowerInvariant();
|
||||
|
||||
ReadUOPFiles(uopPattern);
|
||||
IsUOPAlreadyRead = true;
|
||||
}
|
||||
}
|
||||
var tiles = new Tile[64];
|
||||
if (m_Map != null)
|
||||
{
|
||||
long offset = ((x * BlockHeight) + y) * 196 + 4;
|
||||
|
||||
if (IsUOPFormat)
|
||||
{
|
||||
offset = CalculateOffsetFromUOP(offset);
|
||||
}
|
||||
|
||||
m_Map.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
GCHandle gc = GCHandle.Alloc(tiles, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
if (m_Buffer == null || m_Buffer.Length < 192)
|
||||
{
|
||||
m_Buffer = new byte[192];
|
||||
}
|
||||
|
||||
m_Map.Read(m_Buffer, 0, 192);
|
||||
|
||||
Marshal.Copy(m_Buffer, 0, gc.AddrOfPinnedObject(), 192);
|
||||
}
|
||||
finally
|
||||
{
|
||||
gc.Free();
|
||||
}
|
||||
//m_Map.Close();
|
||||
}
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
public void RemoveStaticBlock(int blockx, int blocky)
|
||||
{
|
||||
if (m_RemovedStaticBlock == null)
|
||||
{
|
||||
m_RemovedStaticBlock = new bool[BlockWidth][];
|
||||
}
|
||||
if (m_RemovedStaticBlock[blockx] == null)
|
||||
{
|
||||
m_RemovedStaticBlock[blockx] = new bool[BlockHeight];
|
||||
}
|
||||
m_RemovedStaticBlock[blockx][blocky] = true;
|
||||
if (m_StaticTiles[blockx] == null)
|
||||
{
|
||||
m_StaticTiles[blockx] = new HuedTile[BlockHeight][][][];
|
||||
}
|
||||
m_StaticTiles[blockx][blocky] = EmptyStaticBlock;
|
||||
}
|
||||
|
||||
public bool IsStaticBlockRemoved(int blockx, int blocky)
|
||||
{
|
||||
if (m_RemovedStaticBlock == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_RemovedStaticBlock[blockx] == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return m_RemovedStaticBlock[blockx][blocky];
|
||||
}
|
||||
|
||||
public bool PendingStatic(int blockx, int blocky)
|
||||
{
|
||||
if (m_StaticTiles_ToAdd == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_StaticTiles_ToAdd[blocky] == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (m_StaticTiles_ToAdd[blocky][blockx] == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AddPendingStatic(int blockx, int blocky, StaticTile toadd)
|
||||
{
|
||||
if (m_StaticTiles_ToAdd == null)
|
||||
{
|
||||
m_StaticTiles_ToAdd = new List<StaticTile>[BlockHeight][];
|
||||
}
|
||||
if (m_StaticTiles_ToAdd[blocky] == null)
|
||||
{
|
||||
m_StaticTiles_ToAdd[blocky] = new List<StaticTile>[BlockWidth];
|
||||
}
|
||||
if (m_StaticTiles_ToAdd[blocky][blockx] == null)
|
||||
{
|
||||
m_StaticTiles_ToAdd[blocky][blockx] = new List<StaticTile>();
|
||||
}
|
||||
m_StaticTiles_ToAdd[blocky][blockx].Add(toadd);
|
||||
}
|
||||
|
||||
public StaticTile[] GetPendingStatics(int blockx, int blocky)
|
||||
{
|
||||
if (m_StaticTiles_ToAdd == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (m_StaticTiles_ToAdd[blocky] == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (m_StaticTiles_ToAdd[blocky][blockx] == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return m_StaticTiles_ToAdd[blocky][blockx].ToArray();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (m_Map != null)
|
||||
{
|
||||
m_Map.Close();
|
||||
}
|
||||
|
||||
if (m_UOPReader != null)
|
||||
{
|
||||
m_UOPReader.Close();
|
||||
}
|
||||
|
||||
if (m_Statics != null)
|
||||
{
|
||||
m_Statics.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct StaticTile
|
||||
{
|
||||
public ushort m_ID;
|
||||
public byte m_X;
|
||||
public byte m_Y;
|
||||
public sbyte m_Z;
|
||||
public short m_Hue;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct HuedTile
|
||||
{
|
||||
internal sbyte m_Z;
|
||||
internal ushort m_ID;
|
||||
internal int m_Hue;
|
||||
|
||||
public ushort ID { get { return m_ID; } set { m_ID = value; } }
|
||||
public int Hue { get { return m_Hue; } set { m_Hue = value; } }
|
||||
public int Z { get { return m_Z; } set { m_Z = (sbyte)value; } }
|
||||
|
||||
public HuedTile(ushort id, short hue, sbyte z)
|
||||
{
|
||||
m_ID = id;
|
||||
m_Hue = hue;
|
||||
m_Z = z;
|
||||
}
|
||||
|
||||
public void Set(ushort id, short hue, sbyte z)
|
||||
{
|
||||
m_ID = id;
|
||||
m_Hue = hue;
|
||||
m_Z = z;
|
||||
}
|
||||
}
|
||||
|
||||
public struct MTile : IComparable
|
||||
{
|
||||
internal ushort m_ID;
|
||||
internal sbyte m_Z;
|
||||
internal TileFlag m_Flag;
|
||||
internal int m_Solver;
|
||||
|
||||
public ushort ID { get { return m_ID; } }
|
||||
public int Z { get { return m_Z; } set { m_Z = (sbyte)value; } }
|
||||
|
||||
public TileFlag Flag { get { return m_Flag; } set { m_Flag = value; } }
|
||||
public int Solver { get { return m_Solver; } set { m_Solver = value; } }
|
||||
|
||||
public MTile(ushort id, sbyte z)
|
||||
{
|
||||
m_ID = Art.GetLegalItemID(id);
|
||||
m_Z = z;
|
||||
m_Flag = TileFlag.Background;
|
||||
m_Solver = 0;
|
||||
}
|
||||
|
||||
public MTile(ushort id, sbyte z, TileFlag flag)
|
||||
{
|
||||
m_ID = Art.GetLegalItemID(id);
|
||||
m_Z = z;
|
||||
m_Flag = flag;
|
||||
m_Solver = 0;
|
||||
}
|
||||
|
||||
public void Set(ushort id, sbyte z)
|
||||
{
|
||||
m_ID = Art.GetLegalItemID(id);
|
||||
m_Z = z;
|
||||
}
|
||||
|
||||
public void Set(ushort id, sbyte z, TileFlag flag)
|
||||
{
|
||||
m_ID = Art.GetLegalItemID(id);
|
||||
m_Z = z;
|
||||
m_Flag = flag;
|
||||
}
|
||||
|
||||
public int CompareTo(object x)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(x is MTile))
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
var a = (MTile)x;
|
||||
|
||||
ItemData ourData = TileData.ItemTable[m_ID];
|
||||
ItemData theirData = TileData.ItemTable[a.ID];
|
||||
|
||||
int ourTreshold = 0;
|
||||
if (ourData.Height > 0)
|
||||
{
|
||||
++ourTreshold;
|
||||
}
|
||||
if (!ourData.Background)
|
||||
{
|
||||
++ourTreshold;
|
||||
}
|
||||
int ourZ = Z;
|
||||
int theirTreshold = 0;
|
||||
if (theirData.Height > 0)
|
||||
{
|
||||
++theirTreshold;
|
||||
}
|
||||
if (!theirData.Background)
|
||||
{
|
||||
++theirTreshold;
|
||||
}
|
||||
int theirZ = a.Z;
|
||||
|
||||
ourZ += ourTreshold;
|
||||
theirZ += theirTreshold;
|
||||
int res = ourZ - theirZ;
|
||||
if (res == 0)
|
||||
{
|
||||
res = ourTreshold - theirTreshold;
|
||||
}
|
||||
if (res == 0)
|
||||
{
|
||||
res = m_Solver - a.Solver;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct Tile : IComparable
|
||||
{
|
||||
internal ushort m_ID;
|
||||
internal sbyte m_Z;
|
||||
|
||||
public ushort ID { get { return m_ID; } }
|
||||
public int Z { get { return m_Z; } set { m_Z = (sbyte)value; } }
|
||||
|
||||
public Tile(ushort id, sbyte z)
|
||||
{
|
||||
m_ID = id;
|
||||
m_Z = z;
|
||||
}
|
||||
|
||||
public Tile(ushort id, sbyte z, sbyte flag)
|
||||
{
|
||||
m_ID = id;
|
||||
m_Z = z;
|
||||
}
|
||||
|
||||
public void Set(ushort id, sbyte z)
|
||||
{
|
||||
m_ID = id;
|
||||
m_Z = z;
|
||||
}
|
||||
|
||||
public void Set(ushort id, sbyte z, sbyte flag)
|
||||
{
|
||||
m_ID = id;
|
||||
m_Z = z;
|
||||
}
|
||||
|
||||
public int CompareTo(object x)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(x is Tile))
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
var a = (Tile)x;
|
||||
|
||||
if (m_Z > a.m_Z)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (a.m_Z > m_Z)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
ItemData ourData = TileData.ItemTable[m_ID];
|
||||
ItemData theirData = TileData.ItemTable[a.m_ID];
|
||||
|
||||
if (ourData.Height > theirData.Height)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (theirData.Height > ourData.Height)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ourData.Background && !theirData.Background)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (theirData.Background && !ourData.Background)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
308
Ultima/TileMatrixPatch.cs
Normal file
308
Ultima/TileMatrixPatch.cs
Normal file
@@ -0,0 +1,308 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class TileMatrixPatch
|
||||
{
|
||||
public int LandBlocksCount { get; private set; }
|
||||
public int StaticBlocksCount { get; private set; }
|
||||
|
||||
public Tile[][][] LandBlocks { get; private set; }
|
||||
public HuedTile[][][][][] StaticBlocks { get; private set; }
|
||||
|
||||
private readonly int BlockWidth;
|
||||
private readonly int BlockHeight;
|
||||
|
||||
private static byte[] m_Buffer;
|
||||
private static StaticTile[] m_TileBuffer = new StaticTile[128];
|
||||
|
||||
public bool IsLandBlockPatched(int x, int y)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (LandBlocks[x] == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (LandBlocks[x][y] == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Tile[] GetLandBlock(int x, int y)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
|
||||
{
|
||||
return TileMatrix.InvalidLandBlock;
|
||||
}
|
||||
if (LandBlocks[x] == null)
|
||||
{
|
||||
return TileMatrix.InvalidLandBlock;
|
||||
}
|
||||
return LandBlocks[x][y];
|
||||
}
|
||||
|
||||
public Tile GetLandTile(int x, int y)
|
||||
{
|
||||
return GetLandBlock(x >> 3, y >> 3)[((y & 0x7) << 3) + (x & 0x7)];
|
||||
}
|
||||
|
||||
public bool IsStaticBlockPatched(int x, int y)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (StaticBlocks[x] == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (StaticBlocks[x][y] == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public HuedTile[][][] GetStaticBlock(int x, int y)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight)
|
||||
{
|
||||
return TileMatrix.EmptyStaticBlock;
|
||||
}
|
||||
if (StaticBlocks[x] == null)
|
||||
{
|
||||
return TileMatrix.EmptyStaticBlock;
|
||||
}
|
||||
return StaticBlocks[x][y];
|
||||
}
|
||||
|
||||
public HuedTile[] GetStaticTiles(int x, int y)
|
||||
{
|
||||
return GetStaticBlock(x >> 3, y >> 3)[x & 0x7][y & 0x7];
|
||||
}
|
||||
|
||||
public TileMatrixPatch(TileMatrix matrix, int index, string path)
|
||||
{
|
||||
BlockWidth = matrix.BlockWidth;
|
||||
BlockHeight = matrix.BlockWidth;
|
||||
|
||||
LandBlocksCount = StaticBlocksCount = 0;
|
||||
string mapDataPath, mapIndexPath;
|
||||
if (path == null)
|
||||
{
|
||||
mapDataPath = Files.GetFilePath("mapdif{0}.mul", index);
|
||||
mapIndexPath = Files.GetFilePath("mapdifl{0}.mul", index);
|
||||
}
|
||||
else
|
||||
{
|
||||
mapDataPath = Path.Combine(path, String.Format("mapdif{0}.mul", index));
|
||||
if (!File.Exists(mapDataPath))
|
||||
{
|
||||
mapDataPath = null;
|
||||
}
|
||||
mapIndexPath = Path.Combine(path, String.Format("mapdifl{0}.mul", index));
|
||||
if (!File.Exists(mapIndexPath))
|
||||
{
|
||||
mapIndexPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (mapDataPath != null && mapIndexPath != null)
|
||||
{
|
||||
LandBlocks = new Tile[matrix.BlockWidth][][];
|
||||
LandBlocksCount = PatchLand(matrix, mapDataPath, mapIndexPath);
|
||||
}
|
||||
|
||||
string staDataPath, staIndexPath, staLookupPath;
|
||||
if (path == null)
|
||||
{
|
||||
staDataPath = Files.GetFilePath("stadif{0}.mul", index);
|
||||
staIndexPath = Files.GetFilePath("stadifl{0}.mul", index);
|
||||
staLookupPath = Files.GetFilePath("stadifi{0}.mul", index);
|
||||
}
|
||||
else
|
||||
{
|
||||
staDataPath = Path.Combine(path, String.Format("stadif{0}.mul", index));
|
||||
if (!File.Exists(staDataPath))
|
||||
{
|
||||
staDataPath = null;
|
||||
}
|
||||
staIndexPath = Path.Combine(path, String.Format("stadifl{0}.mul", index));
|
||||
if (!File.Exists(staIndexPath))
|
||||
{
|
||||
staIndexPath = null;
|
||||
}
|
||||
staLookupPath = Path.Combine(path, String.Format("stadifi{0}.mul", index));
|
||||
if (!File.Exists(staLookupPath))
|
||||
{
|
||||
staLookupPath = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (staDataPath != null && staIndexPath != null && staLookupPath != null)
|
||||
{
|
||||
StaticBlocks = new HuedTile[matrix.BlockWidth][][][][];
|
||||
StaticBlocksCount = PatchStatics(matrix, staDataPath, staIndexPath, staLookupPath);
|
||||
}
|
||||
}
|
||||
|
||||
private int PatchLand(TileMatrix matrix, string dataPath, string indexPath)
|
||||
{
|
||||
using (
|
||||
FileStream fsData = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read),
|
||||
fsIndex = new FileStream(indexPath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
using (var indexReader = new BinaryReader(fsIndex))
|
||||
{
|
||||
var count = (int)(indexReader.BaseStream.Length / 4);
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int blockID = indexReader.ReadInt32();
|
||||
int x = blockID / matrix.BlockHeight;
|
||||
int y = blockID % matrix.BlockHeight;
|
||||
|
||||
fsData.Seek(4, SeekOrigin.Current);
|
||||
|
||||
var tiles = new Tile[64];
|
||||
|
||||
GCHandle gc = GCHandle.Alloc(tiles, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
if (m_Buffer == null || m_Buffer.Length < 192)
|
||||
{
|
||||
m_Buffer = new byte[192];
|
||||
}
|
||||
|
||||
fsData.Read(m_Buffer, 0, 192);
|
||||
|
||||
Marshal.Copy(m_Buffer, 0, gc.AddrOfPinnedObject(), 192);
|
||||
}
|
||||
finally
|
||||
{
|
||||
gc.Free();
|
||||
}
|
||||
if (LandBlocks[x] == null)
|
||||
{
|
||||
LandBlocks[x] = new Tile[matrix.BlockHeight][];
|
||||
}
|
||||
LandBlocks[x][y] = tiles;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int PatchStatics(TileMatrix matrix, string dataPath, string indexPath, string lookupPath)
|
||||
{
|
||||
using (
|
||||
FileStream fsData = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read),
|
||||
fsIndex = new FileStream(indexPath, FileMode.Open, FileAccess.Read, FileShare.Read),
|
||||
fsLookup = new FileStream(lookupPath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
using (BinaryReader indexReader = new BinaryReader(fsIndex), lookupReader = new BinaryReader(fsLookup))
|
||||
{
|
||||
int count = Math.Min((int)(indexReader.BaseStream.Length / 4), (int)(lookupReader.BaseStream.Length / 12));
|
||||
|
||||
var lists = new HuedTileList[8][];
|
||||
|
||||
for (int x = 0; x < 8; ++x)
|
||||
{
|
||||
lists[x] = new HuedTileList[8];
|
||||
|
||||
for (int y = 0; y < 8; ++y)
|
||||
{
|
||||
lists[x][y] = new HuedTileList();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int blockID = indexReader.ReadInt32();
|
||||
int blockX = blockID / matrix.BlockHeight;
|
||||
int blockY = blockID % matrix.BlockHeight;
|
||||
|
||||
int offset = lookupReader.ReadInt32();
|
||||
int length = lookupReader.ReadInt32();
|
||||
lookupReader.ReadInt32(); // Extra
|
||||
|
||||
if (offset < 0 || length <= 0)
|
||||
{
|
||||
if (StaticBlocks[blockX] == null)
|
||||
{
|
||||
StaticBlocks[blockX] = new HuedTile[matrix.BlockHeight][][][];
|
||||
}
|
||||
|
||||
StaticBlocks[blockX][blockY] = TileMatrix.EmptyStaticBlock;
|
||||
continue;
|
||||
}
|
||||
|
||||
fsData.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
int tileCount = length / 7;
|
||||
|
||||
if (m_TileBuffer.Length < tileCount)
|
||||
{
|
||||
m_TileBuffer = new StaticTile[tileCount];
|
||||
}
|
||||
|
||||
StaticTile[] staTiles = m_TileBuffer;
|
||||
|
||||
GCHandle gc = GCHandle.Alloc(staTiles, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
if (m_Buffer == null || m_Buffer.Length < length)
|
||||
{
|
||||
m_Buffer = new byte[length];
|
||||
}
|
||||
|
||||
fsData.Read(m_Buffer, 0, length);
|
||||
|
||||
Marshal.Copy(m_Buffer, 0, gc.AddrOfPinnedObject(), length);
|
||||
|
||||
for (int j = 0; j < tileCount; ++j)
|
||||
{
|
||||
StaticTile cur = staTiles[j];
|
||||
lists[cur.m_X & 0x7][cur.m_Y & 0x7].Add(Art.GetLegalItemID(cur.m_ID), cur.m_Hue, cur.m_Z);
|
||||
}
|
||||
|
||||
var tiles = new HuedTile[8][][];
|
||||
|
||||
for (int x = 0; x < 8; ++x)
|
||||
{
|
||||
tiles[x] = new HuedTile[8][];
|
||||
|
||||
for (int y = 0; y < 8; ++y)
|
||||
{
|
||||
tiles[x][y] = lists[x][y].ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
if (StaticBlocks[blockX] == null)
|
||||
{
|
||||
StaticBlocks[blockX] = new HuedTile[matrix.BlockHeight][][][];
|
||||
}
|
||||
|
||||
StaticBlocks[blockX][blockY] = tiles;
|
||||
}
|
||||
finally
|
||||
{
|
||||
gc.Free();
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
231
Ultima/Ultima.csproj
Normal file
231
Ultima/Ultima.csproj
Normal file
@@ -0,0 +1,231 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
|
||||
<PropertyGroup>
|
||||
<ProjectType>Local</ProjectType>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{E08CFBE4-E013-44EE-8829-426D05BC083F}</ProjectGuid>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ApplicationIcon>
|
||||
</ApplicationIcon>
|
||||
<AssemblyKeyContainerName>
|
||||
</AssemblyKeyContainerName>
|
||||
<AssemblyName>Ultima</AssemblyName>
|
||||
<AssemblyOriginatorKeyFile>
|
||||
</AssemblyOriginatorKeyFile>
|
||||
<DefaultClientScript>JScript</DefaultClientScript>
|
||||
<DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout>
|
||||
<DefaultTargetSchema>IE50</DefaultTargetSchema>
|
||||
<DelaySign>false</DelaySign>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>Ultima</RootNamespace>
|
||||
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
|
||||
<StartupObject>
|
||||
</StartupObject>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<OldToolsVersion>3.5</OldToolsVersion>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<RuntimeIdentifiers>win7-x64;win7-x86;ubuntu.16.10-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<OutputPath>..\</OutputPath>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<BaseAddress>285212672</BaseAddress>
|
||||
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
|
||||
<ConfigurationOverrideFile>
|
||||
</ConfigurationOverrideFile>
|
||||
<DefineConstants>TRACE;DEBUG;NEWTIMERS;ServUO</DefineConstants>
|
||||
<DocumentationFile>
|
||||
</DocumentationFile>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<FileAlignment>4096</FileAlignment>
|
||||
<NoStdLib>false</NoStdLib>
|
||||
<NoWarn>
|
||||
</NoWarn>
|
||||
<Optimize>false</Optimize>
|
||||
<RegisterForComInterop>false</RegisterForComInterop>
|
||||
<RemoveIntegerChecks>false</RemoveIntegerChecks>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DebugType>full</DebugType>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<OutputPath>..\</OutputPath>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<BaseAddress>285212672</BaseAddress>
|
||||
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
|
||||
<ConfigurationOverrideFile>
|
||||
</ConfigurationOverrideFile>
|
||||
<DefineConstants>TRACE;NEWTIMERS;ServUO</DefineConstants>
|
||||
<DocumentationFile>
|
||||
</DocumentationFile>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<FileAlignment>4096</FileAlignment>
|
||||
<NoStdLib>false</NoStdLib>
|
||||
<NoWarn>
|
||||
</NoWarn>
|
||||
<Optimize>true</Optimize>
|
||||
<RegisterForComInterop>false</RegisterForComInterop>
|
||||
<RemoveIntegerChecks>false</RemoveIntegerChecks>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<DebugType>none</DebugType>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>..\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;NEWTIMERS;ServUO</DefineConstants>
|
||||
<BaseAddress>285212672</BaseAddress>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
|
||||
<OutputPath>..\</OutputPath>
|
||||
<BaseAddress>285212672</BaseAddress>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>
|
||||
</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<DefineConstants>TRACE;NEWTIMERS;ServUO</DefineConstants>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>..\</OutputPath>
|
||||
<DefineConstants>TRACE;DEBUG;NEWTIMERS;ServUO</DefineConstants>
|
||||
<BaseAddress>285212672</BaseAddress>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<OutputPath>..\</OutputPath>
|
||||
<BaseAddress>285212672</BaseAddress>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>
|
||||
</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<DefineConstants>TRACE;NEWTIMERS;ServUO</DefineConstants>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Drawing">
|
||||
<Name>System.Drawing</Name>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AnimationEdit.cs" />
|
||||
<Compile Include="Animations.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Animdata.cs" />
|
||||
<Compile Include="Art.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ASCIIFont.cs" />
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
<Compile Include="CalibrationInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Client.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ClientHandles.cs" />
|
||||
<Compile Include="FileIndex.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Files.cs" />
|
||||
<Compile Include="Gumps.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Hues.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Light.cs" />
|
||||
<Compile Include="LocationPointer.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Map.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MultiMap.cs" />
|
||||
<Compile Include="Multis.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="NativeMethods.cs" />
|
||||
<Compile Include="ProcessStream.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="RadarCol.cs" />
|
||||
<Compile Include="Settings.cs" />
|
||||
<Compile Include="SkillGroups.cs" />
|
||||
<Compile Include="Skills.cs" />
|
||||
<Compile Include="Sound.cs" />
|
||||
<Compile Include="SpeechList.cs" />
|
||||
<Compile Include="StringEntry.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="StringList.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Textures.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="TileData.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="TileList.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="TileMatrix.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="TileMatrixPatch.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UnicodeFont.cs" />
|
||||
<Compile Include="Verdata.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="WindowProcessStream.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
272
Ultima/UnicodeFont.cs
Normal file
272
Ultima/UnicodeFont.cs
Normal file
@@ -0,0 +1,272 @@
|
||||
#region References
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
#endregion
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class UnicodeFont
|
||||
{
|
||||
public UnicodeChar[] Chars { get; set; }
|
||||
|
||||
public UnicodeFont()
|
||||
{
|
||||
Chars = new UnicodeChar[0x10000];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns width of text
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public int GetWidth(string text)
|
||||
{
|
||||
if (text == null || text.Length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int width = 0;
|
||||
for (int i = 0; i < text.Length; ++i)
|
||||
{
|
||||
int c = text[i] % 0x10000;
|
||||
width += Chars[c].Width;
|
||||
width += Chars[c].XOffset;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns max height of text
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public int GetHeight(string text)
|
||||
{
|
||||
if (text == null || text.Length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int height = 0;
|
||||
for (int i = 0; i < text.Length; ++i)
|
||||
{
|
||||
int c = text[i] % 0x10000;
|
||||
height = Math.Max(height, Chars[c].Height + Chars[c].YOffset);
|
||||
}
|
||||
return height;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class UnicodeChar
|
||||
{
|
||||
public byte[] Bytes { get; set; }
|
||||
public sbyte XOffset { get; set; }
|
||||
public sbyte YOffset { get; set; }
|
||||
public int Height { get; set; }
|
||||
public int Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets Bitmap of Char
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Bitmap GetImage()
|
||||
{
|
||||
return GetImage(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets Bitmap of Char with Background -1
|
||||
/// </summary>
|
||||
/// <param name="fill"></param>
|
||||
/// <returns></returns>
|
||||
public unsafe Bitmap GetImage(bool fill)
|
||||
{
|
||||
if ((Width == 0) || (Height == 0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var bmp = new Bitmap(Width, Height, Settings.PixelFormat);
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, Settings.PixelFormat);
|
||||
var line = (ushort*)bd.Scan0;
|
||||
int delta = bd.Stride >> 1;
|
||||
for (int y = 0; y < Height; ++y, line += delta)
|
||||
{
|
||||
ushort* cur = line;
|
||||
for (int x = 0; x < Width; ++x)
|
||||
{
|
||||
if (IsPixelSet(Bytes, Width, x, y))
|
||||
{
|
||||
cur[x] = 0x8000;
|
||||
}
|
||||
else if (fill)
|
||||
{
|
||||
cur[x] = 0xffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
bmp.UnlockBits(bd);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
private static bool IsPixelSet(byte[] data, int width, int x, int y)
|
||||
{
|
||||
int offset = x / 8 + y * ((width + 7) / 8);
|
||||
if (offset > data.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return (data[offset] & (1 << (7 - (x % 8)))) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets Buffer with Bitmap
|
||||
/// </summary>
|
||||
/// <param name="bmp"></param>
|
||||
public unsafe void SetBuffer(Bitmap bmp)
|
||||
{
|
||||
Bytes = new byte[bmp.Height * (((bmp.Width - 1) / 8) + 1)];
|
||||
BitmapData bd = bmp.LockBits(
|
||||
new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, Settings.PixelFormat);
|
||||
var line = (ushort*)bd.Scan0;
|
||||
//int delta = bd.Stride >> 1;
|
||||
for (int y = 0; y < bmp.Height; ++y)
|
||||
{
|
||||
ushort* cur = line;
|
||||
for (int x = 0; x < bmp.Width; ++x)
|
||||
{
|
||||
if (cur[x] == 0x8000)
|
||||
{
|
||||
int offset = x / 8 + y * ((bmp.Width + 7) / 8);
|
||||
Bytes[offset] |= (byte)(1 << (7 - (x % 8)));
|
||||
}
|
||||
}
|
||||
}
|
||||
bmp.UnlockBits(bd);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnicodeFonts
|
||||
{
|
||||
private static readonly string[] m_files = new[]
|
||||
{
|
||||
"unifont.mul", "unifont1.mul", "unifont2.mul", "unifont3.mul", "unifont4.mul", "unifont5.mul", "unifont6.mul",
|
||||
"unifont7.mul", "unifont8.mul", "unifont9.mul", "unifont10.mul", "unifont11.mul", "unifont12.mul"
|
||||
};
|
||||
|
||||
public static UnicodeFont[] Fonts = new UnicodeFont[13];
|
||||
|
||||
static UnicodeFonts()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads unifont*.mul
|
||||
/// </summary>
|
||||
public static void Initialize()
|
||||
{
|
||||
for (int i = 0; i < m_files.Length; i++)
|
||||
{
|
||||
string filePath = Files.GetFilePath(m_files[i]);
|
||||
if (filePath == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Fonts[i] = new UnicodeFont();
|
||||
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
using (var bin = new BinaryReader(fs))
|
||||
{
|
||||
for (int c = 0; c < 0x10000; ++c)
|
||||
{
|
||||
Fonts[i].Chars[c] = new UnicodeChar();
|
||||
fs.Seek(((c) * 4), SeekOrigin.Begin);
|
||||
int num2 = bin.ReadInt32();
|
||||
if ((num2 >= fs.Length) || (num2 <= 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
fs.Seek(num2, SeekOrigin.Begin);
|
||||
sbyte xOffset = bin.ReadSByte();
|
||||
sbyte yOffset = bin.ReadSByte();
|
||||
int Width = bin.ReadByte();
|
||||
int Height = bin.ReadByte();
|
||||
Fonts[i].Chars[c].XOffset = xOffset;
|
||||
Fonts[i].Chars[c].YOffset = yOffset;
|
||||
Fonts[i].Chars[c].Width = Width;
|
||||
Fonts[i].Chars[c].Height = Height;
|
||||
if (!((Width == 0) || (Height == 0)))
|
||||
{
|
||||
Fonts[i].Chars[c].Bytes = bin.ReadBytes(Height * (((Width - 1) / 8) + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws Text with font in Bitmap and returns
|
||||
/// </summary>
|
||||
/// <param name="fontId"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap WriteText(int fontId, string text)
|
||||
{
|
||||
var result = new Bitmap(Fonts[fontId].GetWidth(text) + 2, Fonts[fontId].GetHeight(text) + 2);
|
||||
|
||||
int dx = 2;
|
||||
int dy = 2;
|
||||
using (Graphics graph = Graphics.FromImage(result))
|
||||
{
|
||||
for (int i = 0; i < text.Length; ++i)
|
||||
{
|
||||
int c = text[i] % 0x10000;
|
||||
Bitmap bmp = Fonts[fontId].Chars[c].GetImage();
|
||||
dx += Fonts[fontId].Chars[c].XOffset;
|
||||
graph.DrawImage(bmp, dx, dy + Fonts[fontId].Chars[c].YOffset);
|
||||
dx += bmp.Width;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves Font and returns string Filename
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="filetype"></param>
|
||||
/// <returns></returns>
|
||||
public static string Save(string path, int filetype)
|
||||
{
|
||||
string FileName = Path.Combine(path, m_files[filetype]);
|
||||
using (var fs = new FileStream(FileName, FileMode.Create, FileAccess.Write, FileShare.Write))
|
||||
{
|
||||
using (var bin = new BinaryWriter(fs))
|
||||
{
|
||||
fs.Seek(0x10000 * 4, SeekOrigin.Begin);
|
||||
bin.Write(0);
|
||||
// Set first data
|
||||
for (int c = 0; c < 0x10000; ++c)
|
||||
{
|
||||
if (Fonts[filetype].Chars[c].Bytes == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
fs.Seek(((c) * 4), SeekOrigin.Begin);
|
||||
bin.Write((int)fs.Length);
|
||||
fs.Seek(fs.Length, SeekOrigin.Begin);
|
||||
bin.Write(Fonts[filetype].Chars[c].XOffset);
|
||||
bin.Write(Fonts[filetype].Chars[c].YOffset);
|
||||
bin.Write((byte)Fonts[filetype].Chars[c].Width);
|
||||
bin.Write((byte)Fonts[filetype].Chars[c].Height);
|
||||
bin.Write(Fonts[filetype].Chars[c].Bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
return FileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
92
Ultima/Verdata.cs
Normal file
92
Ultima/Verdata.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
#region References
|
||||
using System.IO;
|
||||
#endregion
|
||||
|
||||
// FileIDs
|
||||
//0 - map0.mul
|
||||
//1 - staidx0.mul
|
||||
//2 - statics0.mul
|
||||
//3 - artidx.mul
|
||||
//4 - art.mul
|
||||
//5 - anim.idx
|
||||
//6 - anim.mul
|
||||
//7 - soundidx.mul
|
||||
//8 - sound.mul
|
||||
//9 - texidx.mul
|
||||
//10 - texmaps.mul
|
||||
//11 - gumpidx.mul
|
||||
//12 - gumpart.mul
|
||||
//13 - multi.idx
|
||||
//14 - multi.mul
|
||||
//15 - skills.idx
|
||||
//16 - skills.mul
|
||||
//30 - tiledata.mul
|
||||
//31 - animdata.mul
|
||||
|
||||
namespace Ultima
|
||||
{
|
||||
public sealed class Verdata
|
||||
{
|
||||
public static Stream Stream { get; private set; }
|
||||
public static Entry5D[] Patches { get; private set; }
|
||||
|
||||
private static string path;
|
||||
|
||||
static Verdata()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
path = Files.GetFilePath("verdata.mul");
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
Patches = new Entry5D[0];
|
||||
Stream = Stream.Null;
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
|
||||
{
|
||||
using (var bin = new BinaryReader(Stream))
|
||||
{
|
||||
Patches = new Entry5D[bin.ReadInt32()];
|
||||
|
||||
for (int i = 0; i < Patches.Length; ++i)
|
||||
{
|
||||
Patches[i].file = bin.ReadInt32();
|
||||
Patches[i].index = bin.ReadInt32();
|
||||
Patches[i].lookup = bin.ReadInt32();
|
||||
Patches[i].length = bin.ReadInt32();
|
||||
Patches[i].extra = bin.ReadInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
Stream.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Seek(int lookup)
|
||||
{
|
||||
if (Stream == null || !Stream.CanRead || !Stream.CanSeek)
|
||||
{
|
||||
if (path != null)
|
||||
{
|
||||
Stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
}
|
||||
}
|
||||
Stream.Seek(lookup, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
public struct Entry5D
|
||||
{
|
||||
public int file;
|
||||
public int index;
|
||||
public int lookup;
|
||||
public int length;
|
||||
public int extra;
|
||||
}
|
||||
}
|
||||
31
Ultima/WindowProcessStream.cs
Normal file
31
Ultima/WindowProcessStream.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
namespace Ultima
|
||||
{
|
||||
public class WindowProcessStream : ProcessStream
|
||||
{
|
||||
private ClientWindowHandle m_Window;
|
||||
private ClientProcessHandle m_ProcessID;
|
||||
|
||||
public ClientWindowHandle Window { get { return m_Window; } set { m_Window = value; } }
|
||||
|
||||
public WindowProcessStream(ClientWindowHandle window)
|
||||
{
|
||||
m_Window = window;
|
||||
m_ProcessID = ClientProcessHandle.Invalid;
|
||||
}
|
||||
|
||||
public override ClientProcessHandle ProcessID
|
||||
{
|
||||
get
|
||||
{
|
||||
if (NativeMethods.IsWindow(m_Window) != 0 && !m_ProcessID.IsInvalid)
|
||||
{
|
||||
return m_ProcessID;
|
||||
}
|
||||
|
||||
NativeMethods.GetWindowThreadProcessId(m_Window, ref m_ProcessID);
|
||||
|
||||
return m_ProcessID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user