2019-08-12 22:20:45 +00:00
|
|
|
|
using System;
|
2019-07-28 23:04:23 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Text;
|
2019-08-28 03:22:57 +00:00
|
|
|
|
using Path = Alphaleonis.Win32.Filesystem.Path;
|
2019-07-28 23:04:23 +00:00
|
|
|
|
|
|
|
|
|
namespace Compression.BSA
|
|
|
|
|
{
|
2019-07-30 03:32:52 +00:00
|
|
|
|
internal static class Utils
|
2019-07-28 23:04:23 +00:00
|
|
|
|
{
|
2019-09-14 04:35:42 +00:00
|
|
|
|
private static readonly Encoding Windows1252 = Encoding.GetEncoding(1252);
|
|
|
|
|
|
2019-09-08 03:34:18 +00:00
|
|
|
|
private static Encoding GetEncoding(VersionType version)
|
|
|
|
|
{
|
|
|
|
|
if (version == VersionType.SSE)
|
|
|
|
|
return Windows1252;
|
|
|
|
|
return Encoding.UTF7;
|
|
|
|
|
}
|
2019-08-11 22:57:32 +00:00
|
|
|
|
|
2019-09-08 03:34:18 +00:00
|
|
|
|
public static string ReadStringLen(this BinaryReader rdr, VersionType version)
|
2019-07-28 23:04:23 +00:00
|
|
|
|
{
|
|
|
|
|
var len = rdr.ReadByte();
|
2019-09-08 22:44:15 +00:00
|
|
|
|
if (len == 0)
|
|
|
|
|
//rdr.ReadByte();
|
|
|
|
|
return "";
|
|
|
|
|
|
2019-07-28 23:04:23 +00:00
|
|
|
|
var bytes = rdr.ReadBytes(len - 1);
|
|
|
|
|
rdr.ReadByte();
|
2019-09-08 03:34:18 +00:00
|
|
|
|
return GetEncoding(version).GetString(bytes);
|
2019-07-28 23:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-08 03:34:18 +00:00
|
|
|
|
public static string ReadStringLenNoTerm(this BinaryReader rdr, VersionType version)
|
2019-08-22 04:27:24 +00:00
|
|
|
|
{
|
|
|
|
|
var len = rdr.ReadByte();
|
|
|
|
|
var bytes = rdr.ReadBytes(len);
|
2019-09-08 03:34:18 +00:00
|
|
|
|
return GetEncoding(version).GetString(bytes);
|
2019-08-22 04:27:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-08 03:34:18 +00:00
|
|
|
|
public static string ReadStringTerm(this BinaryReader rdr, VersionType version)
|
2019-07-28 23:04:23 +00:00
|
|
|
|
{
|
2019-09-14 04:35:42 +00:00
|
|
|
|
var acc = new List<byte>();
|
2019-07-28 23:04:23 +00:00
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
var c = rdr.ReadByte();
|
|
|
|
|
|
|
|
|
|
if (c == '\0') break;
|
|
|
|
|
|
|
|
|
|
acc.Add(c);
|
|
|
|
|
}
|
2019-09-14 04:35:42 +00:00
|
|
|
|
|
2019-09-08 03:34:18 +00:00
|
|
|
|
return GetEncoding(version).GetString(acc.ToArray());
|
2019-07-28 23:04:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-14 04:35:42 +00:00
|
|
|
|
/// Returns bytes for a \0 terminated string
|
2019-07-28 23:04:23 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="val"></param>
|
|
|
|
|
/// <returns></returns>
|
2019-09-08 03:34:18 +00:00
|
|
|
|
public static byte[] ToBZString(this string val, VersionType version)
|
2019-07-28 23:04:23 +00:00
|
|
|
|
{
|
2019-09-08 03:34:18 +00:00
|
|
|
|
var b = GetEncoding(version).GetBytes(val);
|
2019-07-28 23:04:23 +00:00
|
|
|
|
var b2 = new byte[b.Length + 2];
|
|
|
|
|
b.CopyTo(b2, 1);
|
2019-09-14 04:35:42 +00:00
|
|
|
|
b2[0] = (byte) (b.Length + 1);
|
2019-07-29 04:52:04 +00:00
|
|
|
|
return b2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-14 04:35:42 +00:00
|
|
|
|
/// Returns bytes for unterminated string with a count at the start
|
2019-07-29 04:52:04 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="val"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static byte[] ToBSString(this string val)
|
|
|
|
|
{
|
2019-08-22 04:27:24 +00:00
|
|
|
|
var b = Encoding.ASCII.GetBytes(val);
|
2019-07-29 04:52:04 +00:00
|
|
|
|
var b2 = new byte[b.Length + 1];
|
|
|
|
|
b.CopyTo(b2, 1);
|
2019-09-14 04:35:42 +00:00
|
|
|
|
b2[0] = (byte) b.Length;
|
2019-07-29 04:52:04 +00:00
|
|
|
|
|
2019-07-28 23:04:23 +00:00
|
|
|
|
return b2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-09-14 04:35:42 +00:00
|
|
|
|
/// Returns bytes for a \0 terminated string prefixed by a length
|
2019-07-28 23:04:23 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="val"></param>
|
|
|
|
|
/// <returns></returns>
|
2019-09-08 03:34:18 +00:00
|
|
|
|
public static byte[] ToTermString(this string val, VersionType version)
|
2019-07-28 23:04:23 +00:00
|
|
|
|
{
|
2019-09-08 03:34:18 +00:00
|
|
|
|
var b = GetEncoding(version).GetBytes(val);
|
2019-07-28 23:04:23 +00:00
|
|
|
|
var b2 = new byte[b.Length + 1];
|
|
|
|
|
b.CopyTo(b2, 0);
|
2019-09-14 04:35:42 +00:00
|
|
|
|
b[0] = (byte) b.Length;
|
2019-07-28 23:04:23 +00:00
|
|
|
|
return b2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static ulong GetBSAHash(this string name)
|
|
|
|
|
{
|
|
|
|
|
name = name.Replace('/', '\\');
|
|
|
|
|
return GetBSAHash(Path.ChangeExtension(name, null), Path.GetExtension(name));
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-29 21:24:05 +00:00
|
|
|
|
public static ulong GetBSAHash(this string name, string ext)
|
2019-07-28 23:04:23 +00:00
|
|
|
|
{
|
|
|
|
|
name = name.ToLowerInvariant();
|
|
|
|
|
ext = ext.ToLowerInvariant();
|
2019-09-08 22:44:15 +00:00
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(name))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
var hashBytes = new[]
|
2019-07-28 23:04:23 +00:00
|
|
|
|
{
|
2019-09-14 04:35:42 +00:00
|
|
|
|
(byte) (name.Length == 0 ? '\0' : name[name.Length - 1]),
|
|
|
|
|
(byte) (name.Length < 3 ? '\0' : name[name.Length - 2]),
|
|
|
|
|
(byte) name.Length,
|
|
|
|
|
(byte) name[0]
|
2019-07-28 23:04:23 +00:00
|
|
|
|
};
|
|
|
|
|
var hash1 = BitConverter.ToUInt32(hashBytes, 0);
|
|
|
|
|
switch (ext)
|
|
|
|
|
{
|
|
|
|
|
case ".kf":
|
|
|
|
|
hash1 |= 0x80;
|
|
|
|
|
break;
|
|
|
|
|
case ".nif":
|
|
|
|
|
hash1 |= 0x8000;
|
|
|
|
|
break;
|
|
|
|
|
case ".dds":
|
|
|
|
|
hash1 |= 0x8080;
|
|
|
|
|
break;
|
|
|
|
|
case ".wav":
|
|
|
|
|
hash1 |= 0x80000000;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint hash2 = 0;
|
2019-09-14 04:35:42 +00:00
|
|
|
|
for (var i = 1; i < name.Length - 2; i++) hash2 = hash2 * 0x1003f + (byte) name[i];
|
2019-07-28 23:04:23 +00:00
|
|
|
|
|
|
|
|
|
uint hash3 = 0;
|
2019-09-14 04:35:42 +00:00
|
|
|
|
for (var i = 0; i < ext.Length; i++) hash3 = hash3 * 0x1003f + (byte) ext[i];
|
2019-07-28 23:04:23 +00:00
|
|
|
|
|
2019-09-14 04:35:42 +00:00
|
|
|
|
return ((ulong) (hash2 + hash3) << 32) + hash1;
|
2019-07-28 23:04:23 +00:00
|
|
|
|
}
|
2019-07-29 04:52:04 +00:00
|
|
|
|
|
|
|
|
|
public static void CopyToLimit(this Stream frm, Stream tw, int limit)
|
|
|
|
|
{
|
2019-09-14 04:35:42 +00:00
|
|
|
|
var buff = new byte[1024];
|
2019-07-29 04:52:04 +00:00
|
|
|
|
while (limit > 0)
|
|
|
|
|
{
|
2019-09-14 04:35:42 +00:00
|
|
|
|
var to_read = Math.Min(buff.Length, limit);
|
|
|
|
|
var read = frm.Read(buff, 0, to_read);
|
2019-07-29 04:52:04 +00:00
|
|
|
|
tw.Write(buff, 0, read);
|
|
|
|
|
limit -= read;
|
|
|
|
|
}
|
2019-09-14 04:35:42 +00:00
|
|
|
|
|
2019-08-22 04:27:24 +00:00
|
|
|
|
tw.Flush();
|
2019-07-29 04:52:04 +00:00
|
|
|
|
}
|
2019-07-28 23:04:23 +00:00
|
|
|
|
}
|
2019-09-14 04:35:42 +00:00
|
|
|
|
}
|