210 Commits

1062 changed files with 143486 additions and 4348 deletions

View File

@@ -1,8 +0,0 @@
using System.Reflection;
[assembly: AssemblyCompany("Cryville")]
[assembly: AssemblyCopyright("Copyright © Cryville 2020-2022")]
[assembly: AssemblyDefaultAlias("Cosmo Resona")]
[assembly: AssemblyProduct("Cosmo Resona")]
[assembly: AssemblyTitle("Cosmo Resona")]
[assembly: AssemblyVersion("0.5.0")]

Binary file not shown.

View File

@@ -33,12 +33,12 @@ namespace Cryville.Common {
public override object ChangeType(object value, Type type, CultureInfo culture) {
if (value == null)
return null;
else if (type == value.GetType())
else if (type.IsAssignableFrom(value.GetType()))
return value;
else if (type.IsEnum && value is string) {
return Enum.Parse(type, (string)value);
}
throw new InvalidCastException();
throw new InvalidCastException(string.Format("Cannot cast {0} to {1}", value.GetType(), type));
}
public override void ReorderArgumentArray(ref object[] args, object state) {

View File

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

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: d6a3a023271b82a4985d1bbcc86e6fa8
guid: f0fc34ac257793d4883a9cfcdb6941b9
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,9 +1,8 @@
fileFormatVersion: 2
guid: 925f95cb7c6644a4695b2701d42e1ea2
guid: 520554ce9a8205b4b91e0ff2b8011673
folderAsset: yes
timeCreated: 1606989037
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 9ed0687e714ce1042921c0057f42039f
guid: ae9dab8f520fadc4194032f523ca87c1
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,113 @@
using Cryville.Common.IO;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace Cryville.Common.Font {
public abstract class FontFile : IEnumerable<Typeface> {
public abstract int Count { get; }
public abstract Typeface this[int index] { get; }
protected FileInfo File { get; private set; }
protected BinaryReader Reader { get; private set; }
public FontFile(FileInfo file) {
File = file;
Reader = new BinaryReaderBE(new FileStream(file.FullName, FileMode.Open, FileAccess.Read));
}
public void Close() { Reader.Close(); }
public static FontFile Create(FileInfo file) {
switch (file.Extension) {
case ".ttf": case ".otf": return new FontFileTTF(file);
case ".ttc": case ".otc": return new FontFileTTC(file);
default: return null;
}
}
public Enumerator GetEnumerator() {
return new Enumerator(this);
}
IEnumerator<Typeface> IEnumerable<Typeface>.GetEnumerator() {
return GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
public struct Enumerator : IEnumerator<Typeface> {
readonly FontFile _self;
int _index;
internal Enumerator(FontFile self) {
_self = self;
_index = -1;
}
public Typeface 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.Count) {
_index = -2;
return false;
}
return true;
}
public void Reset() {
_index = -1;
}
}
}
public class FontFileTTF : FontFile {
public override int Count { get { return 1; } }
public override Typeface this[int index] {
get {
if (index != 0) throw new ArgumentOutOfRangeException("index");
try {
return new TypefaceTTF(Reader, File, index);
}
catch (Exception) {
throw new InvalidDataException("Invalid font");
}
}
}
public FontFileTTF(FileInfo file) : base(file) { }
}
public class FontFileTTC : FontFile {
readonly IReadOnlyList<uint> _offsets;
public override int Count { get { return _offsets.Count; } }
public override Typeface this[int index] {
get {
if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException("index");
Reader.BaseStream.Position = _offsets[index];
try {
return new TypefaceTTF(Reader, File, index);
}
catch (Exception) {
throw new InvalidDataException("Invalid font");
}
}
}
public FontFileTTC(FileInfo file) : base(file) {
try {
_offsets = new TTCHeader(Reader, 0).GetItems();
}
catch (Exception) {
throw new InvalidDataException("Invalid font");
}
}
}
}

View File

@@ -1,8 +1,7 @@
fileFormatVersion: 2
guid: 58e01e1e11af164408a19c1086709bd7
timeCreated: 1638411495
licenseType: Free
guid: c9f44ccf8ddd364418b4f4965414ff9c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0

View File

@@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Cryville.Common.Font {
public abstract class FontManager {
public IReadOnlyDictionary<string, IReadOnlyCollection<Typeface>> MapFullNameToTypeface { get; private set; }
public IReadOnlyDictionary<string, IReadOnlyCollection<Typeface>> MapNameToTypefaces { get; private set; }
public FontManager() {
var map1 = new Dictionary<string, List<Typeface>>();
var map2 = new Dictionary<string, List<Typeface>>();
foreach (var f in EnumerateAllTypefaces()) {
List<Typeface> set1;
if (!map1.TryGetValue(f.FullName, out set1)) {
map1.Add(f.FullName, set1 = new List<Typeface>());
}
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.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 static IEnumerable<Typeface> ScanDirectoryForTypefaces(string dir) {
foreach (var f in new DirectoryInfo(dir).EnumerateFiles()) {
FontFile file;
try {
file = FontFile.Create(f);
}
catch (InvalidDataException) {
continue;
}
if (file == null) continue;
var enumerator = file.GetEnumerator();
while (enumerator.MoveNext()) {
Typeface ret;
try {
ret = enumerator.Current;
}
catch (InvalidDataException) {
continue;
}
yield return ret;
}
file.Close();
}
}
}
public class FontManagerAndroid : FontManager {
protected override IEnumerable<Typeface> EnumerateAllTypefaces() {
return ScanDirectoryForTypefaces("/system/fonts");
}
}
public class FontManagerWindows : FontManager {
protected override IEnumerable<Typeface> EnumerateAllTypefaces() {
return ScanDirectoryForTypefaces("C:/Windows/Fonts");
}
}
}

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

@@ -0,0 +1,222 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Cryville.Common.Font {
public abstract class FontTable<T> {
protected UInt32 Offset { get; private set; }
protected BinaryReader Reader { get; private set; }
protected FontTable(BinaryReader reader, UInt32 offset) {
Reader = reader;
Offset = offset;
reader.BaseStream.Position = offset;
}
public abstract IReadOnlyList<T> GetItems();
}
public abstract class FontTable<T, U> : FontTable<T> {
protected FontTable(BinaryReader reader, UInt32 offset) : base(reader, offset) { }
public abstract U GetSubTable(T item);
}
public sealed class TTCHeader : FontTable<UInt32, TableDirectory> {
readonly String ttcTag;
readonly UInt16 majorVersion;
readonly UInt16 minorVersion;
readonly UInt32 numFonts;
readonly List<UInt32> tableDirectoryOffsets = new List<UInt32>();
readonly String dsigTag;
readonly UInt32 dsigLength;
readonly UInt32 dsigOffset;
public TTCHeader(BinaryReader reader, UInt32 offset) : base(reader, offset) {
ttcTag = reader.ReadTag();
if (ttcTag != "ttcf") throw new NotImplementedException();
majorVersion = reader.ReadUInt16();
minorVersion = reader.ReadUInt16();
numFonts = reader.ReadUInt32();
for (UInt32 i = 0; i < numFonts; i++) tableDirectoryOffsets.Add(reader.ReadUInt32());
if (majorVersion == 2) {
dsigTag = reader.ReadTag();
dsigLength = reader.ReadUInt32();
dsigOffset = reader.ReadUInt32();
}
}
public override IReadOnlyList<UInt32> GetItems() {
return tableDirectoryOffsets;
}
public override TableDirectory GetSubTable(UInt32 item) {
var i = (UInt32)item;
return new TableDirectory(Reader, i);
}
}
public sealed class TableDirectory : FontTable<TableRecord, object> {
readonly UInt32 sfntVersion;
readonly UInt16 numTables;
readonly UInt16 searchRange;
readonly UInt16 entrySelector;
readonly UInt16 rangeShift;
readonly List<TableRecord> tableRecords = new List<TableRecord>();
public TableDirectory(BinaryReader reader, UInt32 offset) : base(reader, offset) {
sfntVersion = reader.ReadUInt32();
numTables = reader.ReadUInt16();
searchRange = reader.ReadUInt16();
entrySelector = reader.ReadUInt16();
rangeShift = reader.ReadUInt16();
for (int i = 0; i < numTables; i++)
tableRecords.Add(new TableRecord {
tableTag = reader.ReadTag(),
checksum = reader.ReadUInt32(),
offset = reader.ReadUInt32(),
length = reader.ReadUInt32(),
});
}
public override IReadOnlyList<TableRecord> GetItems() {
return tableRecords;
}
public override object GetSubTable(TableRecord item) {
switch (item.tableTag) {
case "name": return new NameTable(Reader, item.offset);
case "meta": return new MetaTable(Reader, item.offset);
default: throw new NotImplementedException();
}
}
}
public struct TableRecord {
public string tableTag;
public UInt32 checksum;
public UInt32 offset;
public UInt32 length;
}
public sealed class NameTable : FontTable<NameRecord> {
readonly UInt16 version;
readonly UInt16 count;
readonly UInt16 storageOffset;
readonly List<NameRecord> nameRecord = new List<NameRecord>();
readonly UInt16 langTagCount;
readonly List<LangTagRecord> langTagRecord = new List<LangTagRecord>();
public NameTable(BinaryReader reader, UInt32 offset) : base(reader, offset) {
version = reader.ReadUInt16();
count = reader.ReadUInt16();
storageOffset = reader.ReadUInt16();
for (UInt16 i = 0; i < count; i++)
nameRecord.Add(new NameRecord {
platformID = reader.ReadUInt16(),
encodingID = reader.ReadUInt16(),
languageID = reader.ReadUInt16(),
nameID = (NameID)reader.ReadUInt16(),
length = reader.ReadUInt16(),
stringOffset = reader.ReadUInt16(),
});
if (version == 1) {
langTagCount = reader.ReadUInt16();
for (UInt16 i = 0; i < langTagCount; i++)
langTagRecord.Add(new LangTagRecord {
length = reader.ReadUInt16(),
langTagOffset = reader.ReadUInt16(),
});
}
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() {
return nameRecord;
}
}
public struct NameRecord {
public UInt16 platformID;
public UInt16 encodingID;
public UInt16 languageID;
public NameID nameID;
public UInt16 length;
public UInt16 stringOffset;
public String value { get; private set; }
public NameRecord Load(BinaryReader reader, UInt32 origin) {
reader.BaseStream.Position = origin + stringOffset;
Encoding encoding;
switch (platformID) {
case 0: encoding = Encoding.BigEndianUnicode; break;
case 3: encoding = Encoding.BigEndianUnicode; break;
default: return this;
}
value = encoding.GetString(reader.ReadBytes(length));
return this;
}
}
public enum NameID : UInt16 {
CopyrightNotice = 0,
FontFamilyName = 1,
FontSubfamilyName = 2,
UniqueFontIdentifier = 3,
FullFontName = 4,
VersionString = 5,
PostScriptName = 6,
Trademark = 7,
ManufacturerName = 8,
Designer = 9,
Description = 10,
URLVendor = 11,
URLDesigner = 12,
LicenseDescription = 13,
LicenseInfoURL = 14,
TypographicFamilyName = 16,
TypographicSubfamilyName = 17,
CompatibleFull = 18,
SampleText = 19,
PostScriptCIDFindfontName = 20,
WWSFamilyName = 21,
WWSSubfamilyName = 22,
LightBackgroundPalette = 23,
DarkBackgroundPalette = 24,
VariationsPostScriptNamePrefix = 25,
}
public struct LangTagRecord {
public UInt16 length;
public UInt16 langTagOffset;
public String value { get; private set; }
public LangTagRecord Load(BinaryReader reader, UInt32 origin) {
reader.BaseStream.Position = origin + langTagOffset;
value = Encoding.BigEndianUnicode.GetString(reader.ReadBytes(length));
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 string ReadTag(this BinaryReader reader) {
return Encoding.ASCII.GetString(reader.ReadBytes(4));
}
}
}

View File

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

View File

@@ -1,9 +0,0 @@
using System.Globalization;
namespace Cryville.Common.Font {
public static class FontUtil {
/*public static string MatchFontNameWithLang(string lang) {
}*/
}
}

View File

@@ -0,0 +1,31 @@
using System.IO;
using System.Linq;
namespace Cryville.Common.Font {
public abstract class Typeface {
public FileInfo File { get; private set; }
public int IndexInFile { get; private set; }
public string FamilyName { get; protected set; }
public string SubfamilyName { get; protected set; }
public string FullName { get; protected set; }
protected abstract void GetName(BinaryReader reader);
public Typeface(BinaryReader reader, FileInfo file, int index) {
File = file;
IndexInFile = index;
GetName(reader);
}
}
public class TypefaceTTF : Typeface {
public TypefaceTTF(BinaryReader reader, FileInfo file, int index)
: base(reader, file, index) { }
protected override void GetName(BinaryReader reader) {
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());
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();
FullName = (from i in nameTable.GetItems() where i.nameID == NameID.FullFontName && i.value != null select i.value).First();
}
}
}

View File

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

View File

@@ -1,9 +1,8 @@
fileFormatVersion: 2
guid: 71d5ddaf108e0014c98c206ed5135ded
guid: aaa0d8cecafb37b46a6abe372cfefd93
folderAsset: yes
timeCreated: 1605077401
licenseType: Free
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,75 @@
using System;
using System.IO;
using System.Text;
namespace Cryville.Common.IO {
public class BinaryReaderBE : BinaryReader {
readonly byte[] m_buffer = new byte[8];
public BinaryReaderBE(Stream input) : base(input) { }
public BinaryReaderBE(Stream input, Encoding encoding) : base(input, encoding) { }
public BinaryReaderBE(Stream input, Encoding encoding, bool leaveOpen) : base(input, encoding, leaveOpen) { }
public override short ReadInt16() {
FillBuffer(2);
return (short)(m_buffer[1] | (m_buffer[0] << 8));
}
public override ushort ReadUInt16() {
FillBuffer(2);
return (ushort)(m_buffer[1] | (m_buffer[0] << 8));
}
public override int ReadInt32() {
FillBuffer(4);
return m_buffer[3] | (m_buffer[2] << 8) | (m_buffer[1] << 16) | (m_buffer[0] << 24);
}
public override uint ReadUInt32() {
FillBuffer(4);
return (uint)(m_buffer[3] | (m_buffer[2] << 8) | (m_buffer[1] << 16) | (m_buffer[0] << 24));
}
public override long ReadInt64() {
FillBuffer(8);
uint num = (uint)(m_buffer[7] | (m_buffer[6] << 8) | (m_buffer[5] << 16) | (m_buffer[4] << 24));
uint num2 = (uint)(m_buffer[3] | (m_buffer[2] << 8) | (m_buffer[1] << 16) | (m_buffer[0] << 24));
return (long)(((ulong)num2 << 32) | num);
}
public override ulong ReadUInt64() {
FillBuffer(8);
uint num = (uint)(m_buffer[7] | (m_buffer[6] << 8) | (m_buffer[5] << 16) | (m_buffer[4] << 24));
uint num2 = (uint)(m_buffer[3] | (m_buffer[2] << 8) | (m_buffer[1] << 16) | (m_buffer[0] << 24));
return ((ulong)num2 << 32) | num;
}
protected new void FillBuffer(int numBytes) {
if (m_buffer != null && (numBytes < 0 || numBytes > m_buffer.Length)) {
throw new ArgumentOutOfRangeException("numBytes", "Requested numBytes is larger than the internal buffer size");
}
int num = 0, num2;
if (BaseStream == null) {
throw new IOException("File not open");
}
if (numBytes == 1) {
num2 = BaseStream.ReadByte();
if (num2 == -1) {
throw new EndOfStreamException("The end of the stream is reached before numBytes could be read");
}
m_buffer[0] = (byte)num2;
return;
}
do {
num2 = BaseStream.Read(m_buffer, num, numBytes - num);
if (num2 == 0) {
throw new EndOfStreamException("The end of the stream is reached before numBytes could be read");
}
num += num2;
}
while (num < numBytes);
}
}
}

View File

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

View File

@@ -10,10 +10,10 @@ namespace Cryville.Common {
/// </summary>
public static IdentifierManager SharedInstance = new IdentifierManager();
Dictionary<object, int> _idents = new Dictionary<object, int>();
List<object> _ids = new List<object>();
readonly Dictionary<object, int> _idents = new Dictionary<object, int>();
readonly List<object> _ids = new List<object>();
object _syncRoot = new object();
readonly object _syncRoot = new object();
/// <summary>
/// Creates an instance of the <see cref="IdentifierManager" /> class.

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
namespace Cryville.Common {
@@ -40,7 +41,7 @@ namespace Cryville.Common {
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() + "-" + key + ".log") {
Files[key] = new StreamWriter(logPath + "/" + ((int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds).ToString(CultureInfo.InvariantCulture) + "-" + key + ".log") {
AutoFlush = true
};
}

View File

@@ -0,0 +1,78 @@
using System;
namespace Cryville.Common.Math {
/// <summary>
/// Provides a set of <see langword="static" /> methods related to fractions.
/// </summary>
public static class FractionUtils {
/// <summary>
/// Converts a <see cref="double" /> decimal to a fraction.
/// </summary>
/// <param name="value">The decimal.</param>
/// <param name="error">The error.</param>
/// <param name="n">The numerator.</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>
public static void ToFraction(double value, double error, out int n, out int d) {
if (value < 0.0)
throw new ArgumentOutOfRangeException("value", "Must be >= 0.");
if (error <= 0.0 || error >= 1.0)
throw new ArgumentOutOfRangeException("accuracy", "Must be > 0 and < 1.");
int num = (int)System.Math.Floor(value);
value -= num;
if (value < error) { n = num; d = 1; return; }
if (1 - error < value) { n = num + 1; d = 1; return; }
int lower_n = 0;
int lower_d = 1;
int upper_n = 1;
int upper_d = 1;
while (true) {
int middle_n = lower_n + upper_n;
int middle_d = lower_d + upper_d;
if (middle_d * (value + error) < middle_n) {
upper_n = middle_n;
upper_d = middle_d;
}
else if (middle_n < (value - error) * middle_d) {
lower_n = middle_n;
lower_d = middle_d;
}
else {
n = num * middle_d + middle_n;
d = middle_d;
return;
}
}
}
/// <summary>
/// Gets the greatest common divisor (GCD) of two integers.
/// </summary>
/// <param name="n">The first integer.</param>
/// <param name="d">The second integer.</param>
/// <returns>The greatest common divisor (GCD) of the two integers.</returns>
public static int GreatestCommonDivisor(int n, int d) {
while (d != 0) {
int t = d;
d = n % d;
n = t;
}
return n;
}
/// <summary>
/// Simplifies a fraction.
/// </summary>
/// <param name="n">The numerator.</param>
/// <param name="d">The denominator.</param>
public static void Simplify(ref int n, ref int d) {
var gcd = GreatestCommonDivisor(n, d);
n /= gcd;
d /= gcd;
}
}
}

View File

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

View File

@@ -1,9 +1,13 @@
namespace Cryville.Common.Math {
using System;
namespace Cryville.Common.Math {
/// <summary>
/// Represents a square matrix.
/// </summary>
public class SquareMatrix {
readonly float[,] content;
readonly float[,] buffer;
readonly int[] refl;
/// <summary>
/// The size of the matrix.
/// </summary>
@@ -17,6 +21,8 @@
/// <param name="size">The size of the matrix.</param>
public SquareMatrix(int size) {
content = new float[size, size];
buffer = new float[size, size];
refl = new int[size];
Size = size;
}
/// <summary>
@@ -38,38 +44,36 @@
/// <returns>The column vector eliminated.</returns>
public ColumnVector<T> Eliminate<T>(ColumnVector<T> v, IVectorOperator<T> o) {
int s = Size;
float[,] d = (float[,])content.Clone();
int[] refl = new int[s];
for (int i = 0; i < s; i++)
refl[i] = i;
Array.Copy(content, buffer, Size * Size);
for (int i = 0; i < s; i++) refl[i] = i;
for (int r = 0; r < s; r++) {
for (int r0 = r; r0 < s; r0++)
if (d[refl[r0], r] != 0) {
if (buffer[refl[r0], r] != 0) {
refl[r] = r0;
refl[r0] = r;
break;
}
int or = refl[r];
float sf0 = d[or, r];
float sf0 = buffer[or, r];
for (int c0 = r; c0 < s; c0++)
d[or, c0] /= sf0;
buffer[or, c0] /= sf0;
v[or] = o.ScalarMultiply(1 / sf0, v[or]);
for (int r1 = r + 1; r1 < s; r1++) {
int or1 = refl[r1];
float sf1 = d[or1, r];
float sf1 = buffer[or1, r];
for (int c1 = r; c1 < s; c1++)
d[or1, c1] -= d[or, c1] * sf1;
buffer[or1, c1] -= buffer[or, c1] * sf1;
v[or1] = o.Add(v[or1], o.ScalarMultiply(-sf1, v[or]));
}
}
T[] res = new T[s];
ColumnVector<T> res = new ColumnVector<T>(s);
for (int r2 = s - 1; r2 >= 0; r2--) {
var v2 = v[refl[r2]];
for (int c2 = r2 + 1; c2 < s; c2++)
v2 = o.Add(v2, o.ScalarMultiply(-d[refl[r2], c2], res[refl[c2]]));
v2 = o.Add(v2, o.ScalarMultiply(-buffer[refl[r2], c2], res[refl[c2]]));
res[refl[r2]] = v2;
}
return new ColumnVector<T>(res);
return res;
}
/// <summary>
/// Creates a square matrix and fills it with polynomial coefficients.

View File

@@ -77,7 +77,7 @@ namespace Cryville.Common.Network {
encoding = Encoding.UTF8;
payload = encoding.GetBytes(body);
headers.Add("Content-Encoding", encoding.EncodingName);
headers.Add("Content-Length", payload.Length.ToString());
headers.Add("Content-Length", payload.Length.ToString(CultureInfo.InvariantCulture));
}
string request_line = string.Format(
"{0} {1} {2}\r\n", method, uri, Version

View File

@@ -19,9 +19,7 @@ namespace Cryville.Common.Network {
set { throw new NotSupportedException(); }
}
public override void Flush() {
// Do nothing
}
public override void Flush() { }
public abstract byte[] ReadToEnd();

View File

@@ -28,7 +28,7 @@ namespace Cryville.Common.Network {
}
private class InternalTlsClient : DefaultTlsClient {
string _host;
readonly string _host;
public InternalTlsClient(string host, TlsCrypto crypto) : base(crypto) {
_host = host;
@@ -92,9 +92,7 @@ namespace Cryville.Common.Network {
return null;
}
public void NotifyServerCertificate(TlsServerCertificate serverCertificate) {
// Do nothing
}
public void NotifyServerCertificate(TlsServerCertificate serverCertificate) { }
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Cryville.Common.Pdt {
/// <summary>
@@ -104,7 +105,7 @@ namespace Cryville.Common.Pdt {
else if (i is PdtInstruction.PushVariable) {
i.Execute(this);
var frame = _stack[_framecount - 1];
if (frame.Type != PdtInternalType.Undefined) {
if (frame.Type != PdtInternalType.Undefined && frame.Type != PdtInternalType.Error) {
_rip = il.AddAfter(_rip, new PdtInstruction.PushConstant(frame.Type, _mem, frame.Offset, frame.Length));
il.Remove(_rip.Previous);
}
@@ -147,10 +148,10 @@ namespace Cryville.Common.Pdt {
_goffset += value.Length;
}
}
internal unsafe void PushVariable(int name) {
internal unsafe void PushVariable(int name, bool forced) {
fixed (StackFrame* frame = &_stack[_framecount++]) {
byte[] value;
GetVariable(name, out frame->Type, out value);
GetVariable(name, forced, out frame->Type, out value);
frame->Offset = _goffset;
frame->Length = value.Length;
Array.Copy(value, 0, _mem, _goffset, value.Length);
@@ -161,16 +162,17 @@ namespace Cryville.Common.Pdt {
/// Gets a variable of the specified name.
/// </summary>
/// <param name="name">The name of the variable.</param>
/// <param name="forced">Whether to produce an error stack instead of an identifier stack if the variable is not found.</param>
/// <param name="type">The type of the variable.</param>
/// <param name="value">The value of the variable.</param>
protected abstract void GetVariable(int name, out int type, out byte[] value);
protected abstract void GetVariable(int name, bool forced, out int type, out byte[] value);
internal void Operate(PdtOperatorSignature sig) {
PdtOperator op;
try { op = GetOperator(sig); }
catch (Exception) {
catch (Exception ex) {
for (int i = 0; i < sig.ParamCount; i++)
DiscardStack();
throw;
throw new EvaluationFailureException(string.Format("Failed to get operator {0}", sig), ex);
}
Operate(op, sig.ParamCount);
}
@@ -222,4 +224,18 @@ namespace Cryville.Common.Pdt {
_goffset -= _stack[--_framecount].Length;
}
}
/// <summary>
/// The exception that is thrown when the evalution of a <see cref="PdtExpression" /> fails.
/// </summary>
public class EvaluationFailureException : Exception {
/// <inheritdoc />
public EvaluationFailureException() : base("Evaluation failed") { }
/// <inheritdoc />
public EvaluationFailureException(string message) : base(message) { }
/// <inheritdoc />
public EvaluationFailureException(string message, Exception innerException) : base(message, innerException) { }
/// <inheritdoc />
protected EvaluationFailureException(SerializationInfo info, StreamingContext context) : base(info, context) { }
}
}

View File

@@ -51,13 +51,14 @@ namespace Cryville.Common.Pdt {
}
public class PushVariable : PdtInstruction {
public int Name { get; private set; }
public PushVariable(int name) { Name = name; }
public PushVariable(string name) : this(IdentifierManager.SharedInstance.Request(name)) { }
public bool Forced { get; private set; }
public PushVariable(int name, bool forced = false) { Name = name; Forced = forced; }
public PushVariable(string name, bool forced = false) : this(IdentifierManager.SharedInstance.Request(name)) { Forced = forced; }
internal override void Execute(PdtEvaluatorBase etor) {
etor.PushVariable(Name);
etor.PushVariable(Name, Forced);
}
public override string ToString() {
return string.Format("pushv {0}", IdentifierManager.SharedInstance.Retrieve(Name));
return string.Format(Forced ? "pushv ?{0}" : "pushv {0}", IdentifierManager.SharedInstance.Retrieve(Name));
}
}
public class Operate : PdtInstruction {
@@ -90,7 +91,7 @@ namespace Cryville.Common.Pdt {
}
}
}
public partial class PdtInterpreter<T> {
public partial class PdtInterpreter {
readonly static Dictionary<char, int> OP_PRIORITY = new Dictionary<char, int> {
{ '@', 7 },
{ '*', 6 }, { '/', 6 }, { '%', 6 },
@@ -239,7 +240,11 @@ namespace Cryville.Common.Pdt {
if (defs.TryGetValue(buf.Value.Value, out def)) {
foreach (var i in def.Instructions) ins.AddLast(i);
}
else ins.AddLast(new PdtInstruction.PushVariable(buf.Value.Value));
else {
var name = buf.Value.Value;
if (name[0] == '?') ins.AddLast(new PdtInstruction.PushVariable(name.Substring(1), true));
else ins.AddLast(new PdtInstruction.PushVariable(name));
}
buf = null;
TryPushAdjMul(ins, ref flag);
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
@@ -8,8 +9,7 @@ namespace Cryville.Common.Pdt {
/// <summary>
/// Interpreter for Property Definition Tree (PDT) file format.
/// </summary>
/// <typeparam name="T">The object type represented by the PDT.</typeparam>
public partial class PdtInterpreter<T> {
public partial class PdtInterpreter {
/// <summary>
/// The character category map.
/// </summary>
@@ -32,7 +32,7 @@ namespace Cryville.Common.Pdt {
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,
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, 0x0000,
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,
0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000, 0x0000, 0x0000, 0x0030,
0x0000, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030,
@@ -44,34 +44,35 @@ namespace Cryville.Common.Pdt {
/// </summary>
/// <param name="src">The source string.</param>
/// <returns>The interpreted object.</returns>
public static T Interpret(string src) {
return Interpret(src, BinderAttribute.CreateBinderOfType(typeof(T)));
public static T Interpret<T>(string src) {
return Interpret<T>(src, BinderAttribute.CreateBinderOfType(typeof(T)));
}
/// <summary>
/// Interprets a source string to an object of type <typeparamref name="T" /> with a binder.
/// Interprets a source string to an object of type <typeparamref name="T"/> with a binder.
/// </summary>
/// <param name="src">The source string.</param>
/// <param name="binder">The binder.</param>
/// <returns>The interpreted object.</returns>
public static T Interpret(string src, Binder binder) {
return new PdtInterpreter<T>(src, binder).Interpret();
public static T Interpret<T>(string src, Binder binder) {
return (T)new PdtInterpreter(src, typeof(T), binder).Interpret();
}
readonly string _src;
public string Source { get; private set; }
readonly Type _type;
readonly Binder _binder;
protected int Position { get; private set; }
public int Position { get; private set; }
#pragma warning disable IDE1006
protected char cc { get { return _src[Position]; } }
protected char cc { get { return Source[Position]; } }
protected int ct { get { return cm[cc]; } }
protected string tokenb(int flag) { // Token Whitelist
int sp = Position;
while ((ct & flag) == 0) Position++;
return _src.Substring(sp, Position - sp);
return Source.Substring(sp, Position - sp);
}
protected string tokenw(int flag) { // Token Whitelist
int sp = Position;
while ((ct & flag) != 0) Position++;
return _src.Substring(sp, Position - sp);
return Source.Substring(sp, Position - sp);
}
protected void ws() {
while ((ct & 0x0001) != 0) Position++;
@@ -97,7 +98,7 @@ namespace Cryville.Common.Pdt {
Position++;
} while (ct != 0x0100);
Position++;
return Regex.Replace(_src.Substring(sp + 1, Position - sp - 2), @"\\(.)", "$1");
return Regex.Replace(Source.Substring(sp + 1, Position - sp - 2), @"\\(.)", "$1");
}
protected PdtExpression GetExp() {
var ins = new LinkedList<PdtInstruction>();
@@ -108,23 +109,30 @@ namespace Cryville.Common.Pdt {
readonly Dictionary<string, PdtExpression> defs = new Dictionary<string, PdtExpression>();
/// <summary>
/// Creates an instance of the <see cref="PdtInterpreter{T}" /> class.
/// Creates an instance of the <see cref="PdtInterpreter" /> class.
/// </summary>
/// <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>
public PdtInterpreter(string src, Binder binder) {
_src = src;
public PdtInterpreter(string src, Type type, Binder binder) {
Source = src;
_type = type;
_binder = binder;
if (_binder == null)
_binder = BinderAttribute.CreateBinderOfType(typeof(T));
_binder = BinderAttribute.CreateBinderOfType(_type);
}
/// <summary>
/// Interprets the source to an object of type <typeparamref name="T" />.
/// Interprets the source to an object.
/// </summary>
/// <returns>The interpreted object.</returns>
public T Interpret() {
InterpretDirectives();
return (T)InterpretObject(typeof(T));
public object Interpret() {
try {
InterpretDirectives();
return InterpretObject(_type);
}
catch (Exception ex) {
throw new PdtParsingException(this, ex);
}
}
void InterpretDirectives() {
bool flag = false;
@@ -229,4 +237,36 @@ namespace Cryville.Common.Pdt {
return tokenb(0x1000).Trim();
}
}
/// <summary>
/// The exception that is thrown when the interpretation of a PDT fails.
/// </summary>
public class PdtParsingException : Exception {
public PdtParsingException(PdtInterpreter interpreter) : this(interpreter, null) { }
public PdtParsingException(PdtInterpreter interpreter, Exception innerException)
: base(GenerateMessage(interpreter, innerException), innerException) { }
static string GenerateMessage(PdtInterpreter interpreter, Exception innerException) {
string src = interpreter.Source;
int pos = interpreter.Position;
if (pos >= src.Length) return "Failed to interpret the PDT: There are some missing or redundant tokens";
int lineStartPos = src.LastIndexOf('\n', pos) + 1;
int previewStartPos = src.LastIndexOf('\n', pos, 64);
if (previewStartPos == -1) {
previewStartPos = pos - 64;
if (previewStartPos < 0) previewStartPos = 0;
}
else previewStartPos++;
int previewEndPos = src.IndexOf('\n', pos, 64);
if (previewEndPos == -1) {
previewEndPos = pos + 64;
if (previewEndPos > src.Length) previewEndPos = src.Length;
}
return string.Format(
"Failed to interpret the PDT at line {0}, position {1}: {2}\n{3}",
src.Take(interpreter.Position).Count(c => c == '\n') + 1,
pos - lineStartPos + 1,
innerException == null ? "Unknown error" : innerException.Message,
src.Substring(previewStartPos, previewEndPos - previewStartPos)
);
}
}
}

View File

@@ -19,7 +19,8 @@ namespace Cryville.Common.Pdt {
/// <returns>The operand at the specified index.</returns>
/// <exception cref="IndexOutOfRangeException"><paramref name="index" /> is not less than <see cref="LoadedOperandCount" /> or less than 0.</exception>
protected PdtVariableMemory GetOperand(int index) {
if (index >= LoadedOperandCount || index < 0) throw new IndexOutOfRangeException();
if (index >= LoadedOperandCount || index < 0)
throw new ArgumentOutOfRangeException("index");
int i = index + _loadindex;
return _operands[i];
}
@@ -33,11 +34,9 @@ namespace Cryville.Common.Pdt {
_operands = new PdtVariableMemory[pc];
}
PdtEvaluatorBase _etor;
bool _failure = false;
bool _rfreq = true;
internal void Begin(PdtEvaluatorBase etor) {
_etor = etor;
_failure = false;
_loadindex = ParamCount;
}
internal void LoadOperand(PdtVariableMemory mem) {
@@ -49,13 +48,9 @@ namespace Cryville.Common.Pdt {
_rfreq = false;
try { Execute(); } catch (Exception ex) {
if (_rfreq) _etor.DiscardStack();
throw new InvalidOperationException("Evaluation failed", ex);
throw new EvaluationFailureException("Evaluation failed", ex);
}
if (_failure) {
if (_rfreq) _etor.DiscardStack();
throw new InvalidOperationException("Evaluation failed");
}
if (!_rfreq && !noset) throw new InvalidOperationException("Return frame not set");
if (!_rfreq && !noset) throw new EvaluationFailureException("Return frame not set");
}
/// <summary>
/// Executes the operator.
@@ -69,10 +64,7 @@ namespace Cryville.Common.Pdt {
/// <returns>The return frame.</returns>
/// <exception cref="InvalidOperationException">The return frame has already been requested.</exception>
protected PdtVariableMemory GetReturnFrame(int type, int len) {
if (_rfreq) {
_failure = true;
throw new InvalidOperationException("Return frame already requested");
}
if (_rfreq) throw new InvalidOperationException("Return frame requested twice");
_rfreq = true;
return _etor.StackAlloc(type, _prmem, len);
}

View File

@@ -4,7 +4,7 @@ namespace Cryville.Common.Pdt {
/// <summary>
/// Span on the memory of a <see cref="PdtEvaluatorBase" />.
/// </summary>
public unsafe struct PdtVariableMemory {
public unsafe struct PdtVariableMemory : IEquatable<PdtVariableMemory> {
readonly byte* _ptr;
/// <summary>
/// The length of the span.
@@ -46,6 +46,14 @@ namespace Cryville.Common.Pdt {
for (int i = 0; i < length; i++)
dest[destOffset + i] = _ptr[i];
}
/// <inheritdoc />
public bool Equals(PdtVariableMemory obj) {
if (Type != obj.Type || Length != obj.Length) return false;
for (int i = 0; i < Length; i++) {
if (*(_ptr + i) != *(obj._ptr + i)) return false;
}
return true;
}
/// <summary>
/// Gets the memory of the span as a number.
/// </summary>

View File

@@ -1,49 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
namespace Cryville.Common.Plist {
public class PlistConvert {
public static T Deserialize<T>(string file) {
return (T)Deserialize(typeof(T), PlistCS.Plist.readPlist(file));
}
public static object Deserialize(Type type, object obj, Binder binder = null) {
if (binder == null)
binder = BinderAttribute.CreateBinderOfType(type);
if (obj is IList) {
var lobj = (List<object>)obj;
foreach (var i in lobj) {
throw new NotImplementedException(); // TODO
}
}
else if (obj is IDictionary) {
var dobj = (Dictionary<string, object>)obj;
if (typeof(IDictionary).IsAssignableFrom(type)) {
var result = (IDictionary)ReflectionHelper.InvokeEmptyConstructor(type);
var it = type.GetGenericArguments()[1];
foreach (var i in dobj) {
var value = Deserialize(it, i.Value, binder);
result.Add(i.Key, value);
}
return result;
}
else {
var result = ReflectionHelper.InvokeEmptyConstructor(type);
foreach (var i in dobj) {
var imis = type.GetMember(i.Key);
if (imis.Length == 0) continue;
var imi = imis[0];
var it = ReflectionHelper.GetMemberType(imi);
var value = Deserialize(it, i.Value, binder);
ReflectionHelper.SetValue(imi, result, value, binder);
}
return result;
}
}
else return obj;
throw new Exception(); // TODO
}
}
}

View File

@@ -145,7 +145,7 @@ namespace Cryville.Common {
/// <returns>An array containing all the subclasses of the type in the current app domain.</returns>
public static Type[] GetSubclassesOf<T>() where T : class {
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
IEnumerable<Type> r = new List<Type>();
IEnumerable<Type> r = Enumerable.Empty<Type>();
foreach (var a in assemblies)
r = r.Concat(a.GetTypes().Where(
t => t.IsClass

View File

@@ -40,7 +40,25 @@ namespace Cryville.Common {
/// <param name="name">The file name excluding the extension.</param>
/// <returns>The escaped file name.</returns>
public static string EscapeFileName(string name) {
return Regex.Replace(name, @"[\/\\\<\>\:\x22\|\?\*\p{Cc}\.\s]", "_");
var result = Regex.Replace(name, @"[\/\\\<\>\:\x22\|\?\*\p{Cc}]", "_").TrimEnd(' ', '.');
if (result.Length == 0) return "_";
return result;
}
/// <summary>
/// Gets the process path from a command.
/// </summary>
/// <param name="command">The command.</param>
/// <returns>The process path.</returns>
public static string GetProcessPathFromCommand(string command) {
command = command.Trim();
if (command[0] == '"') {
return command.Substring(1, command.IndexOf('"', 1) - 1);
}
else {
int e = command.IndexOf(' ');
if (e == -1) return command;
else return command.Substring(0, e);
}
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
@@ -11,13 +12,13 @@ namespace Cryville.Common.Unity {
Transform dirs;
Transform files;
public Action Callback { private get; set; }
public event Action OnClose;
#if UNITY_ANDROID && !UNITY_EDITOR_WIN
string androidStorage = "";
#endif
string fileName = "";
string fileName = null;
public string FileName {
get { return fileName; }
}
@@ -27,8 +28,16 @@ namespace Cryville.Common.Unity {
set { m_filter = value; }
}
#pragma warning disable IDE0051
public Dictionary<string, string> m_presetPaths = new Dictionary<string, string>();
public Dictionary<string, string> PresetPaths {
get { return m_presetPaths; }
set { m_presetPaths = value; }
}
GameObject prefabButton;
void Start() {
prefabButton = Resources.Load<GameObject>("Common/Button");
panel = gameObject.transform.Find("Panel");
title = panel.Find("Title/Text");
drives = panel.Find("Drives/DrivesInner");
@@ -38,8 +47,8 @@ namespace Cryville.Common.Unity {
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
CurrentDirectory = new DirectoryInfo(Directory.GetCurrentDirectory());
#elif UNITY_ANDROID
using (AndroidJavaClass ajc=new AndroidJavaClass("android.os.Environment"))
using (AndroidJavaObject file=ajc.CallStatic<AndroidJavaObject>("getExternalStorageDirectory")) {
using (AndroidJavaClass ajc = new AndroidJavaClass("android.os.Environment"))
using (AndroidJavaObject file = ajc.CallStatic<AndroidJavaObject>("getExternalStorageDirectory")) {
androidStorage = file.Call<string>("getAbsolutePath");
CurrentDirectory = new DirectoryInfo(androidStorage);
}
@@ -47,9 +56,8 @@ namespace Cryville.Common.Unity {
#error No default directory
#endif
}
UpdateGUI();
UpdateGUI(0);
}
#pragma warning restore IDE0051
public void Show() {
fileName = null;
@@ -57,85 +65,82 @@ namespace Cryville.Common.Unity {
}
public void Close() {
if (Callback != null) Callback.Invoke();
var ev = OnClose;
if (ev != null) ev.Invoke();
gameObject.SetActive(false);
}
public DirectoryInfo CurrentDirectory;
void OnDriveChanged(string s) {
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
CurrentDirectory = new DirectoryInfo(s);
#elif UNITY_ANDROID
switch (s) {
case "?storage":
CurrentDirectory = new DirectoryInfo(androidStorage);
break;
}
#else
#error No change drive logic
#endif
UpdateGUI();
void ChangeDirectory(DirectoryInfo s) {
CurrentDirectory = s;
UpdateGUI(1);
}
void OnDirectoryChanged(string s) {
CurrentDirectory = new DirectoryInfo(CurrentDirectory.FullName + "/" + s);
UpdateGUI();
}
void OnFileChanged(string s) {
void SelectFile(string s) {
fileName = s;
Close();
}
void UpdateGUI() {
void UpdateGUI(int depth) {
title.GetComponent<Text>().text = CurrentDirectory.FullName;
CallHelper.Purge(drives);
if (depth <= 0) {
CallHelper.Purge(drives);
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
var dl = Directory.GetLogicalDrives();
foreach (string d in dl) {
GameObject btn = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Common/Button"));
btn.GetComponentInChildren<Text>().text = d;
var ts = d;
btn.GetComponentInChildren<Button>().onClick.AddListener(() => OnDriveChanged(ts));
btn.transform.SetParent(drives, false);
}
var dl = Directory.GetLogicalDrives();
foreach (string d in dl) {
GameObject btn = Instantiate(prefabButton);
btn.GetComponentInChildren<Text>().text = d;
btn.GetComponentInChildren<Button>().onClick.AddListener(() => ChangeDirectory(new DirectoryInfo(d)));
btn.transform.SetParent(drives, false);
}
#elif UNITY_ANDROID
GameObject sbtn = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Common/Button"));
sbtn.GetComponentInChildren<Text>().text = "Storage";
sbtn.GetComponentInChildren<Button>().onClick.AddListener(() => OnDriveChanged("?storage"));
sbtn.transform.SetParent(drives, false);
GameObject sbtn = GameObject.Instantiate<GameObject>(prefabButton);
sbtn.GetComponentInChildren<Text>().text = "Storage";
sbtn.GetComponentInChildren<Button>().onClick.AddListener(() => ChangeDirectory(new DirectoryInfo(androidStorage)));
sbtn.transform.SetParent(drives, false);
#else
#error No update GUI logic
#endif
foreach (var p in m_presetPaths) {
var d = new DirectoryInfo(p.Value);
if (d.Exists) {
GameObject btn = Instantiate(prefabButton);
btn.GetComponentInChildren<Text>().text = p.Key;
btn.GetComponentInChildren<Button>().onClick.AddListener(() => ChangeDirectory(d));
btn.transform.SetParent(drives, false);
}
}
}
CallHelper.Purge(dirs);
DirectoryInfo[] subdirs = CurrentDirectory.GetDirectories();
GameObject pbtn = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Common/Button"));
GameObject pbtn = Instantiate(prefabButton);
pbtn.GetComponentInChildren<Text>().text = "..";
pbtn.GetComponentInChildren<Button>().onClick.AddListener(() => OnDirectoryChanged(".."));
pbtn.GetComponentInChildren<Button>().onClick.AddListener(() => ChangeDirectory(new DirectoryInfo(Path.Combine(CurrentDirectory.FullName, ".."))));
pbtn.transform.SetParent(dirs, false);
foreach (DirectoryInfo d in subdirs) {
GameObject btn = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Common/Button"));
GameObject btn = Instantiate(prefabButton);
btn.GetComponentInChildren<Text>().text = d.Name;
var ts = d.Name;
btn.GetComponentInChildren<Button>().onClick.AddListener(() => OnDirectoryChanged(ts));
var ts = d;
btn.GetComponentInChildren<Button>().onClick.AddListener(() => ChangeDirectory(ts));
btn.transform.SetParent(dirs, false);
}
CallHelper.Purge(files);
FileInfo[] fl = CurrentDirectory.GetFiles();
foreach (FileInfo d in fl) {
foreach (string ext in m_filter)
if (d.Extension == ext) goto ext_matched;
continue;
ext_matched:
GameObject btn = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Common/Button"));
btn.GetComponentInChildren<Text>().text = d.Name + " / " + (d.Length / 1024.0).ToString("0.0 KiB");
var ts = d.FullName;
btn.GetComponentInChildren<Button>().onClick.AddListener(() => OnFileChanged(ts));
btn.transform.SetParent(files, false);
foreach (string ext in m_filter) {
if (d.Extension == ext) {
GameObject btn = Instantiate(prefabButton);
btn.GetComponentInChildren<Text>().text = d.Name + " / " + (d.Length / 1024.0).ToString("0.0 KiB");
var ts = d.FullName;
btn.GetComponentInChildren<Button>().onClick.AddListener(() => SelectFile(ts));
btn.transform.SetParent(files, false);
break;
}
}
}
}
}

View File

@@ -4,7 +4,19 @@ using UnityEngine;
namespace Cryville.Common.Unity.Input {
public delegate void InputEventDelegate(InputIdentifier id, InputVector vec);
public abstract class InputHandler : IDisposable {
public event InputEventDelegate OnInput;
InputEventDelegate m_onInput;
public event InputEventDelegate OnInput {
add {
if (m_onInput == null) Activate();
m_onInput -= value;
m_onInput += value;
}
remove {
if (m_onInput == null) return;
m_onInput -= value;
if (m_onInput == null) Deactivate();
}
}
~InputHandler() {
Dispose(false);
@@ -14,26 +26,15 @@ namespace Cryville.Common.Unity.Input {
GC.SuppressFinalize(this);
}
public bool Activated { get; private set; }
public void Activate() {
if (Activated) return;
Activated = true;
ActivateImpl();
}
protected abstract void ActivateImpl();
public void Deactivate() {
if (!Activated) return;
Activated = false;
DeactivateImpl();
}
protected abstract void DeactivateImpl();
protected abstract void Activate();
protected abstract void Deactivate();
public abstract void Dispose(bool disposing);
public abstract bool IsNullable(int type);
public abstract byte GetDimension(int type);
public abstract string GetTypeName(int type);
public abstract double GetCurrentTimestamp();
protected void Feed(int type, int id, InputVector vec) {
var del = OnInput;
var del = m_onInput;
if (del != null) del(new InputIdentifier { Source = new InputSource { Handler = this, Type = type }, Id = id }, vec);
}
}

View File

@@ -4,29 +4,22 @@ using System.Reflection;
namespace Cryville.Common.Unity.Input {
public class InputManager {
static readonly List<Type> HandlerRegistries = new List<Type> {
static readonly HashSet<Type> HandlerRegistries = new HashSet<Type> {
typeof(WindowsPointerHandler),
typeof(UnityKeyHandler<UnityKeyboardReceiver>),
typeof(UnityKeyHandler<UnityMouseButtonReceiver>),
typeof(UnityMouseHandler),
typeof(UnityTouchHandler),
};
// TODO set private
public readonly List<InputHandler> _handlers = new List<InputHandler>();
readonly HashSet<InputHandler> _handlers = new HashSet<InputHandler>();
readonly Dictionary<Type, InputHandler> _typemap = new Dictionary<Type, InputHandler>();
readonly Dictionary<InputHandler, double> _timeOrigins = new Dictionary<InputHandler, double>();
readonly object _lock = new object();
readonly Dictionary<InputIdentifier, InputVector> _vectors = new Dictionary<InputIdentifier, InputVector>();
readonly List<InputEvent> _events = new List<InputEvent>();
public InputManager() {
foreach (var t in HandlerRegistries) {
try {
if (!typeof(InputHandler).IsAssignableFrom(t)) continue;
var h = (InputHandler)ReflectionHelper.InvokeEmptyConstructor(t);
_typemap.Add(t, h);
h.OnInput += OnInput;
_handlers.Add(h);
_timeOrigins.Add(h, 0);
Logger.Log("main", 1, "Input", "Initialized {0}", ReflectionHelper.GetSimpleName(t));
}
catch (TargetInvocationException ex) {
@@ -37,49 +30,8 @@ namespace Cryville.Common.Unity.Input {
public InputHandler GetHandler(string name) {
return _typemap[Type.GetType(name)];
}
public void Activate() {
lock (_lock) {
_events.Clear();
}
foreach (var h in _handlers) h.Activate();
}
public void SyncTime(double time) {
foreach (var h in _handlers) {
_timeOrigins[h] = time - h.GetCurrentTimestamp();
}
}
public void Deactivate() {
foreach (var h in _handlers) h.Deactivate();
}
void OnInput(InputIdentifier id, InputVector vec) {
lock (_lock) {
double timeOrigin = _timeOrigins[id.Source.Handler];
vec.Time += timeOrigin;
InputVector vec0;
if (_vectors.TryGetValue(id, out vec0)) {
_events.Add(new InputEvent {
Id = id,
From = vec0,
To = vec,
});
if (vec.IsNull) _vectors.Remove(id);
else _vectors[id] = vec;
}
else {
_events.Add(new InputEvent {
Id = id,
From = new InputVector(vec.Time),
To = vec,
});
_vectors.Add(id, vec);
}
}
}
public void EnumerateEvents(Action<InputEvent> cb) {
lock (_lock) {
foreach (var ev in _events) cb(ev);
_events.Clear();
}
public void EnumerateHandlers(Action<InputHandler> cb) {
foreach (var h in _handlers) cb(h);
}
}

View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
namespace Cryville.Common.Unity.Input {
public class SimpleInputConsumer {
readonly InputManager _manager;
readonly object _lock = new object();
readonly Dictionary<InputIdentifier, InputVector> _vectors = new Dictionary<InputIdentifier, InputVector>();
readonly List<InputEvent> _events = new List<InputEvent>();
public SimpleInputConsumer(InputManager manager) { _manager = manager; }
public void Activate() {
lock (_lock) {
_events.Clear();
}
_manager.EnumerateHandlers(h => h.OnInput += OnInput);
}
public void Deactivate() {
_manager.EnumerateHandlers(h => h.OnInput -= OnInput);
}
protected void OnInput(InputIdentifier id, InputVector vec) {
lock (_lock) {
InputVector vec0;
if (_vectors.TryGetValue(id, out vec0)) {
_events.Add(new InputEvent {
Id = id,
From = vec0,
To = vec,
});
if (vec.IsNull) _vectors.Remove(id);
else _vectors[id] = vec;
}
else {
_events.Add(new InputEvent {
Id = id,
From = new InputVector(vec.Time),
To = vec,
});
_vectors.Add(id, vec);
}
}
}
public void EnumerateEvents(Action<InputEvent> cb) {
lock (_lock) {
foreach (var ev in _events) cb(ev);
_events.Clear();
}
}
}
}

View File

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

View File

@@ -9,19 +9,19 @@ namespace Cryville.Common.Unity.Input {
public UnityKeyHandler() { }
protected override void ActivateImpl() {
protected override void Activate() {
receiver = new GameObject("__keyrecv__");
recvcomp = receiver.AddComponent<T>();
recvcomp.SetCallback(Feed);
}
protected override void DeactivateImpl() {
protected override void Deactivate() {
if (receiver) GameObject.Destroy(receiver);
}
public override void Dispose(bool disposing) {
if (disposing) {
DeactivateImpl();
Deactivate();
}
}
@@ -44,11 +44,14 @@ namespace Cryville.Common.Unity.Input {
public abstract class UnityKeyReceiver<T> : MonoBehaviour where T : UnityKeyReceiver<T> {
protected Action<int, int, InputVector> Callback;
protected readonly List<int> Keys = new List<int>();
protected readonly HashSet<int> Keys = new HashSet<int>();
public void SetCallback(Action<int, int, InputVector> h) {
Callback = h;
}
public abstract string GetKeyName(int type);
void Awake() {
useGUILayout = false;
}
void Update() {
double time = Time.realtimeSinceStartupAsDouble;
foreach (var k in Keys) {

View File

@@ -12,35 +12,35 @@ namespace Cryville.Common.Unity.Input {
}
}
protected override void ActivateImpl() {
protected override void Activate() {
receiver = new GameObject("__mouserecv__");
receiver.AddComponent<UnityMouseReceiver>().SetHandler(this);
}
protected override void DeactivateImpl() {
protected override void Deactivate() {
if (receiver) GameObject.Destroy(receiver);
}
public override void Dispose(bool disposing) {
if (disposing) {
DeactivateImpl();
Deactivate();
}
}
public override bool IsNullable(int type) {
if (type != 0) throw new ArgumentOutOfRangeException(nameof(type));
if (type != 0) throw new ArgumentOutOfRangeException("type");
return false;
}
public override byte GetDimension(int type) {
if (type != 0) throw new ArgumentOutOfRangeException(nameof(type));
if (type != 0) throw new ArgumentOutOfRangeException("type");
return 2;
}
public override string GetTypeName(int type) {
switch (type) {
case 0: return "Mouse Position";
default: throw new ArgumentOutOfRangeException(nameof(type));
default: throw new ArgumentOutOfRangeException("type");
}
}

View File

@@ -12,35 +12,35 @@ namespace Cryville.Common.Unity.Input {
}
}
protected override void ActivateImpl() {
protected override void Activate() {
receiver = new GameObject("__touchrecv__");
receiver.AddComponent<UnityPointerReceiver>().SetHandler(this);
}
protected override void DeactivateImpl() {
protected override void Deactivate() {
if (receiver) GameObject.Destroy(receiver);
}
public override void Dispose(bool disposing) {
if (disposing) {
DeactivateImpl();
Deactivate();
}
}
public override bool IsNullable(int type) {
if (type != 0) throw new ArgumentOutOfRangeException(nameof(type));
if (type != 0) throw new ArgumentOutOfRangeException("type");
return true;
}
public override byte GetDimension(int type) {
if (type != 0) throw new ArgumentOutOfRangeException(nameof(type));
if (type != 0) throw new ArgumentOutOfRangeException("type");
return 2;
}
public override string GetTypeName(int type) {
switch (type) {
case 0: return "Touch";
default: throw new ArgumentOutOfRangeException(nameof(type));
default: throw new ArgumentOutOfRangeException("type");
}
}

View File

@@ -31,6 +31,8 @@ namespace Cryville.Common.Unity.Input {
public WindowsPointerHandler() {
if (Instance != null)
throw new InvalidOperationException("WindowsPointerHandler already created");
if (Environment.OSVersion.Platform != PlatformID.Win32NT)
throw new NotSupportedException("Windows pointer is not supported on this device");
Instance = this;
usePointerMessage = true;
@@ -61,7 +63,7 @@ namespace Cryville.Common.Unity.Input {
public const int TABLET_DISABLE_PENBARRELFEEDBACK = 0x00000010;
public const int TABLET_DISABLE_FLICKS = 0x00010000;
protected override void ActivateImpl() {
protected override void Activate() {
newWndProc = WndProc;
newWndProcPtr = Marshal.GetFunctionPointerForDelegate(newWndProc);
oldWndProcPtr = SetWindowLongPtr(hMainWindow, -4, newWndProcPtr);
@@ -77,7 +79,7 @@ namespace Cryville.Common.Unity.Input {
);
}
protected override void DeactivateImpl() {
protected override void Deactivate() {
if (pressAndHoldAtomID != 0) {
NativeMethods.RemoveProp(hMainWindow, PRESS_AND_HOLD_ATOM);
NativeMethods.GlobalDeleteAtom(pressAndHoldAtomID);
@@ -142,9 +144,11 @@ namespace Cryville.Common.Unity.Input {
}
public override void Dispose(bool disposing) {
DeactivateImpl();
if (usePointerMessage)
NativeMethods.EnableMouseInPointer(false);
if (disposing) {
Deactivate();
if (usePointerMessage)
NativeMethods.EnableMouseInPointer(false);
}
Instance = null;
}

View File

@@ -118,7 +118,7 @@ namespace Cryville.Common.Unity {
fdialog = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Common/FileDialog")).GetComponent<FileDialog>();
fdialog.Filter = filter;
fdialog.CurrentDirectory = ContextPath;
fdialog.Callback = () => OnFileDialogClosed();
fdialog.OnClose += OnFileDialogClosed;
}
editor.SetDescription(PropertyName, desc);
UpdateValue();

View File

@@ -126,8 +126,8 @@ namespace Cryville.Common.Unity.UI {
void Update() {
Vector2 cprectsize = ((RectTransform)transform.parent).rect.size;
if (cprectsize != pprectsize) {
OnFrameUpdate();
pprectsize = cprectsize;
OnFrameUpdate();
}
}
#pragma warning restore IDE0051

View File

@@ -0,0 +1,66 @@
using Cryville.Common.Font;
using System;
using System.Collections.Generic;
using System.Reflection;
using TMPro;
using UnityEngine;
using UnityEngine.TextCore.LowLevel;
using UnityEngine.TextCore.Text;
namespace Cryville.Common.Unity.UI {
[RequireComponent(typeof(TextMeshProUGUI))]
public class TMPAutoFont : MonoBehaviour {
public static Shader DefaultShader;
public static FontMatcher FontMatcher;
public static int MaxFallbackCount = 4;
static FontAsset _font;
TextMeshProUGUI _text;
[SerializeField]
Shader m_shader;
void Awake() {
if (FontMatcher == null) return;
_text = GetComponent<TextMeshProUGUI>();
if (_font == null) {
foreach (var typeface in FontMatcher.MatchScript(null, true)) {
try {
var ifont = CreateFontAsset(typeface.File.FullName, typeface.IndexInFile);
if (m_shader) ifont.material.shader = m_shader;
else if (DefaultShader) ifont.material.shader = DefaultShader;
if (_font == null) {
_font = ifont;
if (MaxFallbackCount <= 0) break;
}
else {
if (_font.fallbackFontAssetTable == null)
_font.fallbackFontAssetTable = new List<FontAsset>();
_font.fallbackFontAssetTable.Add(ifont);
if (_font.fallbackFontAssetTable.Count >= MaxFallbackCount) break;
}
}
catch (Exception) { }
}
}
_text.font = _font;
}
static MethodInfo _methodCreateFontAsset;
static object[] _paramsCreateFontAsset = new object[] { null, null, 90, 9, GlyphRenderMode.SDFAA, 1024, 1024, Type.Missing, Type.Missing };
static FontAsset CreateFontAsset(string path, int index) {
if (_methodCreateFontAsset == null) {
_methodCreateFontAsset = typeof(FontAsset).GetMethod(
"CreateFontAsset", BindingFlags.Static | BindingFlags.NonPublic, null,
new Type[] {
typeof(string), typeof(int), typeof(int), typeof(int),
typeof(GlyphRenderMode), typeof(int), typeof(int),
typeof(AtlasPopulationMode), typeof(bool)
},
null
);
}
_paramsCreateFontAsset[0] = path;
_paramsCreateFontAsset[1] = index;
return (FontAsset)_methodCreateFontAsset.Invoke(null, _paramsCreateFontAsset);
}
}
}

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