Compare commits
184 Commits
0.7.0-rc0
...
b45cf9cba7
@@ -1,65 +0,0 @@
|
|||||||
namespace Cryville.Common.Buffers {
|
|
||||||
/// <summary>
|
|
||||||
/// A resource pool that allows reusing instances of arrays of type <typeparamref name="T" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The item type of the arrays in the pool.</typeparam>
|
|
||||||
public class ArrayPool<T> {
|
|
||||||
private class Bucket : ObjectPool<T[]> {
|
|
||||||
readonly int _size;
|
|
||||||
public Bucket(int size, int capacity) : base(capacity) {
|
|
||||||
_size = size;
|
|
||||||
}
|
|
||||||
protected override T[] Construct() {
|
|
||||||
return new T[_size];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readonly Bucket[] _buckets;
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of the <see cref="ArrayPool{T}" /> class with the default maximum list size and bucket capacity.
|
|
||||||
/// </summary>
|
|
||||||
public ArrayPool() : this(0x40000000, 256) { }
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of the <see cref="ArrayPool{T}" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="maxSize">The maximum size of the arrays in the pool.</param>
|
|
||||||
/// <param name="capacityPerBucket">The capacity of each bucket. The pool groups arrays of similar sizes into buckets for faster access.</param>
|
|
||||||
public ArrayPool(int maxSize, int capacityPerBucket) {
|
|
||||||
if (maxSize < 16) maxSize = 16;
|
|
||||||
int num = GetID(maxSize) + 1;
|
|
||||||
_buckets = new Bucket[num];
|
|
||||||
for (int i = 0; i < num; i++) {
|
|
||||||
_buckets[i] = new Bucket(GetSize(i), capacityPerBucket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Rents an array that is at least the specified size from the pool.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="size">The minimum size of the array.</param>
|
|
||||||
/// <returns>An array of type <see cref="T" /> that is at least the specified size.</returns>
|
|
||||||
public T[] Rent(int size) {
|
|
||||||
int len2 = size;
|
|
||||||
if (len2 < 16) len2 = 16;
|
|
||||||
var arr = _buckets[GetID(len2)].Rent();
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a rented array to the pool.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="arr">The array to return.</param>
|
|
||||||
public void Return(T[] arr) {
|
|
||||||
int len2 = arr.Length;
|
|
||||||
if (len2 < 16) len2 = 16;
|
|
||||||
_buckets[GetID(len2)].Return(arr);
|
|
||||||
}
|
|
||||||
static int GetID(int size) {
|
|
||||||
size -= 1;
|
|
||||||
size >>= 4;
|
|
||||||
int num = 0;
|
|
||||||
for (; size != 0; size >>= 1) num++;
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
static int GetSize(int id) {
|
|
||||||
return 0x10 << id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Cryville.Common.Buffers {
|
|
||||||
/// <summary>
|
|
||||||
/// A set of resource pools categorized by a category type.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TCategory">The category type.</typeparam>
|
|
||||||
/// <typeparam name="TObject">The type of the objects in the pool.</typeparam>
|
|
||||||
public abstract class CategorizedPool<TCategory, TObject> where TObject : class {
|
|
||||||
/// <summary>
|
|
||||||
/// The set of underlying pools.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>The <see cref="Rent(TCategory)" /> and <see cref="Return(TCategory, TObject)" /> method select an underlying pool directly from this set with the category as the key. When overridden, this set must be available since construction.</para>
|
|
||||||
/// </remarks>
|
|
||||||
protected abstract IReadOnlyDictionary<TCategory, ObjectPool<TObject>> Buckets { get; }
|
|
||||||
/// <summary>
|
|
||||||
/// The count of objects rented from the set of pools.
|
|
||||||
/// </summary>
|
|
||||||
public int RentedCount { get; private set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Rents an object from the pool.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="category">The category.</param>
|
|
||||||
/// <returns>The rented object.</returns>
|
|
||||||
public TObject Rent(TCategory category) {
|
|
||||||
var obj = Buckets[category].Rent();
|
|
||||||
RentedCount++;
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a rented object to the pool.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="category">The category.</param>
|
|
||||||
/// <param name="obj">The object to return.</param>
|
|
||||||
public void Return(TCategory category, TObject obj) {
|
|
||||||
Buckets[category].Return(obj);
|
|
||||||
--RentedCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// A utility to access a categorized pool, representing a single unit that uses a shared categorized pool.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TCategory">The category type.</typeparam>
|
|
||||||
/// <typeparam name="TObject">The type of the objects in the pool.</typeparam>
|
|
||||||
public class CategorizedPoolAccessor<TCategory, TObject> where TObject : class {
|
|
||||||
readonly CategorizedPool<TCategory, TObject> _pool;
|
|
||||||
static readonly SimpleObjectPool<Dictionary<TObject, TCategory>> _dictPool
|
|
||||||
= new SimpleObjectPool<Dictionary<TObject, TCategory>>(1024);
|
|
||||||
Dictionary<TObject, TCategory> _rented;
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of the <see cref="CategorizedPoolAccessor{TCategory, TObject}" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pool">The categorized pool.</param>
|
|
||||||
public CategorizedPoolAccessor(CategorizedPool<TCategory, TObject> pool) {
|
|
||||||
_pool = pool;
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Rents an object from the pool.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="category">The category.</param>
|
|
||||||
/// <returns>The rented object.</returns>
|
|
||||||
public TObject Rent(TCategory category) {
|
|
||||||
var obj = _pool.Rent(category);
|
|
||||||
if (_rented == null) _rented = _dictPool.Rent();
|
|
||||||
_rented.Add(obj, category);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a rented object to the pool.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">The object to return.</param>
|
|
||||||
public void Return(TObject obj) {
|
|
||||||
_pool.Return(_rented[obj], obj);
|
|
||||||
_rented.Remove(obj);
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Returns all objects rented by this accessor to the pool.
|
|
||||||
/// </summary>
|
|
||||||
public void ReturnAll() {
|
|
||||||
if (_rented == null) return;
|
|
||||||
foreach (var obj in _rented) {
|
|
||||||
_pool.Return(obj.Value, obj.Key);
|
|
||||||
}
|
|
||||||
_rented.Clear();
|
|
||||||
_dictPool.Return(_rented);
|
|
||||||
_rented = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Cryville.Common.Buffers {
|
|
||||||
/// <summary>
|
|
||||||
/// A resource pool that allows reusing instances of lists of type <typeparamref name="T" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The item type of the lists in the pool.</typeparam>
|
|
||||||
public class ListPool<T> {
|
|
||||||
private class Bucket : ObjectPool<List<T>> {
|
|
||||||
readonly int _size;
|
|
||||||
public Bucket(int size, int capacity) : base(capacity) {
|
|
||||||
_size = size;
|
|
||||||
}
|
|
||||||
protected override List<T> Construct() {
|
|
||||||
return new List<T>(_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
readonly Bucket[] _buckets;
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of the <see cref="ListPool{T}" /> class with the default maximum list size and bucket capacity.
|
|
||||||
/// </summary>
|
|
||||||
public ListPool() : this(0x40000000, 256) { }
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of the <see cref="ListPool{T}" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="maxSize">The maximum size of the lists in the pool.</param>
|
|
||||||
/// <param name="capacityPerBucket">The capacity of each bucket. The pool groups lists of similar sizes into buckets for faster access.</param>
|
|
||||||
public ListPool(int maxSize, int capacityPerBucket) {
|
|
||||||
if (maxSize < 16) maxSize = 16;
|
|
||||||
int num = GetID(maxSize) + 1;
|
|
||||||
_buckets = new Bucket[num];
|
|
||||||
for (int i = 0; i < num; i++) {
|
|
||||||
_buckets[i] = new Bucket(GetSize(i), capacityPerBucket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Rents a list of the specified size from the pool. The size of the list must not be changed when it is rented.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="size">The size of the list.</param>
|
|
||||||
/// <returns>A <see cref="List{T}" /> of the specified size.</returns>
|
|
||||||
public List<T> Rent(int size) {
|
|
||||||
int len2 = size;
|
|
||||||
if (len2 < 16) len2 = 16;
|
|
||||||
var list = _buckets[GetID(len2)].Rent();
|
|
||||||
if (list.Count < size)
|
|
||||||
for (int i = list.Count; i < size; i++) list.Add(default(T));
|
|
||||||
else if (list.Count > size)
|
|
||||||
list.RemoveRange(size, list.Count - size);
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a rented list to the pool.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="list">The list to return.</param>
|
|
||||||
public void Return(List<T> list) {
|
|
||||||
int len2 = list.Capacity;
|
|
||||||
if (len2 < 16) len2 = 16;
|
|
||||||
_buckets[GetID(len2)].Return(list);
|
|
||||||
}
|
|
||||||
static int GetID(int size) {
|
|
||||||
size -= 1;
|
|
||||||
size >>= 4;
|
|
||||||
int num = 0;
|
|
||||||
for (; size != 0; size >>= 1) num++;
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
static int GetSize(int id) {
|
|
||||||
return 0x10 << id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
namespace Cryville.Common.Buffers {
|
|
||||||
/// <summary>
|
|
||||||
/// A resource pool that allows reusing instances of type <typeparamref name="T" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the objects in the pool.</typeparam>
|
|
||||||
public abstract class ObjectPool<T> where T : class {
|
|
||||||
int _index;
|
|
||||||
readonly T[] _objs;
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of the <see cref="ObjectPool{T}" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="capacity">The capacity of the pool.</param>
|
|
||||||
public ObjectPool(int capacity) {
|
|
||||||
_objs = new T[capacity];
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// The count of objects rented from the pool.
|
|
||||||
/// </summary>
|
|
||||||
public int RentedCount { get { return _index; } }
|
|
||||||
/// <summary>
|
|
||||||
/// Rents a object from the pool.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The rented object.</returns>
|
|
||||||
public T Rent() {
|
|
||||||
T obj = null;
|
|
||||||
if (_index < _objs.Length) {
|
|
||||||
obj = _objs[_index];
|
|
||||||
_objs[_index++] = null;
|
|
||||||
}
|
|
||||||
if (obj == null) obj = Construct();
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a rented object to the pool.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">The object to return.</param>
|
|
||||||
public void Return(T obj) {
|
|
||||||
if (_index > 0) {
|
|
||||||
Reset(obj);
|
|
||||||
_objs[--_index] = obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs a new instance of type <typeparamref name="T" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The new instance.</returns>
|
|
||||||
protected abstract T Construct();
|
|
||||||
/// <summary>
|
|
||||||
/// Resets an object.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">The object.</param>
|
|
||||||
protected virtual void Reset(T obj) { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
namespace Cryville.Common.Buffers {
|
|
||||||
/// <summary>
|
|
||||||
/// A resource pool that allows reusing instances of type <typeparamref name="T" />, which has a parameterless constructor.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the objects in the pool.</typeparam>
|
|
||||||
public class SimpleObjectPool<T> : ObjectPool<T> where T : class, new() {
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of the <see cref="SimpleObjectPool{T}" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="capacity">The capacity of the pool.</param>
|
|
||||||
public SimpleObjectPool(int capacity) : base(capacity) { }
|
|
||||||
protected override T Construct() {
|
|
||||||
return new T();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: b8cd439340f088d4eb83711a5bc6384d
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Cryville.Common.Buffers {
|
|
||||||
/// <summary>
|
|
||||||
/// An auto-resized <see cref="char" /> array as a variable-length string used as a target that is modified frequently.
|
|
||||||
/// </summary>
|
|
||||||
public class TargetString : IEnumerable<char> {
|
|
||||||
public event Action OnUpdate;
|
|
||||||
char[] _arr;
|
|
||||||
bool _invalidated;
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of the <see cref="TargetString" /> class with a capacity of 16.
|
|
||||||
/// </summary>
|
|
||||||
public TargetString() : this(16) { }
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of the <see cref="TargetString" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="capacity">The initial capacity of the string.</param>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="capacity" /> is less than or equal to 0.</exception>
|
|
||||||
public TargetString(int capacity) {
|
|
||||||
if (capacity <= 0) throw new ArgumentOutOfRangeException("capacity");
|
|
||||||
_arr = new char[capacity];
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets one of the characters in the string.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">The zero-based index of the character.</param>
|
|
||||||
/// <returns>The character at the given index.</returns>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index" /> is less than 0 or not less than <see cref="Length" />.</exception>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>Set <see cref="Length" /> to a desired value before updating the characters.</para>
|
|
||||||
/// <para>Call <see cref=" Validate" /> after all the characters are updated.</para>
|
|
||||||
/// </remarks>
|
|
||||||
public char this[int index] {
|
|
||||||
get {
|
|
||||||
if (index < 0 || index >= m_length)
|
|
||||||
throw new ArgumentOutOfRangeException("index");
|
|
||||||
return _arr[index];
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
if (index < 0 || index >= m_length)
|
|
||||||
throw new ArgumentOutOfRangeException("index");
|
|
||||||
if (_arr[index] == value) return;
|
|
||||||
_arr[index] = value;
|
|
||||||
_invalidated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int m_length;
|
|
||||||
/// <summary>
|
|
||||||
/// The length of the string.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException">The value specified for a set operation is less than 0.</exception>
|
|
||||||
public int Length {
|
|
||||||
get {
|
|
||||||
return m_length;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
if (Length < 0) throw new ArgumentOutOfRangeException("length");
|
|
||||||
if (m_length == value) return;
|
|
||||||
if (_arr.Length < value) {
|
|
||||||
var len = _arr.Length;
|
|
||||||
while (len < value) len *= 2;
|
|
||||||
var arr2 = new char[len];
|
|
||||||
Array.Copy(_arr, arr2, m_length);
|
|
||||||
_arr = arr2;
|
|
||||||
}
|
|
||||||
m_length = value;
|
|
||||||
_invalidated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Validates the string.
|
|
||||||
/// </summary>
|
|
||||||
public void Validate() {
|
|
||||||
if (!_invalidated) return;
|
|
||||||
_invalidated = false;
|
|
||||||
var ev = OnUpdate;
|
|
||||||
if (ev != null) ev.Invoke();
|
|
||||||
}
|
|
||||||
internal char[] TrustedAsArray() { return _arr; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns an enumerator that iterates through the <see cref="TargetString" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A <see cref="Enumerator" /> for the <see cref="TargetString" />.</returns>
|
|
||||||
public Enumerator GetEnumerator() {
|
|
||||||
return new Enumerator(this);
|
|
||||||
}
|
|
||||||
IEnumerator<char> IEnumerable<char>.GetEnumerator() {
|
|
||||||
return new Enumerator(this);
|
|
||||||
}
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() {
|
|
||||||
return new Enumerator(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Enumerator : IEnumerator<char> {
|
|
||||||
readonly TargetString _self;
|
|
||||||
int _index;
|
|
||||||
internal Enumerator(TargetString self) {
|
|
||||||
_self = self;
|
|
||||||
_index = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public char Current {
|
|
||||||
get {
|
|
||||||
if (_index < 0)
|
|
||||||
throw new InvalidOperationException(_index == -1 ? "Enum not started" : "Enum ended");
|
|
||||||
return _self[_index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object IEnumerator.Current { get { return Current; } }
|
|
||||||
|
|
||||||
public void Dispose() {
|
|
||||||
_index = -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MoveNext() {
|
|
||||||
if (_index == -2) return false;
|
|
||||||
_index++;
|
|
||||||
if (_index >= _self.Length) {
|
|
||||||
_index = -2;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset() {
|
|
||||||
_index = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f0fc34ac257793d4883a9cfcdb6941b9
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Cryville.Common.Collections.Generic {
|
|
||||||
public interface IPairList<TKey, TValue> : IList<KeyValuePair<TKey, TValue>> {
|
|
||||||
void Add(TKey key, TValue value);
|
|
||||||
void Insert(int index, TKey key, TValue value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 73fb17b484b343242bcce27c15ed7d44
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Cryville.Common.Collections.Generic {
|
|
||||||
[DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy(typeof(PairCollectionDebugView<,>))]
|
|
||||||
public struct PairCollection<TKey, TValue> : IDisposable {
|
|
||||||
public void Dispose() { }
|
|
||||||
readonly IPairList<TKey, TValue> _pairList;
|
|
||||||
readonly IDictionary<TKey, TValue> _dictionary;
|
|
||||||
public PairCollection(object collection) : this() {
|
|
||||||
var type = collection.GetType();
|
|
||||||
if (typeof(IPairList<TKey, TValue>).IsAssignableFrom(type)) _pairList = (IPairList<TKey, TValue>)collection;
|
|
||||||
else if (typeof(IDictionary<TKey, TValue>).IsAssignableFrom(type)) _dictionary = (IDictionary<TKey, TValue>)collection;
|
|
||||||
else throw new ArgumentException("Parameter is not a pair collection");
|
|
||||||
}
|
|
||||||
public int Count {
|
|
||||||
get {
|
|
||||||
if (_pairList != null) return _pairList.Count;
|
|
||||||
else return _dictionary.Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void Add(TKey key, TValue value) {
|
|
||||||
if (_pairList != null) _pairList.Add(key, value);
|
|
||||||
else _dictionary.Add(key, value);
|
|
||||||
}
|
|
||||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int index) {
|
|
||||||
if (_pairList != null) _pairList.CopyTo(array, index);
|
|
||||||
else _dictionary.CopyTo(array, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsPairCollection(Type type) {
|
|
||||||
return typeof(IPairList<TKey, TValue>).IsAssignableFrom(type) || typeof(IDictionary<TKey, TValue>).IsAssignableFrom(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal class PairCollectionDebugView<TKey, TValue> {
|
|
||||||
readonly PairCollection<TKey, TValue> _self;
|
|
||||||
public PairCollectionDebugView(PairCollection<TKey, TValue> self) {
|
|
||||||
_self = self;
|
|
||||||
}
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
|
|
||||||
public KeyValuePair<TKey, TValue>[] Items {
|
|
||||||
get {
|
|
||||||
KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[_self.Count];
|
|
||||||
_self.CopyTo(array, 0);
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 2517e8f040bd36f46948e5fafaf5335c
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Cryville.Common.Collections.Generic {
|
|
||||||
[DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy(typeof(PairListDebugView<,>))]
|
|
||||||
public class PairList<TKey, TValue> : List<KeyValuePair<TKey, TValue>>, IPairList<TKey, TValue>, IPairList {
|
|
||||||
public void Add(TKey key, TValue value) {
|
|
||||||
Add(new KeyValuePair<TKey, TValue>(key, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Add(object key, object value) {
|
|
||||||
try {
|
|
||||||
Add((TKey)key, (TValue)value);
|
|
||||||
}
|
|
||||||
catch (InvalidCastException) {
|
|
||||||
throw new ArgumentException("Wrong key type or value type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Insert(int index, TKey key, TValue value) {
|
|
||||||
Insert(index, new KeyValuePair<TKey, TValue>(key, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Insert(int index, object key, object value) {
|
|
||||||
try {
|
|
||||||
Insert(index, (TKey)key, (TValue)value);
|
|
||||||
}
|
|
||||||
catch (InvalidCastException) {
|
|
||||||
throw new ArgumentException("Wrong key type or value type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal class PairListDebugView<TKey, TValue> {
|
|
||||||
readonly PairList<TKey, TValue> _self;
|
|
||||||
public PairListDebugView(PairList<TKey, TValue> self) {
|
|
||||||
_self = self;
|
|
||||||
}
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
|
|
||||||
public KeyValuePair<TKey, TValue>[] Items {
|
|
||||||
get {
|
|
||||||
KeyValuePair<TKey, TValue>[] array = new KeyValuePair<TKey, TValue>[_self.Count];
|
|
||||||
_self.CopyTo(array, 0);
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: d9ed5ea8b7b1a934287e7ec5971166c0
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics.Contracts;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.ConstrainedExecution;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Cryville.Common.Collections {
|
|
||||||
internal static class HashHelpers {
|
|
||||||
#if FEATURE_RANDOMIZED_STRING_HASHING
|
|
||||||
public const int HashCollisionThreshold = 100;
|
|
||||||
public static bool s_UseRandomizedStringHashing = String.UseRandomizedHashing();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Table of prime numbers to use as hash table sizes.
|
|
||||||
// A typical resize algorithm would pick the smallest prime number in this array
|
|
||||||
// that is larger than twice the previous capacity.
|
|
||||||
// Suppose our Hashtable currently has capacity x and enough elements are added
|
|
||||||
// such that a resize needs to occur. Resizing first computes 2x then finds the
|
|
||||||
// first prime in the table greater than 2x, i.e. if primes are ordered
|
|
||||||
// p_1, p_2, ..., p_i, ..., it finds p_n such that p_n-1 < 2x < p_n.
|
|
||||||
// Doubling is important for preserving the asymptotic complexity of the
|
|
||||||
// hashtable operations such as add. Having a prime guarantees that double
|
|
||||||
// hashing does not lead to infinite loops. IE, your hash function will be
|
|
||||||
// h1(key) + i*h2(key), 0 <= i < size. h2 and the size must be relatively prime.
|
|
||||||
public static readonly int[] primes = {
|
|
||||||
3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
|
|
||||||
1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
|
|
||||||
17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
|
|
||||||
187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
|
|
||||||
1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369};
|
|
||||||
|
|
||||||
// Used by Hashtable and Dictionary's SeralizationInfo .ctor's to store the SeralizationInfo
|
|
||||||
// object until OnDeserialization is called.
|
|
||||||
private static ConditionalWeakTable<object, SerializationInfo> s_SerializationInfoTable;
|
|
||||||
|
|
||||||
internal static ConditionalWeakTable<object, SerializationInfo> SerializationInfoTable {
|
|
||||||
get {
|
|
||||||
if (s_SerializationInfoTable == null) {
|
|
||||||
ConditionalWeakTable<object, SerializationInfo> newTable = new ConditionalWeakTable<object, SerializationInfo>();
|
|
||||||
Interlocked.CompareExchange(ref s_SerializationInfoTable, newTable, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return s_SerializationInfoTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|
||||||
public static bool IsPrime(int candidate) {
|
|
||||||
if ((candidate & 1) != 0) {
|
|
||||||
int limit = (int)System.Math.Sqrt (candidate);
|
|
||||||
for (int divisor = 3; divisor <= limit; divisor += 2) {
|
|
||||||
if ((candidate % divisor) == 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return (candidate == 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal const Int32 HashPrime = 101;
|
|
||||||
|
|
||||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|
||||||
public static int GetPrime(int min) {
|
|
||||||
if (min < 0)
|
|
||||||
throw new ArgumentException("Hashtable's capacity overflowed and went negative. Check load factor, capacity and the current size of the table.");
|
|
||||||
Contract.EndContractBlock();
|
|
||||||
|
|
||||||
for (int i = 0; i < primes.Length; i++) {
|
|
||||||
int prime = primes[i];
|
|
||||||
if (prime >= min) return prime;
|
|
||||||
}
|
|
||||||
|
|
||||||
//outside of our predefined table.
|
|
||||||
//compute the hard way.
|
|
||||||
for (int i = (min | 1); i < Int32.MaxValue; i += 2) {
|
|
||||||
if (IsPrime(i) && ((i - 1) % HashPrime != 0))
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
return min;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetMinPrime() {
|
|
||||||
return primes[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns size of hashtable to grow to.
|
|
||||||
public static int ExpandPrime(int oldSize) {
|
|
||||||
int newSize = 2 * oldSize;
|
|
||||||
|
|
||||||
// Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
|
|
||||||
// Note that this check works even when _items.Length overflowed thanks to the (uint) cast
|
|
||||||
if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize) {
|
|
||||||
Contract.Assert(MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
|
|
||||||
return MaxPrimeArrayLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetPrime(newSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// This is the maximum prime smaller than Array.MaxArrayLength
|
|
||||||
public const int MaxPrimeArrayLength = 0x7FEFFFFD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: e7fe2c6f3299681448c1a546cce4dc65
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
|
|
||||||
namespace Cryville.Common.Collections {
|
|
||||||
public interface IPairList : IList {
|
|
||||||
void Add(object key, object value);
|
|
||||||
void Insert(int index, object key, object value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 046617672d437de4ab7e644a55defd3b
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Cryville.Common.Collections {
|
|
||||||
[DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy(typeof(PairCollectionDebugView))]
|
|
||||||
public struct PairCollection : IDisposable {
|
|
||||||
public void Dispose() { }
|
|
||||||
readonly IPairList _pairList;
|
|
||||||
readonly IDictionary _dictionary;
|
|
||||||
public PairCollection(object collection) : this() {
|
|
||||||
var type = collection.GetType();
|
|
||||||
if (typeof(IPairList).IsAssignableFrom(type)) _pairList = (IPairList)collection;
|
|
||||||
else if (typeof(IDictionary).IsAssignableFrom(type)) _dictionary = (IDictionary)collection;
|
|
||||||
else throw new ArgumentException("Parameter is not a pair collection");
|
|
||||||
}
|
|
||||||
public int Count {
|
|
||||||
get {
|
|
||||||
if (_pairList != null) return _pairList.Count;
|
|
||||||
else return _dictionary.Count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void Add(object key, object value) {
|
|
||||||
if (_pairList != null) _pairList.Add(key, value);
|
|
||||||
else _dictionary.Add(key, value);
|
|
||||||
}
|
|
||||||
public void CopyTo(KeyValuePair<object, object>[] array, int index) {
|
|
||||||
if (_pairList != null) _pairList.CopyTo(array, index);
|
|
||||||
else _dictionary.CopyTo(array, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsPairCollection(Type type) {
|
|
||||||
return typeof(IPairList).IsAssignableFrom(type) || typeof(IDictionary).IsAssignableFrom(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal class PairCollectionDebugView {
|
|
||||||
readonly PairCollection _self;
|
|
||||||
public PairCollectionDebugView(PairCollection self) {
|
|
||||||
_self = self;
|
|
||||||
}
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
|
|
||||||
public KeyValuePair<object, object>[] Items {
|
|
||||||
get {
|
|
||||||
KeyValuePair<object, object>[] array = new KeyValuePair<object, object>[_self.Count];
|
|
||||||
_self.CopyTo(array, 0);
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 1f87dfb8f6a1f5640b6deae741cd715c
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace Cryville.Common.Collections {
|
|
||||||
[DebuggerDisplay("Count = {Count}"), DebuggerTypeProxy(typeof(PairListDebugView))]
|
|
||||||
public class PairList : List<KeyValuePair<object, object>>, IPairList {
|
|
||||||
public void Add(object key, object value) {
|
|
||||||
Add(new KeyValuePair<object, object>(key, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Insert(int index, object key, object value) {
|
|
||||||
Insert(index, new KeyValuePair<object, object>(key, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal class PairListDebugView {
|
|
||||||
readonly PairList _self;
|
|
||||||
public PairListDebugView(PairList self) {
|
|
||||||
_self = self;
|
|
||||||
}
|
|
||||||
[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
|
|
||||||
public KeyValuePair<object, object>[] Items {
|
|
||||||
get {
|
|
||||||
KeyValuePair<object, object>[] array = new KeyValuePair<object, object>[_self.Count];
|
|
||||||
_self.CopyTo(array, 0);
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 57fc9f037c1fda5449e2a365a835c82c
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 634536d804abc784394d4ac028a77879
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -3,12 +3,12 @@ using System;
|
|||||||
namespace Cryville.Common.ComponentModel {
|
namespace Cryville.Common.ComponentModel {
|
||||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
||||||
public class RangeAttribute : Attribute {
|
public class RangeAttribute : Attribute {
|
||||||
public RangeAttribute(float min, float max) {
|
public RangeAttribute(double min, double max) {
|
||||||
Min = min;
|
Min = min;
|
||||||
Max = max;
|
Max = max;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float Min { get; set; }
|
public double Min { get; set; }
|
||||||
public float Max { get; set; }
|
public double Max { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ using System;
|
|||||||
namespace Cryville.Common.ComponentModel {
|
namespace Cryville.Common.ComponentModel {
|
||||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
||||||
public class StepAttribute : Attribute {
|
public class StepAttribute : Attribute {
|
||||||
public StepAttribute(float step) {
|
public StepAttribute(double step) {
|
||||||
Step = step;
|
Step = step;
|
||||||
}
|
}
|
||||||
public float Step { get; set; }
|
public double Step { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 520554ce9a8205b4b91e0ff2b8011673
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: ae9dab8f520fadc4194032f523ca87c1
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,27 +1,30 @@
|
|||||||
|
using Cryville.Common.Logging;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Cryville.Common.Font {
|
namespace Cryville.Common.Font {
|
||||||
public abstract class FontManager {
|
public abstract class FontManager {
|
||||||
public IReadOnlyDictionary<string, IReadOnlyCollection<Typeface>> MapFullNameToTypeface { get; private set; }
|
public IReadOnlyDictionary<string, Typeface> MapFullNameToTypeface { get; private set; }
|
||||||
public IReadOnlyDictionary<string, IReadOnlyCollection<Typeface>> MapNameToTypefaces { get; private set; }
|
public IReadOnlyDictionary<string, IReadOnlyCollection<Typeface>> MapNameToTypefaces { get; private set; }
|
||||||
public FontManager() {
|
public FontManager() {
|
||||||
var map1 = new Dictionary<string, List<Typeface>>();
|
var map1 = new Dictionary<string, Typeface>();
|
||||||
var map2 = new Dictionary<string, List<Typeface>>();
|
var map2 = new Dictionary<string, List<Typeface>>();
|
||||||
foreach (var f in EnumerateAllTypefaces()) {
|
foreach (var f in EnumerateAllTypefaces()) {
|
||||||
List<Typeface> set1;
|
if (!map1.ContainsKey(f.FullName)) {
|
||||||
if (!map1.TryGetValue(f.FullName, out set1)) {
|
map1.Add(f.FullName, f);
|
||||||
map1.Add(f.FullName, set1 = new List<Typeface>());
|
}
|
||||||
|
else {
|
||||||
|
Logger.Log("main", 3, "UI", "Discarding a font with a duplicate full name {0}", f.FullName);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
set1.Add(f);
|
|
||||||
List<Typeface> set2;
|
List<Typeface> set2;
|
||||||
if (!map2.TryGetValue(f.FamilyName, out set2)) {
|
if (!map2.TryGetValue(f.FamilyName, out set2)) {
|
||||||
map2.Add(f.FamilyName, set2 = new List<Typeface>());
|
map2.Add(f.FamilyName, set2 = new List<Typeface>());
|
||||||
}
|
}
|
||||||
set2.Add(f);
|
set2.Add(f);
|
||||||
}
|
}
|
||||||
MapFullNameToTypeface = map1.ToDictionary(i => i.Key, i => (IReadOnlyCollection<Typeface>)i.Value);
|
MapFullNameToTypeface = map1;
|
||||||
MapNameToTypefaces = map2.ToDictionary(i => i.Key, i => (IReadOnlyCollection<Typeface>)i.Value);
|
MapNameToTypefaces = map2.ToDictionary(i => i.Key, i => (IReadOnlyCollection<Typeface>)i.Value);
|
||||||
}
|
}
|
||||||
protected abstract IEnumerable<Typeface> EnumerateAllTypefaces();
|
protected abstract IEnumerable<Typeface> EnumerateAllTypefaces();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Cryville.Common.Culture;
|
using Cryville.Common.Logging;
|
||||||
|
using Cryville.Culture;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -6,323 +7,339 @@ using System.Linq;
|
|||||||
namespace Cryville.Common.Font {
|
namespace Cryville.Common.Font {
|
||||||
public abstract class FontMatcher {
|
public abstract class FontMatcher {
|
||||||
protected FontManager Manager { get; private set; }
|
protected FontManager Manager { get; private set; }
|
||||||
public FontMatcher(FontManager manafer) { Manager = manafer; }
|
public FontMatcher(FontManager manager) { Manager = manager; }
|
||||||
public abstract IEnumerable<Typeface> MatchScript(string script = null, bool distinctFamily = false);
|
public abstract IEnumerable<Typeface> MatchLanguage(LanguageId lang, bool distinctFamily = false);
|
||||||
}
|
}
|
||||||
public class FallbackListFontMatcher : FontMatcher {
|
public class FallbackListFontMatcher : FontMatcher {
|
||||||
|
readonly LanguageMatching _matcher;
|
||||||
|
static readonly string UltimateFallbackScript = "zzzz";
|
||||||
public Dictionary<string, List<string>> MapScriptToTypefaces = new Dictionary<string, List<string>>();
|
public Dictionary<string, List<string>> MapScriptToTypefaces = new Dictionary<string, List<string>>();
|
||||||
public void LoadDefaultWindowsFallbackList() {
|
public static Dictionary<string, List<string>> GetDefaultWindowsFallbackMap() {
|
||||||
if (Environment.OSVersion.Platform != PlatformID.Win32NT) return;
|
var map = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||||
MapScriptToTypefaces.Clear();
|
FillKeysWithScripts(map, () => new List<string>());
|
||||||
ScriptUtils.FillKeysWithScripts(MapScriptToTypefaces, () => new List<string>());
|
|
||||||
// Reference: https://github.com/chromium/chromium/blob/main/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc
|
// Reference: https://github.com/chromium/chromium/blob/main/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc
|
||||||
MapScriptToTypefaces["zyyy"].Insert(0, "SimSun"); // Custom
|
map[UltimateFallbackScript].Insert(0, "SimSun"); // Custom
|
||||||
MapScriptToTypefaces["zyyy"].Insert(0, "SimHei"); // Custom
|
map[UltimateFallbackScript].Insert(0, "SimHei"); // Custom
|
||||||
MapScriptToTypefaces["zyyy"].Insert(0, "Microsoft YaHei"); // Custom
|
map[UltimateFallbackScript].Insert(0, "Microsoft YaHei"); // Custom
|
||||||
MapScriptToTypefaces["zyyy"].Insert(0, "Arial");
|
map[UltimateFallbackScript].Insert(0, "Arial");
|
||||||
MapScriptToTypefaces["zyyy"].Insert(0, "Times New Roman");
|
map[UltimateFallbackScript].Insert(0, "Times New Roman");
|
||||||
MapScriptToTypefaces["zyyy"].Insert(0, "Segoe UI"); // Custom
|
map[UltimateFallbackScript].Insert(0, "Segoe UI"); // Custom
|
||||||
MapScriptToTypefaces["arab"].Insert(0, "Tahoma");
|
map["arab"].Insert(0, "Tahoma");
|
||||||
MapScriptToTypefaces["cyrl"].Insert(0, "Times New Roman");
|
map["cyrl"].Insert(0, "Times New Roman");
|
||||||
MapScriptToTypefaces["grek"].Insert(0, "Times New Roman");
|
map["grek"].Insert(0, "Times New Roman");
|
||||||
MapScriptToTypefaces["hebr"].Insert(0, "David");
|
map["hebr"].Insert(0, "David");
|
||||||
MapScriptToTypefaces["jpan"].Insert(0, "MS PGothic");
|
map["jpan"].Insert(0, "MS PGothic");
|
||||||
MapScriptToTypefaces["latn"].Insert(0, "Times New Roman");
|
map["latn"].Insert(0, "Times New Roman");
|
||||||
MapScriptToTypefaces["hans"].Insert(0, "SimSun");
|
map["hans"].Insert(0, "SimSun");
|
||||||
MapScriptToTypefaces["hans"].Insert(0, "SimHei"); // Custom
|
map["hans"].Insert(0, "SimHei"); // Custom
|
||||||
MapScriptToTypefaces["thai"].Insert(0, "Tahoma");
|
map["thai"].Insert(0, "Tahoma");
|
||||||
MapScriptToTypefaces["hans"].Insert(0, "PMingLiU");
|
map["hans"].Insert(0, "PMingLiU");
|
||||||
// Reference: https://learn.microsoft.com/en-us/globalization/input/font-support
|
// Reference: https://learn.microsoft.com/en-us/globalization/input/font-support
|
||||||
var ver = Environment.OSVersion.Version;
|
var ver = Environment.OSVersion.Version;
|
||||||
if (ver >= new Version(5, 0)) { // Windows 2000
|
if (ver >= new Version(5, 0)) { // Windows 2000
|
||||||
MapScriptToTypefaces["armn"].Insert(0, "Sylfaen");
|
map["armn"].Insert(0, "Sylfaen");
|
||||||
MapScriptToTypefaces["deva"].Insert(0, "Mangal");
|
map["deva"].Insert(0, "Mangal");
|
||||||
MapScriptToTypefaces["geor"].Insert(0, "Sylfaen");
|
map["geor"].Insert(0, "Sylfaen");
|
||||||
MapScriptToTypefaces["taml"].Insert(0, "Latha");
|
map["taml"].Insert(0, "Latha");
|
||||||
}
|
}
|
||||||
if (ver >= new Version(5, 1)) { // Windows XP
|
if (ver >= new Version(5, 1)) { // Windows XP
|
||||||
MapScriptToTypefaces["gujr"].Insert(0, "Shruti");
|
map["gujr"].Insert(0, "Shruti");
|
||||||
MapScriptToTypefaces["guru"].Insert(0, "Raavi");
|
map["guru"].Insert(0, "Raavi");
|
||||||
MapScriptToTypefaces["knda"].Insert(0, "Tunga");
|
map["knda"].Insert(0, "Tunga");
|
||||||
MapScriptToTypefaces["syrc"].Insert(0, "Estrangelo Edessa");
|
map["syrc"].Insert(0, "Estrangelo Edessa");
|
||||||
MapScriptToTypefaces["telu"].Insert(0, "Gautami");
|
map["telu"].Insert(0, "Gautami");
|
||||||
MapScriptToTypefaces["thaa"].Insert(0, "MV Boli");
|
map["thaa"].Insert(0, "MV Boli");
|
||||||
// SP2
|
// SP2
|
||||||
MapScriptToTypefaces["beng"].Insert(0, "Vrinda");
|
map["beng"].Insert(0, "Vrinda");
|
||||||
MapScriptToTypefaces["mlym"].Insert(0, "Kartika");
|
map["mlym"].Insert(0, "Kartika");
|
||||||
}
|
}
|
||||||
if (ver >= new Version(6, 0)) { // Windows Vista
|
if (ver >= new Version(6, 0)) { // Windows Vista
|
||||||
MapScriptToTypefaces["cans"].Insert(0, "Euphemia");
|
map["cans"].Insert(0, "Euphemia");
|
||||||
MapScriptToTypefaces["cher"].Insert(0, "Plantagenet");
|
map["cher"].Insert(0, "Plantagenet");
|
||||||
MapScriptToTypefaces["ethi"].Insert(0, "Nyala");
|
map["ethi"].Insert(0, "Nyala");
|
||||||
MapScriptToTypefaces["khmr"].Insert(0, "DaunPenh MoolBoran");
|
map["khmr"].Insert(0, "DaunPenh MoolBoran");
|
||||||
MapScriptToTypefaces["laoo"].Insert(0, "DokChampa");
|
map["laoo"].Insert(0, "DokChampa");
|
||||||
MapScriptToTypefaces["mong"].Insert(0, "Mongolian Baiti");
|
map["mong"].Insert(0, "Mongolian Baiti");
|
||||||
MapScriptToTypefaces["orya"].Insert(0, "Kalinga");
|
map["orya"].Insert(0, "Kalinga");
|
||||||
MapScriptToTypefaces["sinh"].Insert(0, "Iskoola Pota");
|
map["sinh"].Insert(0, "Iskoola Pota");
|
||||||
MapScriptToTypefaces["tibt"].Insert(0, "Microsoft Himalaya");
|
map["tibt"].Insert(0, "Microsoft Himalaya");
|
||||||
MapScriptToTypefaces["yiii"].Insert(0, "Microsoft Yi Baiti");
|
map["yiii"].Insert(0, "Microsoft Yi Baiti");
|
||||||
MapScriptToTypefaces["arab"].Insert(0, "Segoe UI");
|
map["arab"].Insert(0, "Segoe UI");
|
||||||
MapScriptToTypefaces["cyrl"].Insert(0, "Segoe UI");
|
map["cyrl"].Insert(0, "Segoe UI");
|
||||||
MapScriptToTypefaces["grek"].Insert(0, "Segoe UI");
|
map["grek"].Insert(0, "Segoe UI");
|
||||||
MapScriptToTypefaces["latn"].Insert(0, "Segoe UI");
|
map["latn"].Insert(0, "Segoe UI");
|
||||||
MapScriptToTypefaces["hans"].Add("SimSun-ExtB");
|
map["hans"].Add("SimSun-ExtB");
|
||||||
MapScriptToTypefaces["hant"].Add("MingLiU-ExtB");
|
map["hant"].Add("MingLiU-ExtB");
|
||||||
MapScriptToTypefaces["hant"].Add("MingLiU_HKSCS-ExtB");
|
map["hant"].Add("MingLiU_HKSCS-ExtB");
|
||||||
MapScriptToTypefaces["arab"].Add("Microsoft Uighur");
|
map["arab"].Add("Microsoft Uighur");
|
||||||
MapScriptToTypefaces["zmth"].Insert(0, "Cambria Math");
|
map["zmth"].Insert(0, "Cambria Math");
|
||||||
// Reference: https://en.wikipedia.org/wiki/List_of_CJK_fonts
|
// Reference: https://en.wikipedia.org/wiki/List_of_CJK_fonts
|
||||||
MapScriptToTypefaces["jpan"].Insert(0, "Meiryo");
|
map["jpan"].Insert(0, "Meiryo");
|
||||||
MapScriptToTypefaces["hans"].Insert(0, "Microsoft YaHei");
|
map["hans"].Insert(0, "Microsoft YaHei");
|
||||||
}
|
}
|
||||||
if (ver >= new Version(6, 1)) { // Windows 7
|
if (ver >= new Version(6, 1)) { // Windows 7
|
||||||
MapScriptToTypefaces["brai"].Insert(0, "Segoe UI Symbol");
|
map["brai"].Insert(0, "Segoe UI Symbol");
|
||||||
MapScriptToTypefaces["dsrt"].Insert(0, "Segoe UI Symbol");
|
map["dsrt"].Insert(0, "Segoe UI Symbol");
|
||||||
MapScriptToTypefaces["talu"].Insert(0, "Microsoft New Tai Lue");
|
map["talu"].Insert(0, "Microsoft New Tai Lue");
|
||||||
MapScriptToTypefaces["ogam"].Insert(0, "Segoe UI Symbol");
|
map["ogam"].Insert(0, "Segoe UI Symbol");
|
||||||
MapScriptToTypefaces["osma"].Insert(0, "Ebrima");
|
map["osma"].Insert(0, "Ebrima");
|
||||||
MapScriptToTypefaces["phag"].Insert(0, "Microsoft PhagsPa");
|
map["phag"].Insert(0, "Microsoft PhagsPa");
|
||||||
MapScriptToTypefaces["runr"].Insert(0, "Segoe UI Symbol");
|
map["runr"].Insert(0, "Segoe UI Symbol");
|
||||||
MapScriptToTypefaces["zsym"].Insert(0, "Segoe UI Symbol");
|
map["zsym"].Insert(0, "Segoe UI Symbol");
|
||||||
MapScriptToTypefaces["tale"].Insert(0, "Microsoft Tai Le");
|
map["tale"].Insert(0, "Microsoft Tai Le");
|
||||||
MapScriptToTypefaces["tfng"].Insert(0, "Ebrima");
|
map["tfng"].Insert(0, "Ebrima");
|
||||||
MapScriptToTypefaces["vaii"].Insert(0, "Ebrima");
|
map["vaii"].Insert(0, "Ebrima");
|
||||||
}
|
}
|
||||||
if (ver >= new Version(6, 2)) { // Windows 8
|
if (ver >= new Version(6, 2)) { // Windows 8
|
||||||
MapScriptToTypefaces["glag"].Insert(0, "Segoe UI Symbol");
|
map["glag"].Insert(0, "Segoe UI Symbol");
|
||||||
MapScriptToTypefaces["goth"].Insert(0, "Segoe UI Symbol");
|
map["goth"].Insert(0, "Segoe UI Symbol");
|
||||||
MapScriptToTypefaces["hang"].Add("Malgun Gothic");
|
map["hang"].Add("Malgun Gothic");
|
||||||
MapScriptToTypefaces["ital"].Insert(0, "Segoe UI Symbol");
|
map["ital"].Insert(0, "Segoe UI Symbol");
|
||||||
MapScriptToTypefaces["lisu"].Insert(0, "Segoe UI");
|
map["lisu"].Insert(0, "Segoe UI");
|
||||||
MapScriptToTypefaces["mymr"].Insert(0, "Myanmar Text");
|
map["mymr"].Insert(0, "Myanmar Text");
|
||||||
MapScriptToTypefaces["nkoo"].Insert(0, "Ebrima");
|
map["nkoo"].Insert(0, "Ebrima");
|
||||||
MapScriptToTypefaces["orkh"].Insert(0, "Segoe UI Symbol");
|
map["orkh"].Insert(0, "Segoe UI Symbol");
|
||||||
MapScriptToTypefaces["ethi"].Insert(0, "Ebrima");
|
map["ethi"].Insert(0, "Ebrima");
|
||||||
MapScriptToTypefaces["cans"].Insert(0, "Gadugi");
|
map["cans"].Insert(0, "Gadugi");
|
||||||
MapScriptToTypefaces["hant"].Insert(0, "Microsoft JhengHei UI");
|
map["hant"].Insert(0, "Microsoft JhengHei UI");
|
||||||
MapScriptToTypefaces["hans"].Insert(0, "Microsoft YaHei UI");
|
map["hans"].Insert(0, "Microsoft YaHei UI");
|
||||||
MapScriptToTypefaces["beng"].Insert(0, "Nirmala UI");
|
map["beng"].Insert(0, "Nirmala UI");
|
||||||
MapScriptToTypefaces["deva"].Insert(0, "Nirmala UI");
|
map["deva"].Insert(0, "Nirmala UI");
|
||||||
MapScriptToTypefaces["gujr"].Insert(0, "Nirmala UI");
|
map["gujr"].Insert(0, "Nirmala UI");
|
||||||
MapScriptToTypefaces["guru"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
map["guru"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
||||||
MapScriptToTypefaces["knda"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
map["knda"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
||||||
MapScriptToTypefaces["mlym"].Insert(0, "Nirmala UI");
|
map["mlym"].Insert(0, "Nirmala UI");
|
||||||
MapScriptToTypefaces["orya"].Insert(0, "Nirmala UI");
|
map["orya"].Insert(0, "Nirmala UI");
|
||||||
MapScriptToTypefaces["sinh"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
map["sinh"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
||||||
MapScriptToTypefaces["taml"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
map["taml"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
|
||||||
MapScriptToTypefaces["telu"].Insert(0, "Nirmala UI");
|
map["telu"].Insert(0, "Nirmala UI");
|
||||||
MapScriptToTypefaces["armn"].Insert(0, "Segoe UI");
|
map["armn"].Insert(0, "Segoe UI");
|
||||||
MapScriptToTypefaces["geor"].Insert(0, "Segoe UI");
|
map["geor"].Insert(0, "Segoe UI");
|
||||||
MapScriptToTypefaces["hebr"].Insert(0, "Segoe UI");
|
map["hebr"].Insert(0, "Segoe UI");
|
||||||
}
|
}
|
||||||
if (ver >= new Version(6, 3)) { // Windows 8.1
|
if (ver >= new Version(6, 3)) { // Windows 8.1
|
||||||
MapScriptToTypefaces["bugi"].Insert(0, "Leelawadee UI");
|
map["bugi"].Insert(0, "Leelawadee UI");
|
||||||
MapScriptToTypefaces["copt"].Insert(0, "Segoe UI Symbol");
|
map["copt"].Insert(0, "Segoe UI Symbol");
|
||||||
MapScriptToTypefaces["java"].Insert(0, "Javanese Text");
|
map["java"].Insert(0, "Javanese Text");
|
||||||
MapScriptToTypefaces["merc"].Insert(0, "Segoe UI Symbol");
|
map["merc"].Insert(0, "Segoe UI Symbol");
|
||||||
MapScriptToTypefaces["olck"].Insert(0, "Nirmala UI");
|
map["olck"].Insert(0, "Nirmala UI");
|
||||||
MapScriptToTypefaces["sora"].Insert(0, "Nirmala UI");
|
map["sora"].Insert(0, "Nirmala UI");
|
||||||
MapScriptToTypefaces["khmr"].Insert(0, "Leelawadee UI");
|
map["khmr"].Insert(0, "Leelawadee UI");
|
||||||
MapScriptToTypefaces["laoo"].Insert(0, "Leelawadee UI");
|
map["laoo"].Insert(0, "Leelawadee UI");
|
||||||
MapScriptToTypefaces["thai"].Insert(0, "Leelawadee UI");
|
map["thai"].Insert(0, "Leelawadee UI");
|
||||||
MapScriptToTypefaces["zsye"].Insert(0, "Segoe UI Emoji");
|
map["zsye"].Insert(0, "Segoe UI Emoji");
|
||||||
}
|
}
|
||||||
if (ver >= new Version(10, 0)) { // Windows 10
|
if (ver >= new Version(10, 0)) { // Windows 10
|
||||||
MapScriptToTypefaces["brah"].Insert(0, "Segoe UI Historic");
|
map["brah"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["cari"].Insert(0, "Segoe UI Historic");
|
map["cari"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["cprt"].Insert(0, "Segoe UI Historic");
|
map["cprt"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["egyp"].Insert(0, "Segoe UI Historic");
|
map["egyp"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["armi"].Insert(0, "Segoe UI Historic");
|
map["armi"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["phli"].Insert(0, "Segoe UI Historic");
|
map["phli"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["prti"].Insert(0, "Segoe UI Historic");
|
map["prti"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["khar"].Insert(0, "Segoe UI Historic");
|
map["khar"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["lyci"].Insert(0, "Segoe UI Historic");
|
map["lyci"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["lydi"].Insert(0, "Segoe UI Historic");
|
map["lydi"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["phnx"].Insert(0, "Segoe UI Historic");
|
map["phnx"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["xpeo"].Insert(0, "Segoe UI Historic");
|
map["xpeo"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["sarb"].Insert(0, "Segoe UI Historic");
|
map["sarb"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["shaw"].Insert(0, "Segoe UI Historic");
|
map["shaw"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["xsux"].Insert(0, "Segoe UI Historic");
|
map["xsux"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["ugar"].Insert(0, "Segoe UI Historic");
|
map["ugar"].Insert(0, "Segoe UI Historic");
|
||||||
// Segoe UI Symbol -> Segoe UI Historic
|
// Segoe UI Symbol -> Segoe UI Historic
|
||||||
MapScriptToTypefaces["glag"].Insert(0, "Segoe UI Historic");
|
map["glag"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["goth"].Insert(0, "Segoe UI Historic");
|
map["goth"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["merc"].Insert(0, "Segoe UI Historic");
|
map["merc"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["ogam"].Insert(0, "Segoe UI Historic");
|
map["ogam"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["ital"].Insert(0, "Segoe UI Historic");
|
map["ital"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["orkh"].Insert(0, "Segoe UI Historic");
|
map["orkh"].Insert(0, "Segoe UI Historic");
|
||||||
MapScriptToTypefaces["runr"].Insert(0, "Segoe UI Historic");
|
map["runr"].Insert(0, "Segoe UI Historic");
|
||||||
//
|
//
|
||||||
MapScriptToTypefaces["jpan"].Insert(0, "Yu Gothic UI");
|
map["jpan"].Insert(0, "Yu Gothic UI");
|
||||||
MapScriptToTypefaces["zsym"].Add("Segoe MDL2 Assets");
|
map["zsym"].Add("Segoe MDL2 Assets");
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
public static Dictionary<string, List<string>> GetDefaultAndroidFallbackMap() {
|
||||||
|
var map = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
FillKeysWithScripts(map, () => new List<string>());
|
||||||
|
map[UltimateFallbackScript].Insert(0, "Noto Sans CJK TC"); // TODO Modify default fallback
|
||||||
|
map[UltimateFallbackScript].Insert(0, "Noto Sans CJK JP");
|
||||||
|
map[UltimateFallbackScript].Insert(0, "Noto Sans CJK SC");
|
||||||
|
map[UltimateFallbackScript].Insert(0, "Roboto");
|
||||||
|
map["zsye"].Insert(0, "Noto Color Emoji");
|
||||||
|
map["zsye"].Add("Noto Color Emoji Flags");
|
||||||
|
map["arab"].Insert(0, "Noto Naskh Arabic");
|
||||||
|
map["adlm"].Insert(0, "Noto Sans Adlam");
|
||||||
|
map["ahom"].Insert(0, "Noto Sans Ahom");
|
||||||
|
map["hluw"].Insert(0, "Noto Sans Anatolian Hieroglyphs");
|
||||||
|
map["armn"].Insert(0, "Noto Sans Armenian");
|
||||||
|
map["avst"].Insert(0, "Noto Sans Avestan");
|
||||||
|
map["bali"].Insert(0, "Noto Sans Balinese");
|
||||||
|
map["bamu"].Insert(0, "Noto Sans Bamum");
|
||||||
|
map["bass"].Insert(0, "Noto Sans Bassa Vah");
|
||||||
|
map["batk"].Insert(0, "Noto Sans Batak");
|
||||||
|
map["beng"].Insert(0, "Noto Sans Bengali");
|
||||||
|
map["bhks"].Insert(0, "Noto Sans Bhaiksuki");
|
||||||
|
map["brah"].Insert(0, "Noto Sans Brahmi");
|
||||||
|
map["bugi"].Insert(0, "Noto Sans Buginese");
|
||||||
|
map["buhd"].Insert(0, "Noto Sans Buhid");
|
||||||
|
map["jpan"].Insert(0, "Noto Sans CJK JP");
|
||||||
|
map["kore"].Insert(0, "Noto Sans CJK KR");
|
||||||
|
map["hans"].Insert(0, "Noto Sans CJK SC");
|
||||||
|
map["hant"].Insert(0, "Noto Sans CJK TC");
|
||||||
|
map["hant"].Add("Noto Sans CJK HK");
|
||||||
|
map["cans"].Insert(0, "Noto Sans Canadian Aboriginal");
|
||||||
|
map["cari"].Insert(0, "Noto Sans Carian");
|
||||||
|
map["cakm"].Insert(0, "Noto Sans Chakma");
|
||||||
|
map["cham"].Insert(0, "Noto Sans Cham");
|
||||||
|
map["cher"].Insert(0, "Noto Sans Cherokee");
|
||||||
|
map["copt"].Insert(0, "Noto Sans Coptic");
|
||||||
|
map["xsux"].Insert(0, "Noto Sans Cuneiform");
|
||||||
|
map["cprt"].Insert(0, "Noto Sans Cypriot");
|
||||||
|
map["dsrt"].Insert(0, "Noto Sans Deseret");
|
||||||
|
map["deva"].Insert(0, "Noto Sans Devanagari");
|
||||||
|
map["egyp"].Insert(0, "Noto Sans Egyptian Hieroglyphs");
|
||||||
|
map["elba"].Insert(0, "Noto Sans Elbasan");
|
||||||
|
map["ethi"].Insert(0, "Noto Sans Ethiopic");
|
||||||
|
map["geor"].Insert(0, "Noto Sans Georgian");
|
||||||
|
map["glag"].Insert(0, "Noto Sans Glagolitic");
|
||||||
|
map["goth"].Insert(0, "Noto Sans Gothic");
|
||||||
|
map["gran"].Insert(0, "Noto Sans Grantha");
|
||||||
|
map["gujr"].Insert(0, "Noto Sans Gujarati");
|
||||||
|
map["gong"].Insert(0, "Noto Sans Gunjala Gondi");
|
||||||
|
map["guru"].Insert(0, "Noto Sans Gurmukhi");
|
||||||
|
map["rohg"].Insert(0, "Noto Sans Hanifi Rohingya");
|
||||||
|
map["hano"].Insert(0, "Noto Sans Hanunoo");
|
||||||
|
map["hatr"].Insert(0, "Noto Sans Hatran");
|
||||||
|
map["hebr"].Insert(0, "Noto Sans Hebrew");
|
||||||
|
map["armi"].Insert(0, "Noto Sans Imperial Aramaic");
|
||||||
|
map["phli"].Insert(0, "Noto Sans Inscriptional Pahlavi");
|
||||||
|
map["prti"].Insert(0, "Noto Sans Inscriptional Parthian");
|
||||||
|
map["java"].Insert(0, "Noto Sans Javanese");
|
||||||
|
map["kthi"].Insert(0, "Noto Sans Kaithi");
|
||||||
|
map["knda"].Insert(0, "Noto Sans Kannada");
|
||||||
|
map["kali"].Insert(0, "Noto Sans KayahLi");
|
||||||
|
map["khar"].Insert(0, "Noto Sans Kharoshthi");
|
||||||
|
map["khmr"].Insert(0, "Noto Sans Khmer");
|
||||||
|
map["khoj"].Insert(0, "Noto Sans Khojki");
|
||||||
|
map["laoo"].Insert(0, "Noto Sans Lao");
|
||||||
|
map["lepc"].Insert(0, "Noto Sans Lepcha");
|
||||||
|
map["limb"].Insert(0, "Noto Sans Limbu");
|
||||||
|
map["lina"].Insert(0, "Noto Sans Linear A");
|
||||||
|
map["linb"].Insert(0, "Noto Sans Linear B");
|
||||||
|
map["lisu"].Insert(0, "Noto Sans Lisu");
|
||||||
|
map["lyci"].Insert(0, "Noto Sans Lycian");
|
||||||
|
map["lydi"].Insert(0, "Noto Sans Lydian");
|
||||||
|
map["mlym"].Insert(0, "Noto Sans Malayalam");
|
||||||
|
map["mand"].Insert(0, "Noto Sans Mandiac");
|
||||||
|
map["mani"].Insert(0, "Noto Sans Manichaean");
|
||||||
|
map["marc"].Insert(0, "Noto Sans Marchen");
|
||||||
|
map["gonm"].Insert(0, "Noto Sans Masaram Gondi");
|
||||||
|
map["medf"].Insert(0, "Noto Sans Medefaidrin");
|
||||||
|
map["mtei"].Insert(0, "Noto Sans Meetei Mayek");
|
||||||
|
map["merc"].Insert(0, "Noto Sans Meroitic");
|
||||||
|
map["mero"].Insert(0, "Noto Sans Meroitic");
|
||||||
|
map["plrd"].Insert(0, "Noto Sans Miao");
|
||||||
|
map["modi"].Insert(0, "Noto Sans Modi");
|
||||||
|
map["mong"].Insert(0, "Noto Sans Mongolian");
|
||||||
|
map["mroo"].Insert(0, "Noto Sans Mro");
|
||||||
|
map["mult"].Insert(0, "Noto Sans Multani");
|
||||||
|
map["mymr"].Insert(0, "Noto Sans Myanmar");
|
||||||
|
map["nkoo"].Insert(0, "Noto Sans Nko");
|
||||||
|
map["nbat"].Insert(0, "Noto Sans Nabataean");
|
||||||
|
map["talu"].Insert(0, "Noto Sans New Tai Lue");
|
||||||
|
map["newa"].Insert(0, "Noto Sans Newa");
|
||||||
|
map["ogam"].Insert(0, "Noto Sans Ogham");
|
||||||
|
map["olck"].Insert(0, "Noto Sans Ol Chiki");
|
||||||
|
map["ital"].Insert(0, "Noto Sans Old Italian");
|
||||||
|
map["narb"].Insert(0, "Noto Sans Old North Arabian");
|
||||||
|
map["perm"].Insert(0, "Noto Sans Old Permic");
|
||||||
|
map["xpeo"].Insert(0, "Noto Sans Old Persian");
|
||||||
|
map["sarb"].Insert(0, "Noto Sans Old South Arabian");
|
||||||
|
map["orkh"].Insert(0, "Noto Sans Old Turkic");
|
||||||
|
map["orya"].Insert(0, "Noto Sans Oriya");
|
||||||
|
map["osge"].Insert(0, "Noto Sans Osage");
|
||||||
|
map["osma"].Insert(0, "Noto Sans Osmanya");
|
||||||
|
map["hmng"].Insert(0, "Noto Sans Pahawh Hmong");
|
||||||
|
map["palm"].Insert(0, "Noto Sans Palmyrene");
|
||||||
|
map["pauc"].Insert(0, "Noto Sans Pau Cin Hau");
|
||||||
|
map["phag"].Insert(0, "Noto Sans Phags Pa");
|
||||||
|
map["phnx"].Insert(0, "Noto Sans Phoenician");
|
||||||
|
map["rjng"].Insert(0, "Noto Sans Rejang");
|
||||||
|
map["runr"].Insert(0, "Noto Sans Runic");
|
||||||
|
map["samr"].Insert(0, "Noto Sans Samaritan");
|
||||||
|
map["saur"].Insert(0, "Noto Sans Saurashtra");
|
||||||
|
map["shrd"].Insert(0, "Noto Sans Sharada");
|
||||||
|
map["shaw"].Insert(0, "Noto Sans Shavian");
|
||||||
|
map["sinh"].Insert(0, "Noto Sans Sinhala");
|
||||||
|
map["sora"].Insert(0, "Noto Sans Sora Sompeng");
|
||||||
|
map["soyo"].Insert(0, "Noto Sans Soyombo");
|
||||||
|
map["sund"].Insert(0, "Noto Sans Sundanese");
|
||||||
|
map["sylo"].Insert(0, "Noto Sans Syloti Nagri");
|
||||||
|
map["zsym"].Insert(0, "Noto Sans Symbols");
|
||||||
|
map["syrc"].Add("Noto Sans Syriac Eastern");
|
||||||
|
map["syrc"].Add("Noto Sans Syriac Western");
|
||||||
|
map["syrc"].Add("Noto Sans Syriac Estrangela");
|
||||||
|
map["tglg"].Insert(0, "Noto Sans Tagalog");
|
||||||
|
map["tagb"].Insert(0, "Noto Sans Tagbanwa");
|
||||||
|
map["tale"].Insert(0, "Noto Sans Tai Le");
|
||||||
|
map["lana"].Insert(0, "Noto Sans Tai Tham");
|
||||||
|
map["tavt"].Insert(0, "Noto Sans Tai Viet");
|
||||||
|
map["takr"].Insert(0, "Noto Sans Takri");
|
||||||
|
map["taml"].Insert(0, "Noto Sans Tamil");
|
||||||
|
map["telu"].Insert(0, "Noto Sans Telugu");
|
||||||
|
map["thaa"].Insert(0, "Noto Sans Thaana");
|
||||||
|
map["thai"].Insert(0, "Noto Sans Thai");
|
||||||
|
map["tfng"].Insert(0, "Noto Sans Tifinagh");
|
||||||
|
map["ugar"].Insert(0, "Noto Sans Ugaritic");
|
||||||
|
map["vaii"].Insert(0, "Noto Sans Vai");
|
||||||
|
map["wcho"].Insert(0, "Noto Sans Wancho");
|
||||||
|
map["wara"].Insert(0, "Noto Sans Warang Citi");
|
||||||
|
map["yiii"].Insert(0, "Noto Sans Yi");
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
static void FillKeysWithScripts<T>(IDictionary<string, T> map, Func<T> value) {
|
||||||
|
foreach (var s in IdValidity.Enumerate("script")) map.Add(s, value());
|
||||||
|
}
|
||||||
|
|
||||||
|
public FallbackListFontMatcher(LanguageMatching matcher, FontManager manager) : base(manager) {
|
||||||
|
_matcher = matcher;
|
||||||
|
}
|
||||||
|
public override IEnumerable<Typeface> MatchLanguage(LanguageId lang, bool distinctFamily = false) {
|
||||||
|
var supported = MapScriptToTypefaces.Keys.Select(i => new LanguageId(i)).ToList();
|
||||||
|
bool flag = false;
|
||||||
|
while (_matcher.Match(lang, supported, out var match, out var distance)) {
|
||||||
|
if (distance > 40) break;
|
||||||
|
Logger.Log("main", 0, "UI", "Matching fonts for language {0}, distance = {1}", match, distance);
|
||||||
|
if (match.Script.Equals(UltimateFallbackScript, StringComparison.OrdinalIgnoreCase)) {
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
var candidates = MapScriptToTypefaces[match.Script];
|
||||||
|
foreach (var typeface in EnumerateTypefaces(candidates, distinctFamily)) {
|
||||||
|
yield return typeface;
|
||||||
|
}
|
||||||
|
supported.Remove(match);
|
||||||
|
}
|
||||||
|
if (flag) yield break;
|
||||||
|
Logger.Log("main", 0, "UI", "Matching fallback fonts");
|
||||||
|
foreach (var typeface in EnumerateTypefaces(MapScriptToTypefaces[UltimateFallbackScript], distinctFamily)) {
|
||||||
|
yield return typeface;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void LoadDefaultAndroidFallbackList() {
|
IEnumerable<Typeface> EnumerateTypefaces(List<string> candidates, bool distinctFamily) {
|
||||||
if (Environment.OSVersion.Platform != PlatformID.Unix) return;
|
foreach (var candidate in candidates) {
|
||||||
MapScriptToTypefaces.Clear();
|
if (Manager.MapFullNameToTypeface.TryGetValue(candidate, out var typeface1)) {
|
||||||
ScriptUtils.FillKeysWithScripts(MapScriptToTypefaces, () => new List<string>());
|
yield return typeface1;
|
||||||
MapScriptToTypefaces["zyyy"].Insert(0, "Noto Sans CJK TC"); // TODO Modify default fallback
|
}
|
||||||
MapScriptToTypefaces["zyyy"].Insert(0, "Noto Sans CJK JP");
|
if (distinctFamily) continue;
|
||||||
MapScriptToTypefaces["zyyy"].Insert(0, "Noto Sans CJK SC");
|
IReadOnlyCollection<Typeface> typefaces2;
|
||||||
MapScriptToTypefaces["zyyy"].Insert(0, "Roboto");
|
if (Manager.MapNameToTypefaces.TryGetValue(candidate, out typefaces2)) {
|
||||||
MapScriptToTypefaces["zsye"].Insert(0, "Noto Color Emoji");
|
foreach (var typeface in typefaces2) {
|
||||||
MapScriptToTypefaces["zsye"].Add("Noto Color Emoji Flags");
|
if (typeface1 == typeface) continue;
|
||||||
MapScriptToTypefaces["arab"].Insert(0, "Noto Naskh Arabic");
|
yield return typeface;
|
||||||
MapScriptToTypefaces["adlm"].Insert(0, "Noto Sans Adlam");
|
|
||||||
MapScriptToTypefaces["ahom"].Insert(0, "Noto Sans Ahom");
|
|
||||||
MapScriptToTypefaces["hluw"].Insert(0, "Noto Sans Anatolian Hieroglyphs");
|
|
||||||
MapScriptToTypefaces["armn"].Insert(0, "Noto Sans Armenian");
|
|
||||||
MapScriptToTypefaces["avst"].Insert(0, "Noto Sans Avestan");
|
|
||||||
MapScriptToTypefaces["bali"].Insert(0, "Noto Sans Balinese");
|
|
||||||
MapScriptToTypefaces["bamu"].Insert(0, "Noto Sans Bamum");
|
|
||||||
MapScriptToTypefaces["bass"].Insert(0, "Noto Sans Bassa Vah");
|
|
||||||
MapScriptToTypefaces["batk"].Insert(0, "Noto Sans Batak");
|
|
||||||
MapScriptToTypefaces["beng"].Insert(0, "Noto Sans Bengali");
|
|
||||||
MapScriptToTypefaces["bhks"].Insert(0, "Noto Sans Bhaiksuki");
|
|
||||||
MapScriptToTypefaces["brah"].Insert(0, "Noto Sans Brahmi");
|
|
||||||
MapScriptToTypefaces["bugi"].Insert(0, "Noto Sans Buginese");
|
|
||||||
MapScriptToTypefaces["buhd"].Insert(0, "Noto Sans Buhid");
|
|
||||||
MapScriptToTypefaces["jpan"].Insert(0, "Noto Sans CJK JP");
|
|
||||||
MapScriptToTypefaces["kore"].Insert(0, "Noto Sans CJK KR");
|
|
||||||
MapScriptToTypefaces["hans"].Insert(0, "Noto Sans CJK SC");
|
|
||||||
MapScriptToTypefaces["hant"].Insert(0, "Noto Sans CJK TC");
|
|
||||||
MapScriptToTypefaces["hant"].Add("Noto Sans CJK HK");
|
|
||||||
MapScriptToTypefaces["cans"].Insert(0, "Noto Sans Canadian Aboriginal");
|
|
||||||
MapScriptToTypefaces["cari"].Insert(0, "Noto Sans Carian");
|
|
||||||
MapScriptToTypefaces["cakm"].Insert(0, "Noto Sans Chakma");
|
|
||||||
MapScriptToTypefaces["cham"].Insert(0, "Noto Sans Cham");
|
|
||||||
MapScriptToTypefaces["cher"].Insert(0, "Noto Sans Cherokee");
|
|
||||||
MapScriptToTypefaces["copt"].Insert(0, "Noto Sans Coptic");
|
|
||||||
MapScriptToTypefaces["xsux"].Insert(0, "Noto Sans Cuneiform");
|
|
||||||
MapScriptToTypefaces["cprt"].Insert(0, "Noto Sans Cypriot");
|
|
||||||
MapScriptToTypefaces["dsrt"].Insert(0, "Noto Sans Deseret");
|
|
||||||
MapScriptToTypefaces["deva"].Insert(0, "Noto Sans Devanagari");
|
|
||||||
MapScriptToTypefaces["egyp"].Insert(0, "Noto Sans Egyptian Hieroglyphs");
|
|
||||||
MapScriptToTypefaces["elba"].Insert(0, "Noto Sans Elbasan");
|
|
||||||
MapScriptToTypefaces["ethi"].Insert(0, "Noto Sans Ethiopic");
|
|
||||||
MapScriptToTypefaces["geor"].Insert(0, "Noto Sans Georgian");
|
|
||||||
MapScriptToTypefaces["glag"].Insert(0, "Noto Sans Glagolitic");
|
|
||||||
MapScriptToTypefaces["goth"].Insert(0, "Noto Sans Gothic");
|
|
||||||
MapScriptToTypefaces["gran"].Insert(0, "Noto Sans Grantha");
|
|
||||||
MapScriptToTypefaces["gujr"].Insert(0, "Noto Sans Gujarati");
|
|
||||||
MapScriptToTypefaces["gong"].Insert(0, "Noto Sans Gunjala Gondi");
|
|
||||||
MapScriptToTypefaces["guru"].Insert(0, "Noto Sans Gurmukhi");
|
|
||||||
MapScriptToTypefaces["rohg"].Insert(0, "Noto Sans Hanifi Rohingya");
|
|
||||||
MapScriptToTypefaces["hano"].Insert(0, "Noto Sans Hanunoo");
|
|
||||||
MapScriptToTypefaces["hatr"].Insert(0, "Noto Sans Hatran");
|
|
||||||
MapScriptToTypefaces["hebr"].Insert(0, "Noto Sans Hebrew");
|
|
||||||
MapScriptToTypefaces["armi"].Insert(0, "Noto Sans Imperial Aramaic");
|
|
||||||
MapScriptToTypefaces["phli"].Insert(0, "Noto Sans Inscriptional Pahlavi");
|
|
||||||
MapScriptToTypefaces["prti"].Insert(0, "Noto Sans Inscriptional Parthian");
|
|
||||||
MapScriptToTypefaces["java"].Insert(0, "Noto Sans Javanese");
|
|
||||||
MapScriptToTypefaces["kthi"].Insert(0, "Noto Sans Kaithi");
|
|
||||||
MapScriptToTypefaces["knda"].Insert(0, "Noto Sans Kannada");
|
|
||||||
MapScriptToTypefaces["kali"].Insert(0, "Noto Sans KayahLi");
|
|
||||||
MapScriptToTypefaces["khar"].Insert(0, "Noto Sans Kharoshthi");
|
|
||||||
MapScriptToTypefaces["khmr"].Insert(0, "Noto Sans Khmer");
|
|
||||||
MapScriptToTypefaces["khoj"].Insert(0, "Noto Sans Khojki");
|
|
||||||
MapScriptToTypefaces["laoo"].Insert(0, "Noto Sans Lao");
|
|
||||||
MapScriptToTypefaces["lepc"].Insert(0, "Noto Sans Lepcha");
|
|
||||||
MapScriptToTypefaces["limb"].Insert(0, "Noto Sans Limbu");
|
|
||||||
MapScriptToTypefaces["lina"].Insert(0, "Noto Sans Linear A");
|
|
||||||
MapScriptToTypefaces["linb"].Insert(0, "Noto Sans Linear B");
|
|
||||||
MapScriptToTypefaces["lisu"].Insert(0, "Noto Sans Lisu");
|
|
||||||
MapScriptToTypefaces["lyci"].Insert(0, "Noto Sans Lycian");
|
|
||||||
MapScriptToTypefaces["lydi"].Insert(0, "Noto Sans Lydian");
|
|
||||||
MapScriptToTypefaces["mlym"].Insert(0, "Noto Sans Malayalam");
|
|
||||||
MapScriptToTypefaces["mand"].Insert(0, "Noto Sans Mandiac");
|
|
||||||
MapScriptToTypefaces["mani"].Insert(0, "Noto Sans Manichaean");
|
|
||||||
MapScriptToTypefaces["marc"].Insert(0, "Noto Sans Marchen");
|
|
||||||
MapScriptToTypefaces["gonm"].Insert(0, "Noto Sans Masaram Gondi");
|
|
||||||
MapScriptToTypefaces["medf"].Insert(0, "Noto Sans Medefaidrin");
|
|
||||||
MapScriptToTypefaces["mtei"].Insert(0, "Noto Sans Meetei Mayek");
|
|
||||||
MapScriptToTypefaces["merc"].Insert(0, "Noto Sans Meroitic");
|
|
||||||
MapScriptToTypefaces["mero"].Insert(0, "Noto Sans Meroitic");
|
|
||||||
MapScriptToTypefaces["plrd"].Insert(0, "Noto Sans Miao");
|
|
||||||
MapScriptToTypefaces["modi"].Insert(0, "Noto Sans Modi");
|
|
||||||
MapScriptToTypefaces["mong"].Insert(0, "Noto Sans Mongolian");
|
|
||||||
MapScriptToTypefaces["mroo"].Insert(0, "Noto Sans Mro");
|
|
||||||
MapScriptToTypefaces["mult"].Insert(0, "Noto Sans Multani");
|
|
||||||
MapScriptToTypefaces["mymr"].Insert(0, "Noto Sans Myanmar");
|
|
||||||
MapScriptToTypefaces["nkoo"].Insert(0, "Noto Sans Nko");
|
|
||||||
MapScriptToTypefaces["nbat"].Insert(0, "Noto Sans Nabataean");
|
|
||||||
MapScriptToTypefaces["talu"].Insert(0, "Noto Sans New Tai Lue");
|
|
||||||
MapScriptToTypefaces["newa"].Insert(0, "Noto Sans Newa");
|
|
||||||
MapScriptToTypefaces["ogam"].Insert(0, "Noto Sans Ogham");
|
|
||||||
MapScriptToTypefaces["olck"].Insert(0, "Noto Sans Ol Chiki");
|
|
||||||
MapScriptToTypefaces["ital"].Insert(0, "Noto Sans Old Italian");
|
|
||||||
MapScriptToTypefaces["narb"].Insert(0, "Noto Sans Old North Arabian");
|
|
||||||
MapScriptToTypefaces["perm"].Insert(0, "Noto Sans Old Permic");
|
|
||||||
MapScriptToTypefaces["xpeo"].Insert(0, "Noto Sans Old Persian");
|
|
||||||
MapScriptToTypefaces["sarb"].Insert(0, "Noto Sans Old South Arabian");
|
|
||||||
MapScriptToTypefaces["orkh"].Insert(0, "Noto Sans Old Turkic");
|
|
||||||
MapScriptToTypefaces["orya"].Insert(0, "Noto Sans Oriya");
|
|
||||||
MapScriptToTypefaces["osge"].Insert(0, "Noto Sans Osage");
|
|
||||||
MapScriptToTypefaces["osma"].Insert(0, "Noto Sans Osmanya");
|
|
||||||
MapScriptToTypefaces["hmng"].Insert(0, "Noto Sans Pahawh Hmong");
|
|
||||||
MapScriptToTypefaces["palm"].Insert(0, "Noto Sans Palmyrene");
|
|
||||||
MapScriptToTypefaces["pauc"].Insert(0, "Noto Sans Pau Cin Hau");
|
|
||||||
MapScriptToTypefaces["phag"].Insert(0, "Noto Sans Phags Pa");
|
|
||||||
MapScriptToTypefaces["phnx"].Insert(0, "Noto Sans Phoenician");
|
|
||||||
MapScriptToTypefaces["rjng"].Insert(0, "Noto Sans Rejang");
|
|
||||||
MapScriptToTypefaces["runr"].Insert(0, "Noto Sans Runic");
|
|
||||||
MapScriptToTypefaces["samr"].Insert(0, "Noto Sans Samaritan");
|
|
||||||
MapScriptToTypefaces["saur"].Insert(0, "Noto Sans Saurashtra");
|
|
||||||
MapScriptToTypefaces["shrd"].Insert(0, "Noto Sans Sharada");
|
|
||||||
MapScriptToTypefaces["shaw"].Insert(0, "Noto Sans Shavian");
|
|
||||||
MapScriptToTypefaces["sinh"].Insert(0, "Noto Sans Sinhala");
|
|
||||||
MapScriptToTypefaces["sora"].Insert(0, "Noto Sans Sora Sompeng");
|
|
||||||
MapScriptToTypefaces["soyo"].Insert(0, "Noto Sans Soyombo");
|
|
||||||
MapScriptToTypefaces["sund"].Insert(0, "Noto Sans Sundanese");
|
|
||||||
MapScriptToTypefaces["sylo"].Insert(0, "Noto Sans Syloti Nagri");
|
|
||||||
MapScriptToTypefaces["zsym"].Insert(0, "Noto Sans Symbols");
|
|
||||||
MapScriptToTypefaces["syrn"].Insert(0, "Noto Sans Syriac Eastern");
|
|
||||||
MapScriptToTypefaces["syre"].Insert(0, "Noto Sans Syriac Estrangela");
|
|
||||||
MapScriptToTypefaces["syrj"].Insert(0, "Noto Sans Syriac Western");
|
|
||||||
MapScriptToTypefaces["tglg"].Insert(0, "Noto Sans Tagalog");
|
|
||||||
MapScriptToTypefaces["tagb"].Insert(0, "Noto Sans Tagbanwa");
|
|
||||||
MapScriptToTypefaces["tale"].Insert(0, "Noto Sans Tai Le");
|
|
||||||
MapScriptToTypefaces["lana"].Insert(0, "Noto Sans Tai Tham");
|
|
||||||
MapScriptToTypefaces["tavt"].Insert(0, "Noto Sans Tai Viet");
|
|
||||||
MapScriptToTypefaces["takr"].Insert(0, "Noto Sans Takri");
|
|
||||||
MapScriptToTypefaces["taml"].Insert(0, "Noto Sans Tamil");
|
|
||||||
MapScriptToTypefaces["telu"].Insert(0, "Noto Sans Telugu");
|
|
||||||
MapScriptToTypefaces["thaa"].Insert(0, "Noto Sans Thaana");
|
|
||||||
MapScriptToTypefaces["thai"].Insert(0, "Noto Sans Thai");
|
|
||||||
MapScriptToTypefaces["tfng"].Insert(0, "Noto Sans Tifinagh");
|
|
||||||
MapScriptToTypefaces["ugar"].Insert(0, "Noto Sans Ugaritic");
|
|
||||||
MapScriptToTypefaces["vaii"].Insert(0, "Noto Sans Vai");
|
|
||||||
MapScriptToTypefaces["wcho"].Insert(0, "Noto Sans Wancho");
|
|
||||||
MapScriptToTypefaces["wara"].Insert(0, "Noto Sans Warang Citi");
|
|
||||||
MapScriptToTypefaces["yiii"].Insert(0, "Noto Sans Yi");
|
|
||||||
}
|
|
||||||
public FallbackListFontMatcher(FontManager manager) : base(manager) { }
|
|
||||||
public override IEnumerable<Typeface> MatchScript(string script = null, bool distinctFamily = false) {
|
|
||||||
if (string.IsNullOrEmpty(script)) script = ScriptUtils.UltimateFallbackScript;
|
|
||||||
List<string> candidates;
|
|
||||||
IEnumerable<string> candidateScripts = new string[] { script };
|
|
||||||
while (candidateScripts != null) {
|
|
||||||
foreach (var candidateScript in candidateScripts) {
|
|
||||||
if (MapScriptToTypefaces.TryGetValue(candidateScript, out candidates)) {
|
|
||||||
foreach (var candidate in candidates) {
|
|
||||||
IReadOnlyCollection<Typeface> typefaces1;
|
|
||||||
if (Manager.MapFullNameToTypeface.TryGetValue(candidate, out typefaces1)) {
|
|
||||||
foreach (var typeface in typefaces1) {
|
|
||||||
yield return typeface;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (distinctFamily) continue;
|
|
||||||
IReadOnlyCollection<Typeface> typefaces2;
|
|
||||||
if (Manager.MapNameToTypefaces.TryGetValue(candidate, out typefaces2)) {
|
|
||||||
foreach (var typeface in typefaces2) {
|
|
||||||
if (typefaces1.Contains(typeface)) continue;
|
|
||||||
yield return typeface;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
candidateScripts = ScriptUtils.EnumerateFallbackScripts(script);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
|
#pragma warning disable IDE0049
|
||||||
namespace Cryville.Common.Font {
|
namespace Cryville.Common.Font {
|
||||||
public abstract class FontTable<T> {
|
public abstract class FontTable<T> {
|
||||||
protected UInt32 Offset { get; private set; }
|
protected UInt32 Offset { get; private set; }
|
||||||
@@ -25,14 +26,17 @@ namespace Cryville.Common.Font {
|
|||||||
readonly UInt16 minorVersion;
|
readonly UInt16 minorVersion;
|
||||||
readonly UInt32 numFonts;
|
readonly UInt32 numFonts;
|
||||||
readonly List<UInt32> tableDirectoryOffsets = new List<UInt32>();
|
readonly List<UInt32> tableDirectoryOffsets = new List<UInt32>();
|
||||||
|
#pragma warning disable IDE0052 // Reserved
|
||||||
readonly String dsigTag;
|
readonly String dsigTag;
|
||||||
readonly UInt32 dsigLength;
|
readonly UInt32 dsigLength;
|
||||||
readonly UInt32 dsigOffset;
|
readonly UInt32 dsigOffset;
|
||||||
|
#pragma warning restore IDE0052 // Reserved
|
||||||
public TTCHeader(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
public TTCHeader(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
||||||
ttcTag = reader.ReadTag();
|
ttcTag = reader.ReadTag();
|
||||||
if (ttcTag != "ttcf") throw new NotImplementedException();
|
if (ttcTag != "ttcf") throw new NotSupportedException();
|
||||||
majorVersion = reader.ReadUInt16();
|
majorVersion = reader.ReadUInt16();
|
||||||
minorVersion = reader.ReadUInt16();
|
minorVersion = reader.ReadUInt16();
|
||||||
|
if (minorVersion != 0) throw new NotSupportedException();
|
||||||
numFonts = reader.ReadUInt32();
|
numFonts = reader.ReadUInt32();
|
||||||
for (UInt32 i = 0; i < numFonts; i++) tableDirectoryOffsets.Add(reader.ReadUInt32());
|
for (UInt32 i = 0; i < numFonts; i++) tableDirectoryOffsets.Add(reader.ReadUInt32());
|
||||||
if (majorVersion == 2) {
|
if (majorVersion == 2) {
|
||||||
@@ -52,12 +56,16 @@ namespace Cryville.Common.Font {
|
|||||||
public sealed class TableDirectory : FontTable<TableRecord, object> {
|
public sealed class TableDirectory : FontTable<TableRecord, object> {
|
||||||
readonly UInt32 sfntVersion;
|
readonly UInt32 sfntVersion;
|
||||||
readonly UInt16 numTables;
|
readonly UInt16 numTables;
|
||||||
|
#pragma warning disable IDE0052 // Reserved
|
||||||
readonly UInt16 searchRange;
|
readonly UInt16 searchRange;
|
||||||
readonly UInt16 entrySelector;
|
readonly UInt16 entrySelector;
|
||||||
readonly UInt16 rangeShift;
|
readonly UInt16 rangeShift;
|
||||||
|
#pragma warning restore IDE0052 // Reserved
|
||||||
readonly List<TableRecord> tableRecords = new List<TableRecord>();
|
readonly List<TableRecord> tableRecords = new List<TableRecord>();
|
||||||
public TableDirectory(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
public TableDirectory(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
||||||
sfntVersion = reader.ReadUInt32();
|
sfntVersion = reader.ReadUInt32();
|
||||||
|
if (sfntVersion != 0x00010000 && sfntVersion != 0x4F54544F &&
|
||||||
|
sfntVersion != 0x74727565 && sfntVersion != 0x74797031) throw new NotSupportedException();
|
||||||
numTables = reader.ReadUInt16();
|
numTables = reader.ReadUInt16();
|
||||||
searchRange = reader.ReadUInt16();
|
searchRange = reader.ReadUInt16();
|
||||||
entrySelector = reader.ReadUInt16();
|
entrySelector = reader.ReadUInt16();
|
||||||
@@ -99,48 +107,63 @@ namespace Cryville.Common.Font {
|
|||||||
count = reader.ReadUInt16();
|
count = reader.ReadUInt16();
|
||||||
storageOffset = reader.ReadUInt16();
|
storageOffset = reader.ReadUInt16();
|
||||||
for (UInt16 i = 0; i < count; i++)
|
for (UInt16 i = 0; i < count; i++)
|
||||||
nameRecord.Add(new NameRecord {
|
nameRecord.Add(new NameRecord(
|
||||||
platformID = reader.ReadUInt16(),
|
reader.ReadUInt16(),
|
||||||
encodingID = reader.ReadUInt16(),
|
reader.ReadUInt16(),
|
||||||
languageID = reader.ReadUInt16(),
|
reader.ReadUInt16(),
|
||||||
nameID = (NameID)reader.ReadUInt16(),
|
(NameID)reader.ReadUInt16(),
|
||||||
length = reader.ReadUInt16(),
|
reader.ReadUInt16(),
|
||||||
stringOffset = reader.ReadUInt16(),
|
reader.ReadUInt16()
|
||||||
});
|
));
|
||||||
if (version == 1) {
|
if (version == 1) {
|
||||||
langTagCount = reader.ReadUInt16();
|
langTagCount = reader.ReadUInt16();
|
||||||
for (UInt16 i = 0; i < langTagCount; i++)
|
for (UInt16 i = 0; i < langTagCount; i++)
|
||||||
langTagRecord.Add(new LangTagRecord {
|
langTagRecord.Add(new LangTagRecord(
|
||||||
length = reader.ReadUInt16(),
|
reader.ReadUInt16(),
|
||||||
langTagOffset = reader.ReadUInt16(),
|
reader.ReadUInt16()
|
||||||
});
|
));
|
||||||
|
}
|
||||||
|
foreach (var i in nameRecord)
|
||||||
|
i.Load(reader, offset + storageOffset);
|
||||||
|
if (version == 1) {
|
||||||
|
foreach (var i in langTagRecord)
|
||||||
|
i.Load(reader, offset + storageOffset);
|
||||||
}
|
}
|
||||||
UInt32 origin = (UInt32)reader.BaseStream.Position;
|
|
||||||
for (int i = 0; i < nameRecord.Count; i++) nameRecord[i] = nameRecord[i].Load(reader, origin);
|
|
||||||
for (int i = 0; i < langTagRecord.Count; i++) langTagRecord[i] = langTagRecord[i].Load(reader, origin);
|
|
||||||
}
|
}
|
||||||
public sealed override IReadOnlyList<NameRecord> GetItems() {
|
public sealed override IReadOnlyList<NameRecord> GetItems() {
|
||||||
return nameRecord;
|
return nameRecord;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public struct NameRecord {
|
public class NameRecord {
|
||||||
public UInt16 platformID;
|
public UInt16 PlatformID { get; private set; }
|
||||||
public UInt16 encodingID;
|
public UInt16 EncodingID { get; private set; }
|
||||||
public UInt16 languageID;
|
public UInt16 LanguageID { get; private set; }
|
||||||
public NameID nameID;
|
public NameID NameID { get; private set; }
|
||||||
public UInt16 length;
|
public UInt16 Length { get; private set; }
|
||||||
public UInt16 stringOffset;
|
public UInt16 StringOffset { get; private set; }
|
||||||
public String value { get; private set; }
|
public String Value { get; private set; }
|
||||||
public NameRecord Load(BinaryReader reader, UInt32 origin) {
|
public NameRecord(UInt16 platformID, UInt16 encodingID, UInt16 languageID, NameID nameID, UInt16 length, UInt16 stringOffset) {
|
||||||
reader.BaseStream.Position = origin + stringOffset;
|
PlatformID = platformID;
|
||||||
|
EncodingID = encodingID;
|
||||||
|
LanguageID = languageID;
|
||||||
|
NameID = nameID;
|
||||||
|
Length = length;
|
||||||
|
StringOffset = stringOffset;
|
||||||
|
}
|
||||||
|
public void Load(BinaryReader reader, UInt32 origin) {
|
||||||
|
reader.BaseStream.Position = origin + StringOffset;
|
||||||
Encoding encoding;
|
Encoding encoding;
|
||||||
switch (platformID) {
|
try {
|
||||||
case 0: encoding = Encoding.BigEndianUnicode; break;
|
switch (PlatformID) {
|
||||||
case 3: encoding = Encoding.BigEndianUnicode; break;
|
case 0: encoding = Encoding.BigEndianUnicode; break;
|
||||||
default: return this;
|
case 1: encoding = Encoding.GetEncoding(10000 + EncodingID); break;
|
||||||
|
case 3: encoding = Encoding.BigEndianUnicode; break;
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
value = encoding.GetString(reader.ReadBytes(length));
|
catch (NotSupportedException) { return; }
|
||||||
return this;
|
catch (ArgumentException) { return; }
|
||||||
|
Value = encoding.GetString(reader.ReadBytes(Length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public enum NameID : UInt16 {
|
public enum NameID : UInt16 {
|
||||||
@@ -171,47 +194,58 @@ namespace Cryville.Common.Font {
|
|||||||
DarkBackgroundPalette = 24,
|
DarkBackgroundPalette = 24,
|
||||||
VariationsPostScriptNamePrefix = 25,
|
VariationsPostScriptNamePrefix = 25,
|
||||||
}
|
}
|
||||||
public struct LangTagRecord {
|
public class LangTagRecord {
|
||||||
public UInt16 length;
|
public UInt16 Length { get; private set; }
|
||||||
public UInt16 langTagOffset;
|
public UInt16 LangTagOffset { get; private set; }
|
||||||
public String value { get; private set; }
|
public String Value { get; private set; }
|
||||||
public LangTagRecord Load(BinaryReader reader, UInt32 origin) {
|
public LangTagRecord(UInt16 length, UInt16 langTagOffset) {
|
||||||
reader.BaseStream.Position = origin + langTagOffset;
|
Length = length;
|
||||||
value = Encoding.BigEndianUnicode.GetString(reader.ReadBytes(length));
|
LangTagOffset = langTagOffset;
|
||||||
return this;
|
}
|
||||||
|
public void Load(BinaryReader reader, UInt32 origin) {
|
||||||
|
reader.BaseStream.Position = origin + LangTagOffset;
|
||||||
|
Value = Encoding.BigEndianUnicode.GetString(reader.ReadBytes(Length));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public sealed class MetaTable : FontTable<DataMap> {
|
public sealed class MetaTable : FontTable<DataMap> {
|
||||||
readonly UInt32 version;
|
readonly UInt32 version;
|
||||||
|
#pragma warning disable IDE0052 // Reserved
|
||||||
readonly UInt32 flags;
|
readonly UInt32 flags;
|
||||||
|
#pragma warning restore IDE0052 // Reserved
|
||||||
readonly UInt32 dataMapCount;
|
readonly UInt32 dataMapCount;
|
||||||
readonly List<DataMap> dataMaps = new List<DataMap>();
|
readonly List<DataMap> dataMaps = new List<DataMap>();
|
||||||
public MetaTable(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
public MetaTable(BinaryReader reader, UInt32 offset) : base(reader, offset) {
|
||||||
version = reader.ReadUInt32();
|
version = reader.ReadUInt32();
|
||||||
|
if (version != 1) throw new NotSupportedException();
|
||||||
flags = reader.ReadUInt32();
|
flags = reader.ReadUInt32();
|
||||||
reader.ReadUInt32();
|
reader.ReadUInt32();
|
||||||
dataMapCount = reader.ReadUInt32();
|
dataMapCount = reader.ReadUInt32();
|
||||||
for (UInt32 i = 0; i < dataMapCount; i++)
|
for (UInt32 i = 0; i < dataMapCount; i++)
|
||||||
dataMaps.Add(new DataMap {
|
dataMaps.Add(new DataMap (
|
||||||
tag = reader.ReadTag(),
|
reader.ReadTag(),
|
||||||
dataOffset = reader.ReadUInt32(),
|
reader.ReadUInt32(),
|
||||||
dataLength = reader.ReadUInt32(),
|
reader.ReadUInt32()
|
||||||
});
|
));
|
||||||
for (int i = 0; i < dataMaps.Count; i++) dataMaps[i] = dataMaps[i].Load(reader, offset);
|
foreach (var i in dataMaps)
|
||||||
|
i.Load(reader, offset);
|
||||||
}
|
}
|
||||||
public sealed override IReadOnlyList<DataMap> GetItems() {
|
public sealed override IReadOnlyList<DataMap> GetItems() {
|
||||||
return dataMaps;
|
return dataMaps;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public struct DataMap {
|
public class DataMap {
|
||||||
public String tag;
|
public String Tag { get; private set; }
|
||||||
public UInt32 dataOffset;
|
public UInt32 DataOffset { get; private set; }
|
||||||
public UInt32 dataLength;
|
public UInt32 DataLength { get; private set; }
|
||||||
public String value { get; private set; }
|
public String Value { get; private set; }
|
||||||
public DataMap Load(BinaryReader reader, UInt32 origin) {
|
public DataMap(String tag, UInt32 dataOffset, UInt32 dataLength) {
|
||||||
reader.BaseStream.Position = origin + dataOffset;
|
Tag = tag;
|
||||||
value = Encoding.ASCII.GetString(reader.ReadBytes((int)dataLength));
|
DataOffset = dataOffset;
|
||||||
return this;
|
DataLength = dataLength;
|
||||||
|
}
|
||||||
|
public void Load(BinaryReader reader, UInt32 origin) {
|
||||||
|
reader.BaseStream.Position = origin + DataOffset;
|
||||||
|
Value = Encoding.ASCII.GetString(reader.ReadBytes((int)DataLength));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static class BinaryReaderExtensions {
|
public static class BinaryReaderExtensions {
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ namespace Cryville.Common.Font {
|
|||||||
protected override void GetName(BinaryReader reader) {
|
protected override void GetName(BinaryReader reader) {
|
||||||
var dir = new TableDirectory(reader, (uint)reader.BaseStream.Position);
|
var dir = new TableDirectory(reader, (uint)reader.BaseStream.Position);
|
||||||
var nameTable = (NameTable)dir.GetSubTable((from i in dir.GetItems() where i.tableTag == "name" select i).Single());
|
var nameTable = (NameTable)dir.GetSubTable((from i in dir.GetItems() where i.tableTag == "name" select i).Single());
|
||||||
FamilyName = (from i in nameTable.GetItems() where i.nameID == NameID.FontFamilyName && i.value != null select i.value).First();
|
FamilyName = (from i in nameTable.GetItems() where i.NameID == NameID.FontFamilyName && i.Value != null select i.Value).First();
|
||||||
SubfamilyName = (from i in nameTable.GetItems() where i.nameID == NameID.FontSubfamilyName && i.value != null select i.value).First();
|
SubfamilyName = (from i in nameTable.GetItems() where i.NameID == NameID.FontSubfamilyName && i.Value != null select i.Value).First();
|
||||||
FullName = (from i in nameTable.GetItems() where i.nameID == NameID.FullFontName && i.value != null select i.value).First();
|
FullName = (from i in nameTable.GetItems() where i.NameID == NameID.FullFontName && i.Value != null select i.Value).First();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ namespace Cryville.Common {
|
|||||||
public struct Identifier : IEquatable<Identifier> {
|
public struct Identifier : IEquatable<Identifier> {
|
||||||
public static Identifier Empty = new Identifier(0);
|
public static Identifier Empty = new Identifier(0);
|
||||||
public int Key { get; private set; }
|
public int Key { get; private set; }
|
||||||
public object Name { get { return IdentifierManager.SharedInstance.Retrieve(Key); } }
|
public object Name { get { return IdentifierManager.Shared.Retrieve(Key); } }
|
||||||
public Identifier(int key) {
|
public Identifier(int key) {
|
||||||
Key = key;
|
Key = key;
|
||||||
}
|
}
|
||||||
public Identifier(object name) {
|
public Identifier(object name) {
|
||||||
Key = IdentifierManager.SharedInstance.Request(name);
|
Key = IdentifierManager.Shared.Request(name);
|
||||||
}
|
}
|
||||||
public override bool Equals(object obj) {
|
public override bool Equals(object obj) {
|
||||||
if (obj == null || !(obj is Identifier)) return false;
|
if (obj == null || !(obj is Identifier)) return false;
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Cryville.Common {
|
|
||||||
/// <summary>
|
|
||||||
/// A manager that assigns each given identifiers a unique integer ID.
|
|
||||||
/// </summary>
|
|
||||||
public class IdentifierManager {
|
|
||||||
/// <summary>
|
|
||||||
/// A shared instance of the <see cref="IdentifierManager" /> class.
|
|
||||||
/// </summary>
|
|
||||||
public static IdentifierManager SharedInstance = new IdentifierManager();
|
|
||||||
|
|
||||||
readonly Dictionary<object, int> _idents = new Dictionary<object, int>();
|
|
||||||
readonly List<object> _ids = new List<object>();
|
|
||||||
|
|
||||||
readonly object _syncRoot = new object();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of the <see cref="IdentifierManager" /> class.
|
|
||||||
/// </summary>
|
|
||||||
public IdentifierManager() {
|
|
||||||
Request(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Requests an integer ID for an identifier.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ident">The identifier.</param>
|
|
||||||
/// <returns>The integer ID.</returns>
|
|
||||||
public int Request(object ident) {
|
|
||||||
lock (_syncRoot) {
|
|
||||||
int id;
|
|
||||||
if (!_idents.TryGetValue(ident, out id)) {
|
|
||||||
_idents.Add(ident, id = _idents.Count);
|
|
||||||
_ids.Add(ident);
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the identifier assigned with an integer ID.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The integer ID.</param>
|
|
||||||
/// <returns>The identifier.</returns>
|
|
||||||
public object Retrieve(int id) {
|
|
||||||
lock (_syncRoot) {
|
|
||||||
return _ids[id];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 478086496f56eaf46be4df4e2ad37fee
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace Cryville.Common {
|
|
||||||
/// <summary>
|
|
||||||
/// A logger.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class Logger {
|
|
||||||
static readonly Dictionary<string, Logger> Instances = new Dictionary<string, Logger>();
|
|
||||||
static readonly Dictionary<string, StreamWriter> Files = new Dictionary<string, StreamWriter>();
|
|
||||||
static string logPath = null;
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the path where the log files shall be stored.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
public static void SetLogPath(string path) {
|
|
||||||
logPath = path;
|
|
||||||
var dir = new DirectoryInfo(path);
|
|
||||||
if (!dir.Exists) dir.Create();
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Logs to the specified logger.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The key of the logger.</param>
|
|
||||||
/// <param name="level">The severity level.</param>
|
|
||||||
/// <param name="module">The module that is logging.</param>
|
|
||||||
/// <param name="format">The format string.</param>
|
|
||||||
/// <param name="args">The arguments for formatting.</param>
|
|
||||||
public static void Log(string key, int level, string module, string format, params object[] args) {
|
|
||||||
if (!Instances.ContainsKey(key)) return;
|
|
||||||
Instances[key].Log(level, module, string.Format(format, args));
|
|
||||||
if (Files.ContainsKey(key)) Files[key].WriteLine("[{0:O}] [{1}] <{2}> {3}", DateTime.UtcNow, level, module, string.Format(format, args));
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a created logger to the shared logger manager.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The key of the logger.</param>
|
|
||||||
/// <param name="logger">The logger.</param>
|
|
||||||
public static void Create(string key, Logger logger) {
|
|
||||||
Instances[key] = logger;
|
|
||||||
if (logPath != null) {
|
|
||||||
Files[key] = new StreamWriter(logPath + "/" + ((int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds).ToString(CultureInfo.InvariantCulture) + "-" + key + ".log") {
|
|
||||||
AutoFlush = true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Closes all loggers and related file streams.
|
|
||||||
/// </summary>
|
|
||||||
public static void Close() {
|
|
||||||
Instances.Clear();
|
|
||||||
foreach (var f in Files) f.Value.Dispose();
|
|
||||||
Files.Clear();
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Logs to the logger.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="level">The severity level.</param>
|
|
||||||
/// <param name="module">The module that is logging.</param>
|
|
||||||
/// <param name="msg">The message.</param>
|
|
||||||
public virtual void Log(int level, string module, string msg) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A <see cref="Logger" /> that calls a callback function on log.
|
|
||||||
/// </summary>
|
|
||||||
public class InstantLogger : Logger {
|
|
||||||
readonly Action<int, string, string> callback;
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of the <see cref="InstantLogger" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="callback">The callback function.</param>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="callback" /> is <see langword="null" />.</exception>
|
|
||||||
public InstantLogger(Action<int, string, string> callback) {
|
|
||||||
if (callback == null)
|
|
||||||
throw new ArgumentNullException("callback");
|
|
||||||
this.callback = callback;
|
|
||||||
}
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Log(int level, string module, string msg) {
|
|
||||||
base.Log(level, module, msg);
|
|
||||||
callback(level, module, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A <see cref="Logger" /> that buffers the logs for enumeration.
|
|
||||||
/// </summary>
|
|
||||||
public class BufferedLogger : Logger {
|
|
||||||
readonly List<LogEntry> buffer = new List<LogEntry>();
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of the <see cref="BufferedLogger" /> class.
|
|
||||||
/// </summary>
|
|
||||||
public BufferedLogger() { }
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void Log(int level, string module, string msg) {
|
|
||||||
base.Log(level, module, msg);
|
|
||||||
lock (buffer) {
|
|
||||||
buffer.Add(new LogEntry(level, module, msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Enumerates the buffered logs.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="callback">The callback function to receive the logs.</param>
|
|
||||||
public void Enumerate(Action<int, string, string> callback) {
|
|
||||||
lock (buffer) {
|
|
||||||
foreach (var i in buffer) {
|
|
||||||
callback(i.level, i.module, i.msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffer.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LogEntry {
|
|
||||||
public int level;
|
|
||||||
public string module;
|
|
||||||
public string msg;
|
|
||||||
public LogEntry(int level, string module, string msg) {
|
|
||||||
this.level = level;
|
|
||||||
this.module = module;
|
|
||||||
this.msg = msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 1c1729cfde78f1c479c9f7eb166e0107
|
|
||||||
timeCreated: 1611126212
|
|
||||||
licenseType: Free
|
|
||||||
MonoImporter:
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 53f4e3167a1eee2478b0abc6302aee8f
|
guid: 1a624371d4108614b9cdc4acca1499e2
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
|
using Cryville.Common.Logging;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Cryville.Common.Network {
|
namespace Cryville.Common.Network.Http11 {
|
||||||
public class HttpClient {
|
public class Http11Client : IDisposable {
|
||||||
private readonly string _directHost;
|
private readonly string _directHost;
|
||||||
protected string DirectHost { get { return _directHost; } }
|
protected string DirectHost { get { return _directHost; } }
|
||||||
|
|
||||||
@@ -18,29 +18,17 @@ namespace Cryville.Common.Network {
|
|||||||
readonly Uri _baseUri;
|
readonly Uri _baseUri;
|
||||||
readonly int origPort;
|
readonly int origPort;
|
||||||
|
|
||||||
protected string Version = "HTTP/1.1";
|
protected const string Version = "HTTP/1.1";
|
||||||
protected TcpClient TcpClient { get; private set; }
|
protected TcpClient TcpClient { get; private set; }
|
||||||
protected Stream RawTcpStream {
|
protected Stream RawTcpStream { get { return TcpClient.GetStream(); } }
|
||||||
get {
|
protected virtual Stream Stream { get { return TcpClient.GetStream(); } }
|
||||||
return TcpClient.GetStream();
|
protected virtual string WindowsProxyProtocolName { get { return "http"; } }
|
||||||
}
|
|
||||||
}
|
|
||||||
protected virtual Stream Stream {
|
|
||||||
get {
|
|
||||||
return TcpClient.GetStream();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected virtual string WindowsProxyProtocolName {
|
|
||||||
get {
|
|
||||||
return "http";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly bool _proxied = false;
|
private readonly bool _proxied = false;
|
||||||
|
|
||||||
public Dictionary<string, string> Headers { get; set; }
|
public Dictionary<string, string> Headers { get; set; }
|
||||||
|
|
||||||
public HttpClient(Uri baseUri, int port = 80) {
|
public Http11Client(Uri baseUri, int port = 80) {
|
||||||
_directHost = baseUri.Host;
|
_directHost = baseUri.Host;
|
||||||
_directPort = port;
|
_directPort = port;
|
||||||
_baseUri = baseUri;
|
_baseUri = baseUri;
|
||||||
@@ -60,18 +48,29 @@ namespace Cryville.Common.Network {
|
|||||||
public virtual void Close() {
|
public virtual void Close() {
|
||||||
TcpClient.Close();
|
TcpClient.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpResponse Request(string method, Uri uri, string body = null, Encoding encoding = null) {
|
public void Dispose() {
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Dispose(bool disposing) {
|
||||||
|
if (disposing) {
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Http11Response Request(string method, Uri uri, string body = null, Encoding encoding = null) {
|
||||||
string struri = GetUri(uri).PathAndQuery;
|
string struri = GetUri(uri).PathAndQuery;
|
||||||
return Request(Stream, method, struri, body, encoding);
|
return Request(Stream, method, struri, body, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpResponse Request(Stream stream, string method, string uri, string body = null, Encoding encoding = null) {
|
Http11Response Request(Stream stream, string method, string uri, string body = null, Encoding encoding = null) {
|
||||||
var headers = new Dictionary<string, string>();
|
var headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
foreach (var h in Headers)
|
foreach (var h in Headers)
|
||||||
headers.Add(h.Key, h.Value);
|
headers.Add(h.Key, h.Value);
|
||||||
headers["Host"] = _baseUri.Host;
|
headers["Host"] = _baseUri.Host;
|
||||||
byte[] payload = new byte[0];
|
byte[] payload = null;
|
||||||
if (body != null) {
|
if (body != null) {
|
||||||
if (encoding == null)
|
if (encoding == null)
|
||||||
encoding = Encoding.UTF8;
|
encoding = Encoding.UTF8;
|
||||||
@@ -79,22 +78,24 @@ namespace Cryville.Common.Network {
|
|||||||
headers.Add("Content-Encoding", encoding.EncodingName);
|
headers.Add("Content-Encoding", encoding.EncodingName);
|
||||||
headers.Add("Content-Length", payload.Length.ToString(CultureInfo.InvariantCulture));
|
headers.Add("Content-Length", payload.Length.ToString(CultureInfo.InvariantCulture));
|
||||||
}
|
}
|
||||||
string request_line = string.Format(
|
using (var writer = new StreamWriter(stream, Encoding.ASCII, 1024, true)) {
|
||||||
"{0} {1} {2}\r\n", method, uri, Version
|
writer.Write(method);
|
||||||
);
|
writer.Write(' ');
|
||||||
string header_fields = string.Concat((
|
writer.Write(uri);
|
||||||
from h in headers select h.Key + ":" + h.Value + "\r\n"
|
writer.Write(' ');
|
||||||
).ToArray());
|
writer.Write(Version);
|
||||||
byte[] buffer0 = Encoding.ASCII.GetBytes(string.Format(
|
writer.Write("\r\n");
|
||||||
"{0}{1}\r\n", request_line, header_fields
|
foreach (var header in headers) {
|
||||||
));
|
writer.Write(header.Key);
|
||||||
byte[] buffer1 = new byte[buffer0.Length + payload.Length];
|
writer.Write(':');
|
||||||
Array.Copy(buffer0, buffer1, buffer0.Length);
|
writer.Write(header.Value);
|
||||||
Array.Copy(payload, 0, buffer1, buffer0.Length, payload.Length);
|
writer.Write("\r\n");
|
||||||
Logger.Log("main", 0, "Network", Encoding.UTF8.GetString(buffer1));
|
}
|
||||||
stream.Write(buffer1, 0, buffer1.Length);
|
writer.Write("\r\n");
|
||||||
stream.Flush();
|
if (payload != null) writer.Write(payload);
|
||||||
var response = new HttpResponse(stream);
|
writer.Flush();
|
||||||
|
}
|
||||||
|
var response = new Http11Response(stream);
|
||||||
Logger.Log("main", 0, "Network", "{0}", response);
|
Logger.Log("main", 0, "Network", "{0}", response);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 2745c44c3cc32be4ab3a43888c14b9a1
|
guid: 5a795e416e54c69418de1a3c27a88932
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -1,30 +1,41 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Cryville.Common.Network {
|
namespace Cryville.Common.Network.Http11 {
|
||||||
public class HttpResponse {
|
public class Http11Response : IDisposable {
|
||||||
static readonly char[] spchar = new char[]{ ' ' };
|
static readonly char[] spchar = new char[]{ ' ' };
|
||||||
public string HttpVersion { get; private set; }
|
public string HttpVersion { get; private set; }
|
||||||
public string StatusCode { get; private set; }
|
public string StatusCode { get; private set; }
|
||||||
public string ReasonPhase { get; private set; }
|
public string ReasonPhase { get; private set; }
|
||||||
public Dictionary<string, string> Headers { get; private set; }
|
public Dictionary<string, string> Headers { get; private set; }
|
||||||
public HttpResponseStream MessageBody { get; private set; }
|
public Http11ResponseStream MessageBody { get; private set; }
|
||||||
internal HttpResponse(Stream stream) {
|
internal Http11Response(Stream stream) {
|
||||||
var reader = new BinaryReader(stream, Encoding.ASCII);
|
var reader = new BinaryReader(stream, Encoding.ASCII);
|
||||||
var statu_line = ReadLine(reader).Split(spchar, 3);
|
var statu_line = ReadLine(reader).Split(spchar, 3);
|
||||||
HttpVersion = statu_line[0];
|
HttpVersion = statu_line[0];
|
||||||
StatusCode = statu_line[1];
|
StatusCode = statu_line[1];
|
||||||
ReasonPhase = statu_line[2];
|
ReasonPhase = statu_line[2];
|
||||||
Logger.Log("main", 0, "Network", "Receive Response: {0} {1} {2}", HttpVersion, StatusCode, ReasonPhase);
|
Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
Headers = new Dictionary<string, string>();
|
|
||||||
while (ParseHeader(reader, Headers)) ;
|
while (ParseHeader(reader, Headers)) ;
|
||||||
if (Headers.ContainsKey("content-length")) {
|
if (Headers.ContainsKey("content-length")) {
|
||||||
int length = int.Parse(Headers["content-length"]);
|
int length = int.Parse(Headers["content-length"]);
|
||||||
MessageBody = new HttpResponseBlockStream(reader, length);
|
MessageBody = new Http11ResponseBlockStream(reader, length);
|
||||||
}
|
}
|
||||||
else if (Headers.ContainsKey("transfer-encoding") && Headers["transfer-encoding"] == "chunked") {
|
else if (Headers.ContainsKey("transfer-encoding") && Headers["transfer-encoding"] == "chunked") {
|
||||||
MessageBody = new HttpResponseChunkedStream(reader);
|
MessageBody = new Http11ResponseChunkedStream(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() {
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Dispose(bool disposing) {
|
||||||
|
if (disposing) {
|
||||||
|
MessageBody.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,12 +47,11 @@ namespace Cryville.Common.Network {
|
|||||||
// TODO Multiline header
|
// TODO Multiline header
|
||||||
var header = ReadLine(reader);
|
var header = ReadLine(reader);
|
||||||
if (header == "") return false;
|
if (header == "") return false;
|
||||||
var s = header.Split(':');
|
var s = header.Split(':', 2);
|
||||||
string field_name = s[0].Trim().ToLower();
|
string field_name = s[0].Trim().ToLower();
|
||||||
string field_value = s[1].Trim();
|
string field_value = s[1].Trim();
|
||||||
if (headers.ContainsKey(field_name)) headers[field_name] += "," + field_value;
|
if (headers.ContainsKey(field_name)) headers[field_name] += "," + field_value;
|
||||||
else headers.Add(field_name, field_value);
|
else headers.Add(field_name, field_value);
|
||||||
Logger.Log("main", 0, "Network", "Receive Header {0}: {1}", field_name, field_value);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: ec18f22479042d747b88c093aa90c5c0
|
guid: 71234dd1c93d47b4893750686b2333a3
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -4,8 +4,8 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Cryville.Common.Network {
|
namespace Cryville.Common.Network.Http11 {
|
||||||
public abstract class HttpResponseStream : Stream {
|
public abstract class Http11ResponseStream : Stream {
|
||||||
public override bool CanRead { get { return true; } }
|
public override bool CanRead { get { return true; } }
|
||||||
|
|
||||||
public override bool CanSeek { get { return false; } }
|
public override bool CanSeek { get { return false; } }
|
||||||
@@ -36,11 +36,11 @@ namespace Cryville.Common.Network {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class HttpResponseBlockStream : HttpResponseStream {
|
internal sealed class Http11ResponseBlockStream : Http11ResponseStream {
|
||||||
readonly BinaryReader _reader;
|
readonly BinaryReader _reader;
|
||||||
readonly int _length;
|
readonly int _length;
|
||||||
int _pos = 0;
|
int _pos = 0;
|
||||||
internal HttpResponseBlockStream(BinaryReader reader, int length) {
|
internal Http11ResponseBlockStream(BinaryReader reader, int length) {
|
||||||
_reader = reader;
|
_reader = reader;
|
||||||
_length = length;
|
_length = length;
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,6 @@ namespace Cryville.Common.Network {
|
|||||||
if (recv_len == 0) return 0;
|
if (recv_len == 0) return 0;
|
||||||
while (recv < recv_len) {
|
while (recv < recv_len) {
|
||||||
recv += _reader.Read(buffer, offset + recv, count - recv);
|
recv += _reader.Read(buffer, offset + recv, count - recv);
|
||||||
Logger.Log("main", 0, "Network", "Message body received: {0}/{1}/{2}", recv, recv_len, _length);
|
|
||||||
}
|
}
|
||||||
_pos += recv_len;
|
_pos += recv_len;
|
||||||
return recv_len;
|
return recv_len;
|
||||||
@@ -62,36 +61,34 @@ namespace Cryville.Common.Network {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class HttpResponseChunkedStream : HttpResponseStream {
|
internal sealed class Http11ResponseChunkedStream : Http11ResponseStream {
|
||||||
readonly BinaryReader _reader;
|
readonly BinaryReader _reader;
|
||||||
byte[] _chunk = null;
|
byte[] _chunk = null;
|
||||||
int _pos = 0;
|
int _pos = 0;
|
||||||
internal HttpResponseChunkedStream(BinaryReader reader) {
|
internal Http11ResponseChunkedStream(BinaryReader reader) {
|
||||||
_reader = reader;
|
_reader = reader;
|
||||||
ReadChunk();
|
ReadChunk();
|
||||||
}
|
}
|
||||||
public void ReadChunk() {
|
public void ReadChunk() {
|
||||||
if (_chunk != null && _chunk.Length == 0) return;
|
if (_chunk != null && _chunk.Length == 0) return;
|
||||||
string[] chunkHeader = HttpResponse.ReadLine(_reader).Split(';');
|
string[] chunkHeader = Http11Response.ReadLine(_reader).Split(';');
|
||||||
// int chunkSize = Array.IndexOf(LEN, chunkHeader[0].ToLower()[0]);
|
int chunkSize;
|
||||||
int chunkSize = int.Parse(chunkHeader[0], NumberStyles.HexNumber);
|
if (!int.TryParse(chunkHeader[0], NumberStyles.HexNumber, CultureInfo.InvariantCulture, out chunkSize))
|
||||||
if (chunkSize == -1)
|
|
||||||
throw new IOException("Corrupted chunk received");
|
throw new IOException("Corrupted chunk received");
|
||||||
if (chunkSize == 0) {
|
if (chunkSize == 0) {
|
||||||
_chunk = new byte[0];
|
_chunk = new byte[0];
|
||||||
// TODO TE Header, now just discard
|
// TODO TE Header, now just discard
|
||||||
var headers = new Dictionary<string, string>();
|
var headers = new Dictionary<string, string>();
|
||||||
while (HttpResponse.ParseHeader(_reader, headers)) ;
|
while (Http11Response.ParseHeader(_reader, headers)) ;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_chunk = new byte[chunkSize];
|
_chunk = new byte[chunkSize];
|
||||||
int recv = 0;
|
int recv = 0;
|
||||||
while (recv < chunkSize) {
|
while (recv < chunkSize) {
|
||||||
recv += _reader.Read(_chunk, recv, chunkSize - recv);
|
recv += _reader.Read(_chunk, recv, chunkSize - recv);
|
||||||
Logger.Log("main", 0, "Network", "Message chunk received: {0}/{1}", recv, chunkSize);
|
|
||||||
}
|
}
|
||||||
_pos = 0;
|
_pos = 0;
|
||||||
if (HttpResponse.ReadLine(_reader) != "")
|
if (Http11Response.ReadLine(_reader) != "")
|
||||||
throw new IOException("Corrupted chunk received");
|
throw new IOException("Corrupted chunk received");
|
||||||
}
|
}
|
||||||
public override int Read(byte[] buffer, int offset, int count) {
|
public override int Read(byte[] buffer, int offset, int count) {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: df66519fa93e1b94ea5bb1702cc91b3f
|
guid: 49a8d5b9869e5bb42bafbe71f84fecc5
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Cryville.Common.Network {
|
namespace Cryville.Common.Network.Http11 {
|
||||||
public class HttpsClient : HttpClient {
|
public class Https11Client : Http11Client {
|
||||||
readonly TlsClient _tlsClient;
|
readonly TlsClient _tlsClient;
|
||||||
|
|
||||||
protected override Stream Stream {
|
protected override Stream Stream {
|
||||||
@@ -16,7 +16,7 @@ namespace Cryville.Common.Network {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpsClient(Uri baseUri) : base(baseUri, 443) {
|
public Https11Client(Uri baseUri) : base(baseUri, 443) {
|
||||||
_tlsClient = new TlsClient(RawTcpStream, baseUri.Host);
|
_tlsClient = new TlsClient(RawTcpStream, baseUri.Host);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 2b7b45ff20c33ac47b476371673b037c
|
guid: c5c233e6228ce204fa1a9724c48ac8fe
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 5ea931bf5488011468f3d1243a038874
|
|
||||||
timeCreated: 1622589817
|
|
||||||
licenseType: Free
|
|
||||||
MonoImporter:
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 07e8215a93e3eb1418685009f0c58dcd
|
|
||||||
timeCreated: 1622596274
|
|
||||||
licenseType: Free
|
|
||||||
MonoImporter:
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f191de447a708da4f9d230e6545ce0a6
|
|
||||||
timeCreated: 1635470462
|
|
||||||
licenseType: Free
|
|
||||||
MonoImporter:
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user