178 Commits

659 changed files with 4242 additions and 30969 deletions

1
.gitignore vendored
View File

@@ -68,3 +68,4 @@ crashlytics-build.properties
/UserSettings /UserSettings
/*.zip /*.zip
*.lnk *.lnk
/HybridCLRData

Binary file not shown.

Binary file not shown.

View File

@@ -1,7 +1,8 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 215ebdd4cb9187741a2e24f5e7d8511d guid: 2e0c61e29fd90f04b9e41265d93e2029
DefaultImporter: NativeFormatImporter:
externalObjects: {} externalObjects: {}
mainObjectFileID: 7400000
userData: userData:
assetBundleName: assetBundleName:
assetBundleVariant: assetBundleVariant:

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d815e4d844e6a1c4d849e96e199f8881
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 7400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 82867c59112ff5a419fbea2ebff2d3b9
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 7400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,90 @@
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;
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 4ffe72fef6ebb9e4da3571b4117f0d6d guid: ec18f22479042d747b88c093aa90c5c0
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@@ -14,6 +14,10 @@
_objs = new T[capacity]; _objs = new T[capacity];
} }
/// <summary> /// <summary>
/// The count of objects rented from the pool.
/// </summary>
public int RentedCount { get { return _index; } }
/// <summary>
/// Rents a object from the pool. /// Rents a object from the pool.
/// </summary> /// </summary>
/// <returns>The rented object.</returns> /// <returns>The rented object.</returns>
@@ -24,6 +28,7 @@
_objs[_index++] = null; _objs[_index++] = null;
} }
if (obj == null) obj = Construct(); if (obj == null) obj = Construct();
else Reset(obj);
return obj; return obj;
} }
/// <summary> /// <summary>
@@ -38,5 +43,10 @@
/// </summary> /// </summary>
/// <returns>The new instance.</returns> /// <returns>The new instance.</returns>
protected abstract T Construct(); protected abstract T Construct();
/// <summary>
/// Resets an object.
/// </summary>
/// <param name="obj">The object.</param>
protected virtual void Reset(T obj) { }
} }
} }

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: dbc046e7cabacbb4fbf74520399a7340 guid: c4ef48e4a4983de4e9c31483df2a918e
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 6823ead66b33bc048bbad48719feb25d guid: 9ec674235c0dd6744af2dab2b58dd53c
folderAsset: yes folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}

View File

@@ -0,0 +1,8 @@
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);
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: f5b3f3294f679f14f8ec1195b0def630 guid: 73fb17b484b343242bcce27c15ed7d44
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@@ -0,0 +1,46 @@
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);
}
}
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;
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 23377bf2926d93a4b8e3f3ab6040c7f2 guid: 2517e8f040bd36f46948e5fafaf5335c
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@@ -0,0 +1,48 @@
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;
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: e3c5a8bf05d5e284ba498e91cb0dd35e guid: d9ed5ea8b7b1a934287e7ec5971166c0
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@@ -0,0 +1,8 @@
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);
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 046617672d437de4ab7e644a55defd3b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,47 @@
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);
}
}
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;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1f87dfb8f6a1f5640b6deae741cd715c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
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;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 57fc9f037c1fda5449e2a365a835c82c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -3,7 +3,6 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
namespace Cryville.Common.Font { namespace Cryville.Common.Font {
public abstract class FontFile : IEnumerable<Typeface> { public abstract class FontFile : IEnumerable<Typeface> {

View File

@@ -1,30 +1,30 @@
using Cryville.Common.Culture; using System.Collections.Generic;
using System;
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 {
protected IReadOnlyDictionary<string, Typeface> MapFullNameToTypeface { get; private set; } public IReadOnlyDictionary<string, IReadOnlyCollection<Typeface>> MapFullNameToTypeface { get; private set; }
protected 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, Typeface>(); var map1 = new Dictionary<string, List<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()) {
map1.Add(f.FullName, f); List<Typeface> set1;
List<Typeface> set; if (!map1.TryGetValue(f.FullName, out set1)) {
if (!map2.TryGetValue(f.FamilyName, out set)) { map1.Add(f.FullName, set1 = new List<Typeface>());
map2.Add(f.FamilyName, set = new List<Typeface>());
} }
set.Add(f); set1.Add(f);
List<Typeface> set2;
if (!map2.TryGetValue(f.FamilyName, out set2)) {
map2.Add(f.FamilyName, set2 = new List<Typeface>());
}
set2.Add(f);
} }
MapFullNameToTypeface = map1; MapFullNameToTypeface = map1.ToDictionary(i => i.Key, i => (IReadOnlyCollection<Typeface>)i.Value);
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();
public abstract IEnumerable<Typeface> MatchScript(string script = null);
public abstract IEnumerable<Typeface> MatchFamily(string[] candidates);
protected static IEnumerable<Typeface> ScanDirectoryForTypefaces(string dir) { protected static IEnumerable<Typeface> ScanDirectoryForTypefaces(string dir) {
foreach (var f in new DirectoryInfo(dir).EnumerateFiles()) { foreach (var f in new DirectoryInfo(dir).EnumerateFiles()) {
FontFile file; FontFile file;
@@ -54,195 +54,10 @@ namespace Cryville.Common.Font {
protected override IEnumerable<Typeface> EnumerateAllTypefaces() { protected override IEnumerable<Typeface> EnumerateAllTypefaces() {
return ScanDirectoryForTypefaces("/system/fonts"); return ScanDirectoryForTypefaces("/system/fonts");
} }
public override IEnumerable<Typeface> MatchScript(string script = null) {
throw new NotImplementedException();
}
public override IEnumerable<Typeface> MatchFamily(string[] candidates) {
throw new NotImplementedException();
}
} }
public class FontManagerWindows : FontManager { public class FontManagerWindows : FontManager {
public static Dictionary<string, List<string>> MapScriptToTypefaces = new Dictionary<string, List<string>>();
static FontManagerWindows() {
if (Environment.OSVersion.Platform != PlatformID.Win32NT) return;
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
MapScriptToTypefaces["zyyy"].Insert(0, "SimSun"); // Custom
MapScriptToTypefaces["zyyy"].Insert(0, "SimHei"); // Custom
MapScriptToTypefaces["zyyy"].Insert(0, "Arial");
MapScriptToTypefaces["zyyy"].Insert(0, "Times New Roman");
MapScriptToTypefaces["zyyy"].Insert(0, "Segoe UI"); // Custom
MapScriptToTypefaces["arab"].Insert(0, "Tahoma");
MapScriptToTypefaces["cyrl"].Insert(0, "Times New Roman");
MapScriptToTypefaces["grek"].Insert(0, "Times New Roman");
MapScriptToTypefaces["hebr"].Insert(0, "David");
MapScriptToTypefaces["jpan"].Insert(0, "MS PGothic");
MapScriptToTypefaces["latn"].Insert(0, "Times New Roman");
MapScriptToTypefaces["hans"].Insert(0, "SimSun");
MapScriptToTypefaces["hans"].Insert(0, "SimHei"); // Custom
MapScriptToTypefaces["thai"].Insert(0, "Tahoma");
MapScriptToTypefaces["hans"].Insert(0, "PMingLiU");
// Reference: https://learn.microsoft.com/en-us/globalization/input/font-support
var ver = Environment.OSVersion.Version;
if (ver >= new Version(5, 0)) { // Windows 2000
MapScriptToTypefaces["armn"].Insert(0, "Sylfaen");
MapScriptToTypefaces["deva"].Insert(0, "Mangal");
MapScriptToTypefaces["geor"].Insert(0, "Sylfaen");
MapScriptToTypefaces["taml"].Insert(0, "Latha");
}
if (ver >= new Version(5, 1)) { // Windows XP
MapScriptToTypefaces["gujr"].Insert(0, "Shruti");
MapScriptToTypefaces["guru"].Insert(0, "Raavi");
MapScriptToTypefaces["knda"].Insert(0, "Tunga");
MapScriptToTypefaces["syrc"].Insert(0, "Estrangelo Edessa");
MapScriptToTypefaces["telu"].Insert(0, "Gautami");
MapScriptToTypefaces["thaa"].Insert(0, "MV Boli");
// SP2
MapScriptToTypefaces["beng"].Insert(0, "Vrinda");
MapScriptToTypefaces["mlym"].Insert(0, "Kartika");
}
if (ver >= new Version(6, 0)) { // Windows Vista
MapScriptToTypefaces["cans"].Insert(0, "Euphemia");
MapScriptToTypefaces["cher"].Insert(0, "Plantagenet");
MapScriptToTypefaces["ethi"].Insert(0, "Nyala");
MapScriptToTypefaces["khmr"].Insert(0, "DaunPenh MoolBoran");
MapScriptToTypefaces["laoo"].Insert(0, "DokChampa");
MapScriptToTypefaces["mong"].Insert(0, "Mongolian Baiti");
MapScriptToTypefaces["orya"].Insert(0, "Kalinga");
MapScriptToTypefaces["sinh"].Insert(0, "Iskoola Pota");
MapScriptToTypefaces["tibt"].Insert(0, "Microsoft Himalaya");
MapScriptToTypefaces["yiii"].Insert(0, "Microsoft Yi Baiti");
MapScriptToTypefaces["arab"].Insert(0, "Segoe UI");
MapScriptToTypefaces["cyrl"].Insert(0, "Segoe UI");
MapScriptToTypefaces["grek"].Insert(0, "Segoe UI");
MapScriptToTypefaces["latn"].Insert(0, "Segoe UI");
MapScriptToTypefaces["hans"].Add("SimSun-ExtB");
MapScriptToTypefaces["hant"].Add("MingLiU-ExtB");
MapScriptToTypefaces["hant"].Add("MingLiU_HKSCS-ExtB");
MapScriptToTypefaces["arab"].Add("Microsoft Uighur");
MapScriptToTypefaces["zmth"].Insert(0, "Cambria Math");
// Reference: https://en.wikipedia.org/wiki/List_of_CJK_fonts
MapScriptToTypefaces["jpan"].Insert(0, "Meiryo");
MapScriptToTypefaces["hans"].Insert(0, "Microsoft YaHei");
}
if (ver >= new Version(6, 1)) { // Windows 7
MapScriptToTypefaces["brai"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["dsrt"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["talu"].Insert(0, "Microsoft New Tai Lue");
MapScriptToTypefaces["ogam"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["osma"].Insert(0, "Ebrima");
MapScriptToTypefaces["phag"].Insert(0, "Microsoft PhagsPa");
MapScriptToTypefaces["runr"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["zsym"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["tale"].Insert(0, "Microsoft Tai Le");
MapScriptToTypefaces["tfng"].Insert(0, "Ebrima");
MapScriptToTypefaces["vaii"].Insert(0, "Ebrima");
}
if (ver >= new Version(6, 2)) { // Windows 8
MapScriptToTypefaces["glag"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["goth"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["hang"].Add("Malgun Gothic");
MapScriptToTypefaces["ital"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["lisu"].Insert(0, "Segoe UI");
MapScriptToTypefaces["mymr"].Insert(0, "Myanmar Text");
MapScriptToTypefaces["nkoo"].Insert(0, "Ebrima");
MapScriptToTypefaces["orkh"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["ethi"].Insert(0, "Ebrima");
MapScriptToTypefaces["cans"].Insert(0, "Gadugi");
MapScriptToTypefaces["hant"].Insert(0, "Microsoft JhengHei UI");
MapScriptToTypefaces["hans"].Insert(0, "Microsoft YaHei UI");
MapScriptToTypefaces["beng"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["deva"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["gujr"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["guru"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
MapScriptToTypefaces["knda"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
MapScriptToTypefaces["mlym"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["orya"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["sinh"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
MapScriptToTypefaces["taml"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
MapScriptToTypefaces["telu"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["armn"].Insert(0, "Segoe UI");
MapScriptToTypefaces["geor"].Insert(0, "Segoe UI");
MapScriptToTypefaces["hebr"].Insert(0, "Segoe UI");
}
if (ver >= new Version(6, 3)) { // Windows 8.1
MapScriptToTypefaces["bugi"].Insert(0, "Leelawadee UI");
MapScriptToTypefaces["copt"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["java"].Insert(0, "Javanese Text");
MapScriptToTypefaces["merc"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["olck"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["sora"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["khmr"].Insert(0, "Leelawadee UI");
MapScriptToTypefaces["laoo"].Insert(0, "Leelawadee UI");
MapScriptToTypefaces["thai"].Insert(0, "Leelawadee UI");
MapScriptToTypefaces["zsye"].Insert(0, "Segoe UI Emoji");
}
if (ver >= new Version(10, 0)) { // Windows 10
MapScriptToTypefaces["brah"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["cari"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["cprt"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["egyp"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["armi"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["phli"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["prti"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["khar"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["lyci"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["lydi"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["phnx"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["xpeo"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["sarb"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["shaw"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["xsux"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["ugar"].Insert(0, "Segoe UI Historic");
// Segoe UI Symbol -> Segoe UI Historic
MapScriptToTypefaces["glag"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["goth"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["merc"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["ogam"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["ital"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["orkh"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["runr"].Insert(0, "Segoe UI Historic");
//
MapScriptToTypefaces["jpan"].Insert(0, "Yu Gothic UI");
MapScriptToTypefaces["zsym"].Add("Segoe MDL2 Assets");
}
}
protected override IEnumerable<Typeface> EnumerateAllTypefaces() { protected override IEnumerable<Typeface> EnumerateAllTypefaces() {
return ScanDirectoryForTypefaces("C:/Windows/Fonts"); return ScanDirectoryForTypefaces("C:/Windows/Fonts");
} }
public override IEnumerable<Typeface> MatchScript(string script = null) {
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) {
Typeface typeface;
if (MapFullNameToTypeface.TryGetValue(candidate, out typeface)) {
yield return typeface;
}
IReadOnlyCollection<Typeface> typefaces;
if (MapNameToTypefaces.TryGetValue(candidate, out typefaces)) {
foreach (var typeface2 in typefaces) {
if (typeface2 == typeface) continue;
yield return typeface2;
}
}
}
}
}
candidateScripts = ScriptUtils.EnumerateFallbackScripts(script);
}
}
public override IEnumerable<Typeface> MatchFamily(string[] candidates) {
throw new NotImplementedException();
}
} }
} }

View File

@@ -0,0 +1,329 @@
using Cryville.Common.Culture;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Cryville.Common.Font {
public abstract class FontMatcher {
protected FontManager Manager { get; private set; }
public FontMatcher(FontManager manafer) { Manager = manafer; }
public abstract IEnumerable<Typeface> MatchScript(string script = null, bool distinctFamily = false);
}
public class FallbackListFontMatcher : FontMatcher {
public Dictionary<string, List<string>> MapScriptToTypefaces = new Dictionary<string, List<string>>();
public void LoadDefaultWindowsFallbackList() {
if (Environment.OSVersion.Platform != PlatformID.Win32NT) return;
MapScriptToTypefaces.Clear();
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
MapScriptToTypefaces["zyyy"].Insert(0, "SimSun"); // Custom
MapScriptToTypefaces["zyyy"].Insert(0, "SimHei"); // Custom
MapScriptToTypefaces["zyyy"].Insert(0, "Microsoft YaHei"); // Custom
MapScriptToTypefaces["zyyy"].Insert(0, "Arial");
MapScriptToTypefaces["zyyy"].Insert(0, "Times New Roman");
MapScriptToTypefaces["zyyy"].Insert(0, "Segoe UI"); // Custom
MapScriptToTypefaces["arab"].Insert(0, "Tahoma");
MapScriptToTypefaces["cyrl"].Insert(0, "Times New Roman");
MapScriptToTypefaces["grek"].Insert(0, "Times New Roman");
MapScriptToTypefaces["hebr"].Insert(0, "David");
MapScriptToTypefaces["jpan"].Insert(0, "MS PGothic");
MapScriptToTypefaces["latn"].Insert(0, "Times New Roman");
MapScriptToTypefaces["hans"].Insert(0, "SimSun");
MapScriptToTypefaces["hans"].Insert(0, "SimHei"); // Custom
MapScriptToTypefaces["thai"].Insert(0, "Tahoma");
MapScriptToTypefaces["hans"].Insert(0, "PMingLiU");
// Reference: https://learn.microsoft.com/en-us/globalization/input/font-support
var ver = Environment.OSVersion.Version;
if (ver >= new Version(5, 0)) { // Windows 2000
MapScriptToTypefaces["armn"].Insert(0, "Sylfaen");
MapScriptToTypefaces["deva"].Insert(0, "Mangal");
MapScriptToTypefaces["geor"].Insert(0, "Sylfaen");
MapScriptToTypefaces["taml"].Insert(0, "Latha");
}
if (ver >= new Version(5, 1)) { // Windows XP
MapScriptToTypefaces["gujr"].Insert(0, "Shruti");
MapScriptToTypefaces["guru"].Insert(0, "Raavi");
MapScriptToTypefaces["knda"].Insert(0, "Tunga");
MapScriptToTypefaces["syrc"].Insert(0, "Estrangelo Edessa");
MapScriptToTypefaces["telu"].Insert(0, "Gautami");
MapScriptToTypefaces["thaa"].Insert(0, "MV Boli");
// SP2
MapScriptToTypefaces["beng"].Insert(0, "Vrinda");
MapScriptToTypefaces["mlym"].Insert(0, "Kartika");
}
if (ver >= new Version(6, 0)) { // Windows Vista
MapScriptToTypefaces["cans"].Insert(0, "Euphemia");
MapScriptToTypefaces["cher"].Insert(0, "Plantagenet");
MapScriptToTypefaces["ethi"].Insert(0, "Nyala");
MapScriptToTypefaces["khmr"].Insert(0, "DaunPenh MoolBoran");
MapScriptToTypefaces["laoo"].Insert(0, "DokChampa");
MapScriptToTypefaces["mong"].Insert(0, "Mongolian Baiti");
MapScriptToTypefaces["orya"].Insert(0, "Kalinga");
MapScriptToTypefaces["sinh"].Insert(0, "Iskoola Pota");
MapScriptToTypefaces["tibt"].Insert(0, "Microsoft Himalaya");
MapScriptToTypefaces["yiii"].Insert(0, "Microsoft Yi Baiti");
MapScriptToTypefaces["arab"].Insert(0, "Segoe UI");
MapScriptToTypefaces["cyrl"].Insert(0, "Segoe UI");
MapScriptToTypefaces["grek"].Insert(0, "Segoe UI");
MapScriptToTypefaces["latn"].Insert(0, "Segoe UI");
MapScriptToTypefaces["hans"].Add("SimSun-ExtB");
MapScriptToTypefaces["hant"].Add("MingLiU-ExtB");
MapScriptToTypefaces["hant"].Add("MingLiU_HKSCS-ExtB");
MapScriptToTypefaces["arab"].Add("Microsoft Uighur");
MapScriptToTypefaces["zmth"].Insert(0, "Cambria Math");
// Reference: https://en.wikipedia.org/wiki/List_of_CJK_fonts
MapScriptToTypefaces["jpan"].Insert(0, "Meiryo");
MapScriptToTypefaces["hans"].Insert(0, "Microsoft YaHei");
}
if (ver >= new Version(6, 1)) { // Windows 7
MapScriptToTypefaces["brai"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["dsrt"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["talu"].Insert(0, "Microsoft New Tai Lue");
MapScriptToTypefaces["ogam"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["osma"].Insert(0, "Ebrima");
MapScriptToTypefaces["phag"].Insert(0, "Microsoft PhagsPa");
MapScriptToTypefaces["runr"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["zsym"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["tale"].Insert(0, "Microsoft Tai Le");
MapScriptToTypefaces["tfng"].Insert(0, "Ebrima");
MapScriptToTypefaces["vaii"].Insert(0, "Ebrima");
}
if (ver >= new Version(6, 2)) { // Windows 8
MapScriptToTypefaces["glag"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["goth"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["hang"].Add("Malgun Gothic");
MapScriptToTypefaces["ital"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["lisu"].Insert(0, "Segoe UI");
MapScriptToTypefaces["mymr"].Insert(0, "Myanmar Text");
MapScriptToTypefaces["nkoo"].Insert(0, "Ebrima");
MapScriptToTypefaces["orkh"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["ethi"].Insert(0, "Ebrima");
MapScriptToTypefaces["cans"].Insert(0, "Gadugi");
MapScriptToTypefaces["hant"].Insert(0, "Microsoft JhengHei UI");
MapScriptToTypefaces["hans"].Insert(0, "Microsoft YaHei UI");
MapScriptToTypefaces["beng"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["deva"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["gujr"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["guru"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
MapScriptToTypefaces["knda"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
MapScriptToTypefaces["mlym"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["orya"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["sinh"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
MapScriptToTypefaces["taml"].Insert(0, "Nirmala UI"); // NOT DOCUMENTED, UNVERIFIED
MapScriptToTypefaces["telu"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["armn"].Insert(0, "Segoe UI");
MapScriptToTypefaces["geor"].Insert(0, "Segoe UI");
MapScriptToTypefaces["hebr"].Insert(0, "Segoe UI");
}
if (ver >= new Version(6, 3)) { // Windows 8.1
MapScriptToTypefaces["bugi"].Insert(0, "Leelawadee UI");
MapScriptToTypefaces["copt"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["java"].Insert(0, "Javanese Text");
MapScriptToTypefaces["merc"].Insert(0, "Segoe UI Symbol");
MapScriptToTypefaces["olck"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["sora"].Insert(0, "Nirmala UI");
MapScriptToTypefaces["khmr"].Insert(0, "Leelawadee UI");
MapScriptToTypefaces["laoo"].Insert(0, "Leelawadee UI");
MapScriptToTypefaces["thai"].Insert(0, "Leelawadee UI");
MapScriptToTypefaces["zsye"].Insert(0, "Segoe UI Emoji");
}
if (ver >= new Version(10, 0)) { // Windows 10
MapScriptToTypefaces["brah"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["cari"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["cprt"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["egyp"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["armi"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["phli"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["prti"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["khar"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["lyci"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["lydi"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["phnx"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["xpeo"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["sarb"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["shaw"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["xsux"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["ugar"].Insert(0, "Segoe UI Historic");
// Segoe UI Symbol -> Segoe UI Historic
MapScriptToTypefaces["glag"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["goth"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["merc"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["ogam"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["ital"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["orkh"].Insert(0, "Segoe UI Historic");
MapScriptToTypefaces["runr"].Insert(0, "Segoe UI Historic");
//
MapScriptToTypefaces["jpan"].Insert(0, "Yu Gothic UI");
MapScriptToTypefaces["zsym"].Add("Segoe MDL2 Assets");
}
}
public void LoadDefaultAndroidFallbackList() {
if (Environment.OSVersion.Platform != PlatformID.Unix) return;
MapScriptToTypefaces.Clear();
ScriptUtils.FillKeysWithScripts(MapScriptToTypefaces, () => new List<string>());
MapScriptToTypefaces["zyyy"].Insert(0, "Noto Sans CJK TC"); // TODO Modify default fallback
MapScriptToTypefaces["zyyy"].Insert(0, "Noto Sans CJK JP");
MapScriptToTypefaces["zyyy"].Insert(0, "Noto Sans CJK SC");
MapScriptToTypefaces["zyyy"].Insert(0, "Roboto");
MapScriptToTypefaces["zsye"].Insert(0, "Noto Color Emoji");
MapScriptToTypefaces["zsye"].Add("Noto Color Emoji Flags");
MapScriptToTypefaces["arab"].Insert(0, "Noto Naskh Arabic");
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);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: afcde0ad1865db24da79ca1ce7256791
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
namespace Cryville.Common.Font { namespace Cryville.Common.Font {
@@ -46,7 +45,7 @@ namespace Cryville.Common.Font {
return tableDirectoryOffsets; return tableDirectoryOffsets;
} }
public override TableDirectory GetSubTable(UInt32 item) { public override TableDirectory GetSubTable(UInt32 item) {
var i = (UInt32)item; var i = item;
return new TableDirectory(Reader, i); return new TableDirectory(Reader, i);
} }
} }
@@ -77,6 +76,7 @@ namespace Cryville.Common.Font {
public override object GetSubTable(TableRecord item) { public override object GetSubTable(TableRecord item) {
switch (item.tableTag) { switch (item.tableTag) {
case "name": return new NameTable(Reader, item.offset); case "name": return new NameTable(Reader, item.offset);
case "meta": return new MetaTable(Reader, item.offset);
default: throw new NotImplementedException(); default: throw new NotImplementedException();
} }
} }
@@ -120,7 +120,7 @@ namespace Cryville.Common.Font {
for (int i = 0; i < langTagRecord.Count; i++) langTagRecord[i] = langTagRecord[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.Cast<NameRecord>().ToList(); return nameRecord;
} }
} }
public struct NameRecord { public struct NameRecord {
@@ -181,6 +181,39 @@ namespace Cryville.Common.Font {
return this; return this;
} }
} }
public sealed class MetaTable : FontTable<DataMap> {
readonly UInt32 version;
readonly UInt32 flags;
readonly UInt32 dataMapCount;
readonly List<DataMap> dataMaps = new List<DataMap>();
public MetaTable(BinaryReader reader, UInt32 offset) : base(reader, offset) {
version = reader.ReadUInt32();
flags = reader.ReadUInt32();
reader.ReadUInt32();
dataMapCount = reader.ReadUInt32();
for (UInt32 i = 0; i < dataMapCount; i++)
dataMaps.Add(new DataMap {
tag = reader.ReadTag(),
dataOffset = reader.ReadUInt32(),
dataLength = reader.ReadUInt32(),
});
for (int i = 0; i < dataMaps.Count; i++) dataMaps[i] = dataMaps[i].Load(reader, offset);
}
public sealed override IReadOnlyList<DataMap> GetItems() {
return dataMaps;
}
}
public struct DataMap {
public String tag;
public UInt32 dataOffset;
public UInt32 dataLength;
public String value { get; private set; }
public DataMap Load(BinaryReader reader, UInt32 origin) {
reader.BaseStream.Position = origin + dataOffset;
value = Encoding.ASCII.GetString(reader.ReadBytes((int)dataLength));
return this;
}
}
public static class BinaryReaderExtensions { public static class BinaryReaderExtensions {
public static string ReadTag(this BinaryReader reader) { public static string ReadTag(this BinaryReader reader) {
return Encoding.ASCII.GetString(reader.ReadBytes(4)); return Encoding.ASCII.GetString(reader.ReadBytes(4));

View File

@@ -2,6 +2,7 @@
namespace Cryville.Common { namespace Cryville.Common {
public struct Identifier : IEquatable<Identifier> { public struct Identifier : IEquatable<Identifier> {
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.SharedInstance.Retrieve(Key); } }
public Identifier(int key) { public Identifier(int key) {
@@ -24,11 +25,11 @@ namespace Cryville.Common {
if (Key == 0) return ""; if (Key == 0) return "";
return Name.ToString(); return Name.ToString();
} }
public static implicit operator Identifier(string identifier) { public static bool operator ==(Identifier lhs, Identifier rhs) {
return new Identifier(identifier); return lhs.Equals(rhs);
} }
public static implicit operator string(Identifier identifier) { public static bool operator !=(Identifier lhs, Identifier rhs) {
return identifier.ToString(); return !lhs.Equals(rhs);
} }
} }
} }

View File

@@ -54,16 +54,13 @@ namespace Cryville.Common.Math {
return res; return res;
} }
/// <summary> /// <summary>
/// Creates a <see cref="System.Single" /> column vector and fills it with polynomial coefficients. /// Fills a <see cref="System.Single" /> column vector with polynomial coefficients.
/// </summary> /// </summary>
/// <param name="size">The size of the column vector.</param> /// <param name="vec">The column vector.</param>
/// <param name="num">The base number.</param> /// <param name="num">The base number.</param>
/// <returns>A <see cref="System.Single" /> column vector filled with polynomial coefficients.</returns> public static void FillWithPolynomialCoefficients(ColumnVector<float> vec, float num) {
public static ColumnVector<float> WithPolynomialCoefficients(int size, float num) { for (var i = 0; i < vec.Size; i++)
var m = new ColumnVector<float>(size); vec[i] = (float)System.Math.Pow(num, i);
for (var i = 0; i < size; i++)
m[i] = (float)System.Math.Pow(num, i);
return m;
} }
} }
} }

View File

@@ -0,0 +1,44 @@
using SMath = System.Math;
namespace Cryville.Common.Math {
// Ported from https://github.com/arian/cubic-bezier/blob/master/index.js
public static class CubicBezier {
public static float Evaluate(float t, float x1, float y1, float x2, float y2, float epsilon) {
float x = t, t0, t1, t2, tx, d2, i;
for (t2 = x, i = 0; i < 8; i++) {
tx = CurveX(t2, x1, x2) - x;
if (SMath.Abs(tx) < epsilon) return CurveY(t2, y1, y2);
d2 = DerivativeCurveX(t2, x1, x2);
if (SMath.Abs(d2) < 1e-6) break;
t2 -= tx / d2;
}
t0 = 0; t1 = 1; t2 = x;
if (t2 < t0) return CurveY(t0, y1, y2);
if (t2 > t1) return CurveY(t1, y1, y2);
while (t0 < t1) {
tx = CurveX(t2, x1, x2);
if (SMath.Abs(tx - x) < epsilon) return CurveY(t2, y1, y2);
if (x > tx) t0 = t2;
else t1 = t2;
t2 = (t1 - t0) * .5f + t0;
}
return CurveY(t2, y1, y2);
}
static float CurveX(float t, float x1, float x2) {
float v = 1 - t;
return 3 * v * v * t * x1 + 3 * v * t * t * x2 + t * t * t;
}
static float CurveY(float t, float y1, float y2) {
float v = 1 - t;
return 3 * v * v * t * y1 + 3 * v * t * t * y2 + t * t * t;
}
static float DerivativeCurveX(float t, float x1, float x2) {
float v = 1 - t;
return 3 * (2 * (t - 1) * t + v * v) * x1 + 3 * (-t * t * t + 2 * v * t) * x2;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 17dd6f775fc965f43960da7166b55b87
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -12,12 +12,12 @@ namespace Cryville.Common.Math {
/// <param name="error">The error.</param> /// <param name="error">The error.</param>
/// <param name="n">The numerator.</param> /// <param name="n">The numerator.</param>
/// <param name="d">The denominator.</param> /// <param name="d">The denominator.</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="value" /> is less than 0 or <paramref name="error" /> is not greater than 0 or not less than 1.</exception> /// <exception cref="ArgumentOutOfRangeException"><paramref name="value" /> is less than 0 or <paramref name="error" /> is not greater than 0 or greater than 1.</exception>
public static void ToFraction(double value, double error, out int n, out int d) { public static void ToFraction(double value, double error, out int n, out int d) {
if (value < 0.0) if (value < 0.0)
throw new ArgumentOutOfRangeException("value", "Must be >= 0."); throw new ArgumentOutOfRangeException("value", "Must be >= 0.");
if (error <= 0.0 || error >= 1.0) if (error <= 0.0 || error > 1.0)
throw new ArgumentOutOfRangeException("accuracy", "Must be > 0 and < 1."); throw new ArgumentOutOfRangeException("error", "Must be > 0 and <= 1.");
int num = (int)System.Math.Floor(value); int num = (int)System.Math.Floor(value);
value -= num; value -= num;

View File

@@ -1,4 +1,4 @@
using System; using UnsafeIL;
namespace Cryville.Common.Math { namespace Cryville.Common.Math {
/// <summary> /// <summary>
@@ -40,11 +40,11 @@ namespace Cryville.Common.Math {
/// </summary> /// </summary>
/// <typeparam name="T">The vector type.</typeparam> /// <typeparam name="T">The vector type.</typeparam>
/// <param name="v">The column vector.</param> /// <param name="v">The column vector.</param>
/// <param name="result">The result column vector.</param>
/// <param name="o">The column operator.</param> /// <param name="o">The column operator.</param>
/// <returns>The column vector eliminated.</returns> public void Eliminate<T>(ColumnVector<T> v, ColumnVector<T> result, IVectorOperator<T> o) {
public ColumnVector<T> Eliminate<T>(ColumnVector<T> v, IVectorOperator<T> o) {
int s = Size; int s = Size;
Array.Copy(content, buffer, Size * Size); FillBuffer();
for (int i = 0; i < s; i++) refl[i] = i; for (int i = 0; i < s; i++) refl[i] = i;
for (int r = 0; r < s; r++) { for (int r = 0; r < s; r++) {
for (int r0 = r; r0 < s; r0++) for (int r0 = r; r0 < s; r0++)
@@ -66,14 +66,17 @@ namespace Cryville.Common.Math {
v[or1] = o.Add(v[or1], o.ScalarMultiply(-sf1, v[or])); v[or1] = o.Add(v[or1], o.ScalarMultiply(-sf1, v[or]));
} }
} }
ColumnVector<T> res = new ColumnVector<T>(s);
for (int r2 = s - 1; r2 >= 0; r2--) { for (int r2 = s - 1; r2 >= 0; r2--) {
var v2 = v[refl[r2]]; var v2 = v[refl[r2]];
for (int c2 = r2 + 1; c2 < s; c2++) for (int c2 = r2 + 1; c2 < s; c2++)
v2 = o.Add(v2, o.ScalarMultiply(-buffer[refl[r2], c2], res[refl[c2]])); v2 = o.Add(v2, o.ScalarMultiply(-buffer[refl[r2], c2], result[refl[c2]]));
res[refl[r2]] = v2; result[refl[r2]] = v2;
}
}
unsafe void FillBuffer() {
fixed (void* ptrc = content, ptrb = buffer) {
Unsafe.CopyBlock(ptrb, ptrc, (uint)(Size * Size * sizeof(float)));
} }
return res;
} }
/// <summary> /// <summary>
/// Creates a square matrix and fills it with polynomial coefficients. /// Creates a square matrix and fills it with polynomial coefficients.

View File

@@ -1,7 +1,21 @@
using System; using System;
namespace Cryville.Common.Pdt { namespace Cryville.Common.Pdt {
/// <summary>
/// Indicates that the attributed member is an element list.
/// </summary>
/// <remarks>
/// <para>An element list is a <see cref="System.Collections.IDictionary" /> or <see cref="Cryville.Common.Collections.Generic.IPairList" /> that represents a collection of PDT elements. There must be at most one element list in a class.</para>
/// </remarks>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class ElementListAttribute : Attribute { } public class ElementListAttribute : Attribute { }
public class ComponentListAttribute : Attribute { }
/// <summary>
/// Indicates that the attributed member is a property list.
/// </summary>
/// <remarks>
/// <para>A property list is a <see cref="System.Collections.IDictionary" /> or <see cref="Cryville.Common.Collections.Generic.IPairList" /> that represents a collection of PDT properties. There must be at most one property list in a class.</para>
/// </remarks>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class PropertyListAttribute : Attribute { } public class PropertyListAttribute : Attribute { }
} }

View File

@@ -186,7 +186,7 @@ namespace Cryville.Common.Pdt {
protected abstract PdtOperator GetOperator(PdtOperatorSignature sig); protected abstract PdtOperator GetOperator(PdtOperatorSignature sig);
unsafe void Operate(PdtOperator op, int pc, bool noset = false) { unsafe void Operate(PdtOperator op, int pc, bool noset = false) {
fixed (byte* pmem = _mem) { fixed (byte* pmem = _mem) {
op.Begin(this); op.Begin(this, pc);
for (int i = 0; i < pc; i++) { for (int i = 0; i < pc; i++) {
var frame = _stack[--_framecount]; var frame = _stack[--_framecount];
op.LoadOperand(new PdtVariableMemory(frame.Type, pmem + frame.Offset, frame.Length)); op.LoadOperand(new PdtVariableMemory(frame.Type, pmem + frame.Offset, frame.Length));

View File

@@ -11,7 +11,7 @@ namespace Cryville.Common.Pdt {
/// <summary> /// <summary>
/// Whether the value of this expression is constant. /// Whether the value of this expression is constant.
/// </summary> /// </summary>
/// <remarks>The value of this property is <c>false</c> until it is optimized.</remarks> /// <remarks>The value of this property is <see langword="false" /> until it is optimized.</remarks>
public bool IsConstant { get; internal set; } public bool IsConstant { get; internal set; }
internal bool IsPotentialConstant; internal bool IsPotentialConstant;
internal PdtExpression(LinkedList<PdtInstruction> ins) { internal PdtExpression(LinkedList<PdtInstruction> ins) {
@@ -92,7 +92,7 @@ namespace Cryville.Common.Pdt {
} }
} }
public partial class PdtInterpreter { public partial class PdtInterpreter {
readonly static Dictionary<char, int> OP_PRIORITY = new Dictionary<char, int> { static readonly Dictionary<char, int> OP_PRIORITY = new Dictionary<char, int> {
{ '@', 7 }, { '@', 7 },
{ '*', 6 }, { '/', 6 }, { '%', 6 }, { '*', 6 }, { '/', 6 }, { '%', 6 },
{ '+', 5 }, { '-', 5 }, { '+', 5 }, { '-', 5 },
@@ -103,7 +103,7 @@ namespace Cryville.Common.Pdt {
{ ',', 0 }, { ',', 0 },
{ '$', -1 }, { '$', -1 },
}; };
readonly static Dictionary<char, int> OP_TYPE = new Dictionary<char, int> { static readonly Dictionary<char, int> OP_TYPE = new Dictionary<char, int> {
{ '@', 0 }, { '@', 0 },
{ '*', 0 }, { '/', 0 }, { '%', 0 }, { '*', 0 }, { '/', 0 }, { '%', 0 },
{ '+', 0 }, { '-', 0 }, { '+', 0 }, { '-', 0 },
@@ -115,7 +115,7 @@ namespace Cryville.Common.Pdt {
{ '$', -1 }, { '$', -1 },
}; };
readonly static PdtExpression _emptyexp; static readonly PdtExpression _emptyexp;
static PdtInterpreter() { static PdtInterpreter() {
var ins = new LinkedList<PdtInstruction>(); var ins = new LinkedList<PdtInstruction>();
ins.AddLast(new PdtInstruction.PushConstant( ins.AddLast(new PdtInstruction.PushConstant(
@@ -143,7 +143,7 @@ namespace Cryville.Common.Pdt {
public override string ToString() { public override string ToString() {
return string.Format("0x{0:x4}: {1}", Type, Value); return string.Format("0x{0:x4}: {1}", Type, Value);
} }
public readonly static PdtExpToken EmptyOperator = new PdtExpToken { public static readonly PdtExpToken EmptyOperator = new PdtExpToken {
Type = 0x0080, Type = 0x0080,
Value = "$", Value = "$",
}; };

View File

@@ -6,30 +6,30 @@
/// <summary> /// <summary>
/// Error type. /// Error type.
/// </summary> /// </summary>
public readonly static int Error = 0x00525245; public const int Error = 0x00525245;
/// <summary> /// <summary>
/// Array of a same variable-length type, with a suffix indicating the element count and the element type. /// Array of a same variable-length type, with a suffix indicating the element count and the element type.
/// </summary> /// </summary>
public readonly static int Array = 0x00525241; public const int Array = 0x00525241;
/// <summary> /// <summary>
/// Null type. /// Null type.
/// </summary> /// </summary>
public readonly static int Null = 0x4c4c554e; public const int Null = 0x4c4c554e;
/// <summary> /// <summary>
/// IEEE 754 32-bit floating-point number. /// IEEE 754 32-bit floating-point number.
/// </summary> /// </summary>
public readonly static int Number = 0x004d554e; public const int Number = 0x004d554e;
/// <summary> /// <summary>
/// A sequence of UTF-16 code units, with a prefix indicating the number of the code units. /// A sequence of UTF-16 code units, with a prefix indicating the number of the code units.
/// </summary> /// </summary>
public readonly static int String = 0x00525453; public const int String = 0x00525453;
/// <summary> /// <summary>
/// A sequence of UTF-16 code units, with a prefix indicating the number of the code units, representing the name of an undefined variable. /// A sequence of UTF-16 code units, with a prefix indicating the number of the code units, representing the name of an undefined variable.
/// </summary> /// </summary>
public readonly static int Undefined = 0x00444e55; public const int Undefined = 0x00444e55;
/// <summary> /// <summary>
/// Vector of a same constant-length type, with a suffix indicating the element type. /// Vector of a same constant-length type, with a suffix indicating the element type.
/// </summary> /// </summary>
public readonly static int Vector = 0x00434556; public const int Vector = 0x00434556;
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using Cryville.Common.Collections;
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -27,16 +28,16 @@ namespace Cryville.Common.Pdt {
/// <item><term><c>0x1000</c></term><description>End of Key</description></item> /// <item><term><c>0x1000</c></term><description>End of Key</description></item>
/// </list> /// </list>
/// </remarks> /// </remarks>
readonly static int[] cm = new int[] { static readonly int[] cm = new int[] {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F // 0 1 2 3 4 5 6 7 8 9 A B C D E F
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0001, 0x0080, 0x0100, 0x0000, 0x0030, 0x0080, 0x0080, 0x0000, 0x0200, 0x0400, 0x0080, 0x0080, 0x0080, 0x0080, 0x0040, 0x0080, 0x0001, 0x0080, 0x0100, 0x0000, 0x0030, 0x0080, 0x0080, 0x0000, 0x0200, 0x0400, 0x0080, 0x0080, 0x0080, 0x0080, 0x0040, 0x0080,
0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x1000, 0x1800, 0x0080, 0x0080, 0x0080, 0x0030, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x0050, 0x1000, 0x1800, 0x0080, 0x0080, 0x0080, 0x0030,
0x0080, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0080, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000, 0x0000, 0x0000, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0080, 0x0000, 0x0080, 0x0030,
0x0000, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x1000, 0x0080, 0x1000, 0x0000, 0x0000, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x1000, 0x0080, 0x1000, 0x0080, 0x0000,
}; };
/// <summary> /// <summary>
@@ -54,43 +55,92 @@ namespace Cryville.Common.Pdt {
/// <param name="binder">The binder.</param> /// <param name="binder">The binder.</param>
/// <returns>The interpreted object.</returns> /// <returns>The interpreted object.</returns>
public static T Interpret<T>(string src, Binder binder) { public static T Interpret<T>(string src, Binder binder) {
return (T)new PdtInterpreter(src, typeof(T), binder).Interpret(); return (T)new PdtInterpreter(src, binder).Interpret(typeof(T));
} }
/// <summary>
/// The source string.
/// </summary>
public string Source { get; private set; } public string Source { get; private set; }
readonly Type _type; Binder _binder;
readonly Binder _binder; /// <summary>
/// The current position in the string being parsed by the interpreter.
/// </summary>
public int Position { get; private set; } public int Position { get; private set; }
#pragma warning disable IDE1006 #pragma warning disable IDE1006
/// <summary>
/// The character at the current position.
/// </summary>
/// <exception cref="IndexOutOfRangeException">The end of the source string is reached.</exception>
protected char cc { get { return Source[Position]; } } protected char cc { get { return Source[Position]; } }
/// <summary>
/// The category of the character.
/// </summary>
/// <exception cref="IndexOutOfRangeException">The end of the source string is reached.</exception>
protected int ct { get { return cm[cc]; } } protected int ct { get { return cm[cc]; } }
protected string tokenb(int flag) { // Token Whitelist /// <summary>
/// Reads a token until a character of type <paramref name="flag" /> is met.
/// </summary>
/// <param name="flag">The type filter.</param>
/// <returns>A token from the current position (inclusive) to the next character of type <paramref name="flag" /> (exclusive).</returns>
/// <exception cref="IndexOutOfRangeException">No character of type <paramref name="flag" /> is met.</exception>
protected string tokenb(int flag) {
int sp = Position; int sp = Position;
while ((ct & flag) == 0) Position++; while ((ct & flag) == 0) Position++;
return Source.Substring(sp, Position - sp); return Source.Substring(sp, Position - sp);
} }
protected string tokenw(int flag) { // Token Whitelist /// <summary>
/// Reads a token until a character that is not of type <paramref name="flag" /> is met.
/// </summary>
/// <param name="flag">The type filter.</param>
/// <returns>A token from the current position (inclusive) to the next character that is not of type <paramref name="flag" /> (exclusive).</returns>
/// <exception cref="IndexOutOfRangeException">No character that is not of type <paramref name="flag" /> is met.</exception>
protected string tokenw(int flag) {
int sp = Position; int sp = Position;
while ((ct & flag) != 0) Position++; while ((ct & flag) != 0) Position++;
return Source.Substring(sp, Position - sp); return Source.Substring(sp, Position - sp);
} }
/// <summary>
/// Skips over whitespaces.
/// </summary>
/// <exception cref="IndexOutOfRangeException">The end of the source string is reached.</exception>
protected void ws() { protected void ws() {
while ((ct & 0x0001) != 0) Position++; while ((ct & 0x0001) != 0) Position++;
} }
#pragma warning restore IDE1006 #pragma warning restore IDE1006
/// <summary>
/// Reads the current character and increments the position.
/// </summary>
/// <returns>The current character.</returns>
/// <exception cref="IndexOutOfRangeException">The end of the source string is reached.</exception>
protected char GetChar() { protected char GetChar() {
char r = cc; char r = cc;
Position++; Position++;
return r; return r;
} }
/// <summary>
/// Reads an identifier.
/// </summary>
/// <returns>An identifier.</returns>
/// <exception cref="IndexOutOfRangeException">The end of the source string is reached.</exception>
protected string GetIdentifier() { protected string GetIdentifier() {
if ((ct & 0x0020) == 0) return ""; if ((ct & 0x0020) == 0) return "";
return tokenw(0x0010); return tokenw(0x0010);
} }
/// <summary>
/// Reads a number.
/// </summary>
/// <returns>A number.</returns>
/// <exception cref="IndexOutOfRangeException">The end of the source string is reached.</exception>
protected string GetNumber() { protected string GetNumber() {
return tokenw(0x0040); return tokenw(0x0040);
} }
/// <summary>
/// Reads a string.
/// </summary>
/// <returns>A string.</returns>
/// <exception cref="IndexOutOfRangeException">The end of the source string is reached.</exception>
protected string GetString() { protected string GetString() {
int sp = Position; int sp = Position;
do { do {
@@ -100,6 +150,11 @@ namespace Cryville.Common.Pdt {
Position++; Position++;
return Regex.Replace(Source.Substring(sp + 1, Position - sp - 2), @"\\(.)", "$1"); return Regex.Replace(Source.Substring(sp + 1, Position - sp - 2), @"\\(.)", "$1");
} }
/// <summary>
/// Reads an expression.
/// </summary>
/// <returns>An expression.</returns>
/// <exception cref="IndexOutOfRangeException">The end of the source string is reached.</exception>
protected PdtExpression GetExp() { protected PdtExpression GetExp() {
var ins = new LinkedList<PdtInstruction>(); var ins = new LinkedList<PdtInstruction>();
int _; int _;
@@ -112,23 +167,27 @@ namespace Cryville.Common.Pdt {
/// Creates an instance of the <see cref="PdtInterpreter" /> class. /// Creates an instance of the <see cref="PdtInterpreter" /> class.
/// </summary> /// </summary>
/// <param name="src">The source string.</param> /// <param name="src">The source string.</param>
/// <param name="type">The destination type.</param>
/// <param name="binder">The binder. May be <c>null</c>.</param> /// <param name="binder">The binder. May be <c>null</c>.</param>
public PdtInterpreter(string src, Type type, Binder binder) { public PdtInterpreter(string src, Binder binder) {
Source = src; Source = src;
_type = type;
_binder = binder; _binder = binder;
if (_binder == null) }
_binder = BinderAttribute.CreateBinderOfType(_type); int[] m_formatVersion;
public int[] GetFormatVersion() {
if (m_formatVersion == null) InterpretDirectives();
return m_formatVersion;
} }
/// <summary> /// <summary>
/// Interprets the source to an object. /// Interprets the source to an object.
/// </summary> /// </summary>
/// <param name="type">The output type.</param>
/// <returns>The interpreted object.</returns> /// <returns>The interpreted object.</returns>
public object Interpret() { public object Interpret(Type type) {
try { try {
InterpretDirectives(); if (m_formatVersion == null) InterpretDirectives();
return InterpretObject(_type); if (_binder == null)
_binder = BinderAttribute.CreateBinderOfType(type);
return InterpretObject(type);
} }
catch (Exception ex) { catch (Exception ex) {
throw new PdtParsingException(this, ex); throw new PdtParsingException(this, ex);
@@ -146,7 +205,10 @@ namespace Cryville.Common.Pdt {
break; break;
case "format": case "format":
ws(); ws();
if (GetNumber() != "1") m_formatVersion = (from i in GetNumber().Split('.') select int.Parse(i)).ToArray();
if (m_formatVersion.Length == 0)
throw new FormatException("Invalid format version");
if (m_formatVersion[0] != 1)
throw new NotSupportedException("Format not supported"); throw new NotSupportedException("Format not supported");
flag = true; flag = true;
break; break;
@@ -167,10 +229,14 @@ namespace Cryville.Common.Pdt {
} }
object InterpretObject(Type type) { object InterpretObject(Type type) {
var result = ReflectionHelper.InvokeEmptyConstructor(type); var result = ReflectionHelper.InvokeEmptyConstructor(type);
bool dictflag = ReflectionHelper.IsGenericDictionary(type); bool dictflag = typeof(IDictionary).IsAssignableFrom(type);
while (true) { while (true) {
try { ws(); } try { ws(); }
catch (IndexOutOfRangeException) { return result; } catch (IndexOutOfRangeException) { return result; }
if (cc == '}') {
GetChar();
return result;
}
object pkey = InterpretKey(type); object pkey = InterpretKey(type);
char c = GetChar(); char c = GetChar();
switch (c) { switch (c) {
@@ -183,19 +249,20 @@ namespace Cryville.Common.Pdt {
((IDictionary)result).Add(key, value); ((IDictionary)result).Add(key, value);
} }
else { else {
MemberInfo prop; MemberInfo prop = null;
bool flag = ReflectionHelper.TryFindMemberWithAttribute<ElementListAttribute>(type, out prop); bool flag = false;
if (!flag && pkey is string) prop = ReflectionHelper.GetMember(type, (string)pkey); if (pkey is string) prop = ReflectionHelper.GetMember(type, (string)pkey);
if (prop == null) flag = ReflectionHelper.TryFindMemberWithAttribute<ElementListAttribute>(type, out prop);
if (prop == null) throw new MissingMemberException(string.Format("The property \"{0}\" is not found", pkey));
Type ptype = ReflectionHelper.GetMemberType(prop); Type ptype = ReflectionHelper.GetMemberType(prop);
if (ReflectionHelper.IsGenericDictionary(ptype)) { if (flag) {
var ktype = ptype.GetGenericArguments()[0]; using (var collection = new PairCollection(ReflectionHelper.GetValue(prop, result))) {
var vtype = ptype.GetGenericArguments()[1]; var ktype = ptype.GetGenericArguments()[0];
if (flag) { var vtype = ptype.GetGenericArguments()[1];
object key = _binder.ChangeType(pkey, ktype, null); object key = _binder.ChangeType(pkey, ktype, null);
object value = InterpretObject(vtype); object value = InterpretObject(vtype);
((IDictionary)ReflectionHelper.GetValue(prop, result)).Add(key, value); collection.Add(key, value);
} }
else ReflectionHelper.SetValue(prop, result, InterpretObject(ptype));
} }
else ReflectionHelper.SetValue(prop, result, InterpretObject(ptype)); else ReflectionHelper.SetValue(prop, result, InterpretObject(ptype));
} }
@@ -211,28 +278,36 @@ namespace Cryville.Common.Pdt {
((IDictionary)result).Add(key, value); ((IDictionary)result).Add(key, value);
} }
else { else {
MemberInfo prop; MemberInfo prop = null;
bool flag = ReflectionHelper.TryFindMemberWithAttribute<PropertyListAttribute>(type, out prop); bool flag = false;
if (!flag && pkey is string) prop = ReflectionHelper.GetMember(type, (string)pkey); if (pkey is string) prop = ReflectionHelper.GetMember(type, (string)pkey);
if (prop == null) flag = ReflectionHelper.TryFindMemberWithAttribute<PropertyListAttribute>(type, out prop);
if (prop == null) throw new MissingMemberException(string.Format("The property \"{0}\" is not found", pkey));
var ptype = ReflectionHelper.GetMemberType(prop); var ptype = ReflectionHelper.GetMemberType(prop);
if (!typeof(IDictionary).IsAssignableFrom(ptype)) { if (flag) {
using (var collection = new PairCollection(ReflectionHelper.GetValue(prop, result))) {
var ktype = ptype.GetGenericArguments()[0];
var vtype = ptype.GetGenericArguments()[1];
object key = _binder.ChangeType(pkey, ktype, null);
object value = _binder.ChangeType(exp, vtype, null);
collection.Add(key, value);
}
}
else {
object value = _binder.ChangeType(exp, ptype, null); object value = _binder.ChangeType(exp, ptype, null);
ReflectionHelper.SetValue(prop, result, value, _binder); ReflectionHelper.SetValue(prop, result, value, _binder);
} }
else {
var ktype = ptype.GetGenericArguments()[0];
var vtype = ptype.GetGenericArguments()[1];
object key = _binder.ChangeType(pkey, ktype, null);
object value = _binder.ChangeType(exp, vtype, null);
((IDictionary)ReflectionHelper.GetValue(prop, result)).Add(key, value);
}
} }
break; break;
case '}': default:
return result; throw new InvalidOperationException("Internal error: Invalid key interpretation");
} }
} }
} }
/// <summary>
/// Interprets a key from the current position.
/// </summary>
/// <returns>The interpreted key.</returns>
protected virtual object InterpretKey(Type type) { protected virtual object InterpretKey(Type type) {
return tokenb(0x1000).Trim(); return tokenb(0x1000).Trim();
} }

Some files were not shown because too many files have changed in this diff Show More