maidraw 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/assets/themes/maimai/chart/common/prism/jp/background.png +0 -0
  2. package/assets/themes/maimai/chart/common/versionless/logo/cn/200.png +0 -0
  3. package/assets/themes/maimai/chart/common/versionless/logo/cn/210.png +0 -0
  4. package/assets/themes/maimai/chart/common/versionless/logo/cn/220.png +0 -0
  5. package/assets/themes/maimai/chart/common/versionless/logo/cn/230.png +0 -0
  6. package/assets/themes/maimai/chart/common/versionless/logo/cn/240.png +0 -0
  7. package/assets/themes/maimai/chart/common/versionless/logo/intl/210.png +0 -0
  8. package/assets/themes/maimai/chart/common/versionless/logo/intl/215.png +0 -0
  9. package/assets/themes/maimai/chart/common/versionless/logo/jp/100.png +0 -0
  10. package/assets/themes/maimai/chart/common/versionless/logo/jp/110.png +0 -0
  11. package/assets/themes/maimai/chart/common/versionless/logo/jp/120.png +0 -0
  12. package/assets/themes/maimai/chart/common/versionless/logo/jp/130.png +0 -0
  13. package/assets/themes/maimai/chart/common/versionless/logo/jp/140.png +0 -0
  14. package/assets/themes/maimai/chart/common/versionless/logo/jp/150.png +0 -0
  15. package/assets/themes/maimai/chart/common/versionless/logo/jp/160.png +0 -0
  16. package/assets/themes/maimai/chart/common/versionless/logo/jp/170.png +0 -0
  17. package/assets/themes/maimai/chart/common/versionless/logo/jp/180.png +0 -0
  18. package/assets/themes/maimai/chart/common/versionless/logo/jp/185.png +0 -0
  19. package/assets/themes/maimai/chart/common/versionless/logo/jp/190.png +0 -0
  20. package/assets/themes/maimai/chart/common/versionless/logo/jp/195.png +0 -0
  21. package/assets/themes/maimai/chart/common/versionless/logo/jp/199.png +0 -0
  22. package/assets/themes/maimai/chart/common/versionless/logo/jp/200.png +0 -0
  23. package/assets/themes/maimai/chart/common/versionless/logo/jp/205.png +0 -0
  24. package/assets/themes/maimai/chart/common/versionless/logo/jp/210.png +0 -0
  25. package/assets/themes/maimai/chart/common/versionless/logo/jp/215.png +0 -0
  26. package/assets/themes/maimai/chart/common/versionless/logo/jp/220.png +0 -0
  27. package/assets/themes/maimai/chart/common/versionless/logo/jp/225.png +0 -0
  28. package/assets/themes/maimai/chart/common/versionless/logo/jp/230.png +0 -0
  29. package/assets/themes/maimai/chart/common/versionless/logo/jp/235.png +0 -0
  30. package/assets/themes/maimai/chart/common/versionless/logo/jp/240.png +0 -0
  31. package/assets/themes/maimai/chart/common/versionless/logo/jp/245.png +0 -0
  32. package/assets/themes/maimai/chart/common/versionless/logo/jp/250.png +0 -0
  33. package/assets/themes/maimai/chart/common/versionless/logo/jp/255.png +0 -0
  34. package/assets/themes/maimai/chart/jp/prism/manifest.json +167 -0
  35. package/dist/chu/bests/best50.d.ts +0 -2
  36. package/dist/chu/bests/best50.js +14 -61
  37. package/dist/chu/bests/best50.js.map +1 -1
  38. package/dist/chu/bests/type.d.ts +1 -1
  39. package/dist/geki/bests/best50.d.ts +0 -2
  40. package/dist/geki/bests/best50.js +14 -67
  41. package/dist/geki/bests/best50.js.map +1 -1
  42. package/dist/geki/bests/type.d.ts +1 -1
  43. package/dist/geki/chart/database.js +0 -1
  44. package/dist/geki/chart/database.js.map +1 -1
  45. package/dist/lib/util.d.ts +32 -0
  46. package/dist/lib/util.js +119 -0
  47. package/dist/lib/util.js.map +1 -0
  48. package/dist/mai/best50/index.d.ts +231 -38
  49. package/dist/mai/best50/index.js +115 -161
  50. package/dist/mai/best50/index.js.map +1 -1
  51. package/dist/mai/chart/index.d.ts +224 -0
  52. package/dist/mai/chart/index.js +1150 -1
  53. package/dist/mai/chart/index.js.map +1 -1
  54. package/dist/mai/{best50/lib → lib}/divingFish/index.d.ts +14 -6
  55. package/dist/mai/{best50/lib → lib}/divingFish/index.js +38 -27
  56. package/dist/mai/lib/divingFish/index.js.map +1 -0
  57. package/dist/mai/{best50/lib → lib}/index.d.ts +12 -4
  58. package/dist/mai/{best50/lib → lib}/index.js +2 -2
  59. package/dist/mai/lib/index.js.map +1 -0
  60. package/dist/mai/{best50/lib → lib}/kamaiTachi/index.d.ts +37 -29
  61. package/dist/mai/{best50/lib → lib}/kamaiTachi/index.js +74 -21
  62. package/dist/mai/lib/kamaiTachi/index.js.map +1 -0
  63. package/dist/mai/{best50/lib → lib}/lxns/index.d.ts +14 -6
  64. package/dist/mai/{best50/lib → lib}/lxns/index.js +37 -27
  65. package/dist/mai/lib/lxns/index.js.map +1 -0
  66. package/dist/mai/type.d.ts +0 -51
  67. package/dist/mai/type.js +1 -35
  68. package/dist/mai/type.js.map +1 -1
  69. package/package.json +5 -2
  70. package/dist/mai/best50/lib/divingFish/index.js.map +0 -1
  71. package/dist/mai/best50/lib/index.js.map +0 -1
  72. package/dist/mai/best50/lib/kamaiTachi/index.js.map +0 -1
  73. package/dist/mai/best50/lib/lxns/index.js.map +0 -1
  74. package/dist/mai/best50/type.d.ts +0 -145
  75. package/dist/mai/best50/type.js +0 -3
  76. package/dist/mai/best50/type.js.map +0 -1
  77. /package/assets/themes/maimai/{cn → best50/cn}/2024Landscape/manifest.json +0 -0
  78. /package/assets/themes/maimai/{cn → best50/cn}/2024Portrait/manifest.json +0 -0
  79. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/a.png +0 -0
  80. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/aa.png +0 -0
  81. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/aaa.png +0 -0
  82. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/b.png +0 -0
  83. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/bb.png +0 -0
  84. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/bbb.png +0 -0
  85. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/c.png +0 -0
  86. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/d.png +0 -0
  87. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/s.png +0 -0
  88. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/sp.png +0 -0
  89. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/ss.png +0 -0
  90. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/ssp.png +0 -0
  91. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/sss.png +0 -0
  92. /package/assets/themes/maimai/{common → best50/common}/buddies/achievement/sssp.png +0 -0
  93. /package/assets/themes/maimai/{common → best50/common}/buddies/cn/logo.png +0 -0
  94. /package/assets/themes/maimai/{common → best50/common}/buddies/jp/background.png +0 -0
  95. /package/assets/themes/maimai/{common → best50/common}/buddies/jp/chara.png +0 -0
  96. /package/assets/themes/maimai/{common → best50/common}/buddies/jp/laundry.png +0 -0
  97. /package/assets/themes/maimai/{common → best50/common}/buddies/jp/logo.png +0 -0
  98. /package/assets/themes/maimai/{common → best50/common}/buddies/plus/chara.png +0 -0
  99. /package/assets/themes/maimai/{common → best50/common}/buddies/plus/laundry.png +0 -0
  100. /package/assets/themes/maimai/{common → best50/common}/buddies/plus/logo.png +0 -0
  101. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/a.png +0 -0
  102. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/aa.png +0 -0
  103. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/aaa.png +0 -0
  104. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/b.png +0 -0
  105. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/c.png +0 -0
  106. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/d.png +0 -0
  107. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/e.png +0 -0
  108. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/f.png +0 -0
  109. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/s.png +0 -0
  110. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/sp.png +0 -0
  111. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/ss.png +0 -0
  112. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/ssp.png +0 -0
  113. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/sss.png +0 -0
  114. /package/assets/themes/maimai/{common → best50/common}/finale/achievement/sssp.png +0 -0
  115. /package/assets/themes/maimai/{common → best50/common}/finale/background.png +0 -0
  116. /package/assets/themes/maimai/{common → best50/common}/finale/background2.png +0 -0
  117. /package/assets/themes/maimai/{common → best50/common}/finale/logo.png +0 -0
  118. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/ap.png +0 -0
  119. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/app.png +0 -0
  120. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/fc.png +0 -0
  121. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/fcp.png +0 -0
  122. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/fdx.png +0 -0
  123. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/fdxp.png +0 -0
  124. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/fsp.png +0 -0
  125. /package/assets/themes/maimai/{common → best50/common}/finale/milestone/sync.png +0 -0
  126. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/a.png +0 -0
  127. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/aa.png +0 -0
  128. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/aaa.png +0 -0
  129. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/b.png +0 -0
  130. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/bb.png +0 -0
  131. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/bbb.png +0 -0
  132. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/c.png +0 -0
  133. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/d.png +0 -0
  134. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/s.png +0 -0
  135. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/sp.png +0 -0
  136. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/ss.png +0 -0
  137. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/ssp.png +0 -0
  138. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/sss.png +0 -0
  139. /package/assets/themes/maimai/{common → best50/common}/prism/achievement/sssp.png +0 -0
  140. /package/assets/themes/maimai/{common → best50/common}/prism/jp/background.png +0 -0
  141. /package/assets/themes/maimai/{common → best50/common}/prism/jp/chara.png +0 -0
  142. /package/assets/themes/maimai/{common → best50/common}/prism/jp/laundry.png +0 -0
  143. /package/assets/themes/maimai/{common → best50/common}/prism/jp/logo.png +0 -0
  144. /package/assets/themes/maimai/{common → best50/common}/prism/plus/chara.png +0 -0
  145. /package/assets/themes/maimai/{common → best50/common}/prism/plus/laundry.png +0 -0
  146. /package/assets/themes/maimai/{common → best50/common}/prism/plus/logo.png +0 -0
  147. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/blue.png +0 -0
  148. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/bronze.png +0 -0
  149. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/gold.png +0 -0
  150. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/green.png +0 -0
  151. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/platinum.png +0 -0
  152. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/purple.png +0 -0
  153. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/rainbow.png +0 -0
  154. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/red.png +0 -0
  155. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/silver.png +0 -0
  156. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/white.png +0 -0
  157. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/cn/yellow.png +0 -0
  158. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/blue.png +0 -0
  159. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/bronze.png +0 -0
  160. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/gold.png +0 -0
  161. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/green.png +0 -0
  162. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/platinum.png +0 -0
  163. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/purple.png +0 -0
  164. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/rainbow.png +0 -0
  165. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/red.png +0 -0
  166. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/silver.png +0 -0
  167. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/white.png +0 -0
  168. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/jp/yellow.png +0 -0
  169. /package/assets/themes/maimai/{common → best50/common}/versionless/dxRating/numberMap.png +0 -0
  170. /package/assets/themes/maimai/{common → best50/common}/versionless/icon.png +0 -0
  171. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/ap.png +0 -0
  172. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/app.png +0 -0
  173. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/fc.png +0 -0
  174. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/fcp.png +0 -0
  175. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/fdx.png +0 -0
  176. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/fdxp.png +0 -0
  177. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/fs.png +0 -0
  178. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/fsp.png +0 -0
  179. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/none.png +0 -0
  180. /package/assets/themes/maimai/{common → best50/common}/versionless/milestone/sync.png +0 -0
  181. /package/assets/themes/maimai/{common → best50/common}/versionless/mode/cn/dx.png +0 -0
  182. /package/assets/themes/maimai/{common → best50/common}/versionless/mode/cn/standard.png +0 -0
  183. /package/assets/themes/maimai/{common → best50/common}/versionless/mode/jp/dx.png +0 -0
  184. /package/assets/themes/maimai/{common → best50/common}/versionless/mode/jp/standard.png +0 -0
  185. /package/assets/themes/maimai/{common → best50/common}/versionless/nameplate.png +0 -0
  186. /package/assets/themes/maimai/{common → best50/common}/versionless/void.png +0 -0
  187. /package/assets/themes/maimai/{jp → best50/jp}/buddiesLandscape/manifest.json +0 -0
  188. /package/assets/themes/maimai/{jp → best50/jp}/buddiesPlusLandscape/manifest.json +0 -0
  189. /package/assets/themes/maimai/{jp → best50/jp}/buddiesPlusPortrait/manifest.json +0 -0
  190. /package/assets/themes/maimai/{jp → best50/jp}/buddiesPortrait/manifest.json +0 -0
  191. /package/assets/themes/maimai/{jp → best50/jp}/finaleLandscape/manifest.json +0 -0
  192. /package/assets/themes/maimai/{jp → best50/jp}/finalePortrait/manifest.json +0 -0
  193. /package/assets/themes/maimai/{jp → best50/jp}/prismLandscape/manifest.json +0 -0
  194. /package/assets/themes/maimai/{jp → best50/jp}/prismPlusLandscape/manifest.json +0 -0
  195. /package/assets/themes/maimai/{jp → best50/jp}/prismPlusPortrait/manifest.json +0 -0
  196. /package/assets/themes/maimai/{jp → best50/jp}/prismPortrait/manifest.json +0 -0
  197. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/a.png +0 -0
  198. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/aa.png +0 -0
  199. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/aaa.png +0 -0
  200. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/b.png +0 -0
  201. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/bb.png +0 -0
  202. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/bbb.png +0 -0
  203. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/c.png +0 -0
  204. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/d.png +0 -0
  205. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/s.png +0 -0
  206. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/sp.png +0 -0
  207. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/ss.png +0 -0
  208. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/ssp.png +0 -0
  209. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/sss.png +0 -0
  210. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/achievement/sssp.png +0 -0
  211. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/background.png +0 -0
  212. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/chara.png +0 -0
  213. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/image/logo.png +0 -0
  214. /package/assets/themes/maimai/{salt → best50/salt}/2026landscape/manifest.json +0 -0
@@ -22,10 +22,1159 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
25
28
  Object.defineProperty(exports, "__esModule", { value: true });
26
29
  exports.Chart = void 0;
30
+ const sharp_1 = __importDefault(require("sharp"));
31
+ const type_1 = require("../type");
27
32
  const database = __importStar(require("./database"));
28
- var Chart;
33
+ const util_1 = require("../../lib/util");
34
+ const canvas_1 = require("canvas");
35
+ const color_1 = __importDefault(require("color"));
36
+ const glob_1 = require("glob");
37
+ const upath_1 = __importDefault(require("upath"));
38
+ const fs_1 = __importDefault(require("fs"));
39
+ const best50_1 = require("../best50");
40
+ const string_template_1 = __importDefault(require("string-template"));
41
+ const lodash_1 = __importDefault(require("lodash"));
42
+ class Chart {
43
+ static DEFAULT_THEME = "jp-prism";
44
+ static primaryTheme = null;
45
+ static get assetsPath() {
46
+ return upath_1.default.join(__dirname, "..", "..", "..", "assets");
47
+ }
48
+ static themes = {};
49
+ static hasTheme(name) {
50
+ return !!this.themes[name];
51
+ }
52
+ static {
53
+ const manifests = (0, glob_1.globSync)(upath_1.default.join(this.assetsPath, "themes", "maimai", "**", "manifest.json"));
54
+ for (const manifestPath of manifests) {
55
+ const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, "utf-8"));
56
+ if (this.validateManifest(manifest, upath_1.default.dirname(manifestPath))) {
57
+ this.themes[manifest.name] = upath_1.default.dirname(manifestPath);
58
+ }
59
+ }
60
+ const loadThemeResult = this.loadTheme(this.DEFAULT_THEME);
61
+ if (!loadThemeResult) {
62
+ console.error("Failed to load default theme.");
63
+ }
64
+ (0, canvas_1.registerFont)(upath_1.default.join(this.assetsPath, "fonts", "gen-jyuu-gothic", "GenJyuuGothic-Bold.ttf"), {
65
+ family: "standard-font-title-jp",
66
+ });
67
+ (0, canvas_1.registerFont)(upath_1.default.join(this.assetsPath, "fonts", "comfortaa", "Comfortaa-Bold.ttf"), {
68
+ family: "standard-font-title-latin",
69
+ weight: "regular",
70
+ });
71
+ (0, canvas_1.registerFont)(upath_1.default.join(this.assetsPath, "fonts", "seurat-db", "FOT-Seurat Pro DB.otf"), {
72
+ family: "standard-font-username",
73
+ weight: "regular",
74
+ });
75
+ }
76
+ static validateManifest(payload, path) {
77
+ function isFileExist(p) {
78
+ if (isString(p))
79
+ return fs_1.default.existsSync(upath_1.default.join(path, p));
80
+ else
81
+ return false;
82
+ }
83
+ function isOneOf(p, ...args) {
84
+ return args.includes(p);
85
+ }
86
+ function isString(p) {
87
+ return typeof p == "string";
88
+ }
89
+ function isNumber(p) {
90
+ return typeof p == "number";
91
+ }
92
+ function isArray(p) {
93
+ return Array.isArray(p);
94
+ }
95
+ function isObject(p) {
96
+ return typeof p == "object";
97
+ }
98
+ function isHexColor(p) {
99
+ if (isString(p))
100
+ return /^(?:#[0-9A-F]{6}|#[0-9A-F]{8})$/i.test(p);
101
+ else
102
+ return false;
103
+ }
104
+ function isUndefined(p) {
105
+ return typeof p == "undefined";
106
+ }
107
+ function isBoolean(p) {
108
+ return typeof p == "boolean";
109
+ }
110
+ if (isString(payload.displayName) &&
111
+ isString(payload.name) &&
112
+ isNumber(payload.width) &&
113
+ isNumber(payload.height) &&
114
+ isObject(payload.sprites) &&
115
+ isObject(payload.sprites.achievement) &&
116
+ isFileExist(payload.sprites.achievement.d) &&
117
+ isFileExist(payload.sprites.achievement.c) &&
118
+ isFileExist(payload.sprites.achievement.b) &&
119
+ isFileExist(payload.sprites.achievement.bb) &&
120
+ isFileExist(payload.sprites.achievement.bbb) &&
121
+ isFileExist(payload.sprites.achievement.a) &&
122
+ isFileExist(payload.sprites.achievement.aa) &&
123
+ isFileExist(payload.sprites.achievement.aaa) &&
124
+ isFileExist(payload.sprites.achievement.s) &&
125
+ isFileExist(payload.sprites.achievement.sp) &&
126
+ isFileExist(payload.sprites.achievement.ss) &&
127
+ isFileExist(payload.sprites.achievement.ssp) &&
128
+ isFileExist(payload.sprites.achievement.sss) &&
129
+ isFileExist(payload.sprites.achievement.sssp) &&
130
+ isObject(payload.sprites.mode) &&
131
+ isFileExist(payload.sprites.mode.standard) &&
132
+ isFileExist(payload.sprites.mode.dx) &&
133
+ isObject(payload.sprites.milestone) &&
134
+ isFileExist(payload.sprites.milestone.ap) &&
135
+ isFileExist(payload.sprites.milestone.app) &&
136
+ isFileExist(payload.sprites.milestone.fc) &&
137
+ isFileExist(payload.sprites.milestone.fcp) &&
138
+ isFileExist(payload.sprites.milestone.fdx) &&
139
+ isFileExist(payload.sprites.milestone.fdxp) &&
140
+ isFileExist(payload.sprites.milestone.fs) &&
141
+ isFileExist(payload.sprites.milestone.fsp) &&
142
+ isFileExist(payload.sprites.milestone.sync) &&
143
+ isFileExist(payload.sprites.milestone.none) &&
144
+ isObject(payload.sprites.dxRating) &&
145
+ isFileExist(payload.sprites.dxRating.white) &&
146
+ isFileExist(payload.sprites.dxRating.blue) &&
147
+ isFileExist(payload.sprites.dxRating.green) &&
148
+ isFileExist(payload.sprites.dxRating.yellow) &&
149
+ isFileExist(payload.sprites.dxRating.red) &&
150
+ isFileExist(payload.sprites.dxRating.purple) &&
151
+ isFileExist(payload.sprites.dxRating.bronze) &&
152
+ isFileExist(payload.sprites.dxRating.silver) &&
153
+ isFileExist(payload.sprites.dxRating.gold) &&
154
+ isFileExist(payload.sprites.dxRating.platinum) &&
155
+ isFileExist(payload.sprites.dxRating.rainbow) &&
156
+ isObject(payload.sprites.profile) &&
157
+ isFileExist(payload.sprites.profile.icon) &&
158
+ isFileExist(payload.sprites.profile.nameplate) &&
159
+ isFileExist(payload.sprites.dxRatingNumberMap) &&
160
+ isObject(payload.sprites.versions) &&
161
+ isObject(payload.sprites.versions.OLD) &&
162
+ isFileExist(payload.sprites.versions.OLD[0]) &&
163
+ isFileExist(payload.sprites.versions.OLD[10]) &&
164
+ isFileExist(payload.sprites.versions.OLD[20]) &&
165
+ isFileExist(payload.sprites.versions.OLD[30]) &&
166
+ isFileExist(payload.sprites.versions.OLD[40]) &&
167
+ isFileExist(payload.sprites.versions.OLD[50]) &&
168
+ isFileExist(payload.sprites.versions.OLD[60]) &&
169
+ isFileExist(payload.sprites.versions.OLD[70]) &&
170
+ isFileExist(payload.sprites.versions.OLD[80]) &&
171
+ isFileExist(payload.sprites.versions.OLD[85]) &&
172
+ isFileExist(payload.sprites.versions.OLD[90]) &&
173
+ isFileExist(payload.sprites.versions.OLD[95]) &&
174
+ isFileExist(payload.sprites.versions.OLD[99]) &&
175
+ isObject(payload.sprites.versions.DX) &&
176
+ isFileExist(payload.sprites.versions.DX[0]) &&
177
+ isFileExist(payload.sprites.versions.DX[5]) &&
178
+ isFileExist(payload.sprites.versions.DX[10]) &&
179
+ isFileExist(payload.sprites.versions.DX[15]) &&
180
+ isFileExist(payload.sprites.versions.DX[20]) &&
181
+ isFileExist(payload.sprites.versions.DX[25]) &&
182
+ isFileExist(payload.sprites.versions.DX[30]) &&
183
+ isFileExist(payload.sprites.versions.DX[35]) &&
184
+ isFileExist(payload.sprites.versions.DX[40]) &&
185
+ isFileExist(payload.sprites.versions.DX[45]) &&
186
+ isFileExist(payload.sprites.versions.DX[50]) &&
187
+ isFileExist(payload.sprites.versions.DX[55]) &&
188
+ isObject(payload.sprites.versions.EX) &&
189
+ isFileExist(payload.sprites.versions.DX[10]) &&
190
+ isFileExist(payload.sprites.versions.DX[15]) &&
191
+ isObject(payload.sprites.versions.CN) &&
192
+ isFileExist(payload.sprites.versions.DX[0]) &&
193
+ isFileExist(payload.sprites.versions.DX[10]) &&
194
+ isFileExist(payload.sprites.versions.DX[20]) &&
195
+ isFileExist(payload.sprites.versions.DX[30]) &&
196
+ isFileExist(payload.sprites.versions.DX[40]) &&
197
+ isArray(payload.elements)) {
198
+ for (const element of payload.elements) {
199
+ if (isNumber(element.x) && isNumber(element.y)) {
200
+ switch (element.type) {
201
+ case "image": {
202
+ if (isNumber(element.width) &&
203
+ isNumber(element.height) &&
204
+ isFileExist(element.path)) {
205
+ continue;
206
+ }
207
+ else
208
+ return false;
209
+ }
210
+ case "chart-grid": {
211
+ if (isNumber(element.width) &&
212
+ isNumber(element.height) &&
213
+ isNumber(element.margin) &&
214
+ isNumber(element.gap) &&
215
+ isObject(element.bubble) &&
216
+ isHexColor(element.bubble.color.basic) &&
217
+ isHexColor(element.bubble.color.advanced) &&
218
+ isHexColor(element.bubble.color.expert) &&
219
+ isHexColor(element.bubble.color.master) &&
220
+ isHexColor(element.bubble.color.remaster) &&
221
+ isHexColor(element.bubble.color.utage) &&
222
+ isObject(element.color) &&
223
+ isHexColor(element.color.card)) {
224
+ continue;
225
+ }
226
+ else
227
+ return false;
228
+ }
229
+ case "profile": {
230
+ if (isNumber(element.height)) {
231
+ continue;
232
+ }
233
+ else
234
+ return false;
235
+ }
236
+ case "detail-info": {
237
+ if (isNumber(element.width) &&
238
+ isNumber(element.height) &&
239
+ isNumber(element.margin) &&
240
+ isObject(element.color) &&
241
+ isHexColor(element.color.card) &&
242
+ (isUndefined(element.font) ||
243
+ isString(element.font))) {
244
+ continue;
245
+ }
246
+ else
247
+ return false;
248
+ }
249
+ case "text": {
250
+ if (isNumber(element.size) &&
251
+ isString(element.content) &&
252
+ (isUndefined(element.width) ||
253
+ isNumber(element.width)) &&
254
+ (isUndefined(element.height) ||
255
+ isNumber(element.height)) &&
256
+ (isUndefined(element.linebreak) ||
257
+ isBoolean(element.linebreak)) &&
258
+ (isUndefined(element.align) ||
259
+ isOneOf(element.align, "left", "center", "right")) &&
260
+ (isUndefined(element.color) ||
261
+ isString(element.color)) &&
262
+ (isUndefined(element.borderColor) ||
263
+ isString(element.borderColor)) &&
264
+ (isUndefined(element.font) ||
265
+ isString(element.font))) {
266
+ continue;
267
+ }
268
+ else
269
+ return false;
270
+ }
271
+ default:
272
+ return false;
273
+ }
274
+ }
275
+ }
276
+ return true;
277
+ }
278
+ else
279
+ return false;
280
+ }
281
+ static loadTheme(path) {
282
+ const theme = this.getTheme(path);
283
+ if (theme) {
284
+ this.primaryTheme = theme;
285
+ return true;
286
+ }
287
+ else
288
+ return false;
289
+ }
290
+ static getTheme(path) {
291
+ if (!fs_1.default.existsSync(upath_1.default.join(path, "manifest.json"))) {
292
+ path = this.themes[path] ?? "";
293
+ }
294
+ else
295
+ path = upath_1.default.join(this.assetsPath, path);
296
+ if (fs_1.default.existsSync(upath_1.default.join(path, "manifest.json"))) {
297
+ const manifest = JSON.parse(fs_1.default.readFileSync(upath_1.default.join(path, "manifest.json"), "utf-8"));
298
+ if (this.validateManifest(manifest, path)) {
299
+ return { manifest, path };
300
+ }
301
+ }
302
+ return null;
303
+ }
304
+ static getThemeFile(path, themePath) {
305
+ if (typeof path == "string" &&
306
+ fs_1.default.existsSync(upath_1.default.join(themePath ?? this.primaryTheme?.path ?? "", path)))
307
+ return fs_1.default.readFileSync(upath_1.default.join(themePath ?? this.primaryTheme?.path, path));
308
+ else
309
+ return Buffer.from([]);
310
+ }
311
+ /* Begin Draw Tools*/
312
+ static async drawImageModule(ctx, theme, element) {
313
+ const img = new canvas_1.Image();
314
+ img.src = this.getThemeFile(element.path, theme.path);
315
+ ctx.drawImage(img, element.x, element.y, element.width, element.height);
316
+ }
317
+ static async drawChartGridCard(ctx, theme, element, chart, difficulty, x, y, width, height, isShort, score) {
318
+ let curColor = "#FFFFFF";
319
+ switch (difficulty) {
320
+ case type_1.EDifficulty.BASIC:
321
+ curColor = element.bubble.color.basic;
322
+ break;
323
+ case type_1.EDifficulty.ADVANCED:
324
+ curColor = element.bubble.color.advanced;
325
+ break;
326
+ case type_1.EDifficulty.EXPERT:
327
+ curColor = element.bubble.color.expert;
328
+ break;
329
+ case type_1.EDifficulty.MASTER:
330
+ curColor = element.bubble.color.master;
331
+ break;
332
+ case type_1.EDifficulty.REMASTER:
333
+ curColor = element.bubble.color.remaster;
334
+ break;
335
+ case type_1.EDifficulty.UTAGE:
336
+ curColor = element.bubble.color.utage;
337
+ break;
338
+ }
339
+ /** Begin Card Draw */
340
+ ctx.save();
341
+ ctx.fillStyle = new color_1.default(curColor).lighten(0.4).hexa();
342
+ ctx.beginPath();
343
+ ctx.roundRect(x, y, width, height, (height * 0.806) / 7);
344
+ ctx.strokeStyle = new color_1.default(curColor).darken(0.3).hexa();
345
+ ctx.lineWidth = element.bubble.margin / 4;
346
+ ctx.stroke();
347
+ ctx.fill();
348
+ ctx.beginPath();
349
+ ctx.roundRect(x, y, width, height, (height * 0.806) / 7);
350
+ ctx.clip();
351
+ /** Begin Main Content Draw */
352
+ {
353
+ ctx.save();
354
+ ctx.beginPath();
355
+ ctx.rect(x, y, width, height);
356
+ ctx.clip();
357
+ ctx.fillStyle = curColor;
358
+ ctx.fill();
359
+ const titleSize = height * (47 / 256);
360
+ /** Begin Difficulty Text & Separation Line Draw */
361
+ {
362
+ let difficultiy = "";
363
+ switch (chart.difficulty) {
364
+ case type_1.EDifficulty.BASIC:
365
+ difficultiy = "BASIC";
366
+ break;
367
+ case type_1.EDifficulty.ADVANCED:
368
+ difficultiy = "ADVANCED";
369
+ break;
370
+ case type_1.EDifficulty.EXPERT:
371
+ difficultiy = "EXPERT";
372
+ break;
373
+ case type_1.EDifficulty.MASTER:
374
+ difficultiy = "MASTER";
375
+ break;
376
+ case type_1.EDifficulty.REMASTER:
377
+ difficultiy = "Re:MASTER";
378
+ break;
379
+ case type_1.EDifficulty.UTAGE:
380
+ difficultiy = "U·TA·GE";
381
+ break;
382
+ }
383
+ const levelTextSize = titleSize * (5 / 8);
384
+ util_1.Util.drawText(ctx, difficultiy, x + element.bubble.margin, y +
385
+ element.bubble.margin +
386
+ titleSize -
387
+ element.bubble.margin * (1 / 4), titleSize, height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
388
+ const difficultyTextWidth = util_1.Util.measureText(ctx, difficultiy, titleSize, Infinity).width;
389
+ util_1.Util.drawText(ctx, `Lv. ${chart.level.toFixed(1)}`, x + element.bubble.margin * 2 + difficultyTextWidth, y +
390
+ element.bubble.margin +
391
+ titleSize -
392
+ element.bubble.margin * (1 / 4), levelTextSize, height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
393
+ ctx.beginPath();
394
+ ctx.roundRect(x + element.bubble.margin, y +
395
+ element.bubble.margin +
396
+ titleSize +
397
+ element.bubble.margin * (1 / 4), height * 2 - element.bubble.margin * 2, height * 0.806 * 0.02, height * 0.806 * 0.16);
398
+ ctx.fillStyle = new color_1.default(curColor).darken(0.3).hex();
399
+ ctx.fill();
400
+ }
401
+ /** End Difficulty Text & Separation Line Draw */
402
+ /** Begin Achievement Rate Draw */
403
+ {
404
+ const scoreSize = height * 0.806 * 0.208;
405
+ util_1.Util.drawText(ctx, score ? `${score.achievement.toFixed(4)}%` : "NO RECORD", x +
406
+ height * 2 -
407
+ element.bubble.margin -
408
+ height * 0.806 * 0.02, y +
409
+ element.bubble.margin +
410
+ titleSize +
411
+ element.bubble.margin * (5 / 8) +
412
+ scoreSize, scoreSize, height * 0.806 * 0.04, Infinity, "right", "white", new color_1.default(curColor).darken(0.3).hexa());
413
+ }
414
+ /** End Achievement Rate Draw */
415
+ /** Begin Achievement Rank Draw */
416
+ {
417
+ if (score) {
418
+ let rankImg;
419
+ switch (score.achievementRank) {
420
+ case best50_1.Best50.EAchievementTypes.D:
421
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.d, theme.path);
422
+ break;
423
+ case best50_1.Best50.EAchievementTypes.C:
424
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.c, theme.path);
425
+ break;
426
+ case best50_1.Best50.EAchievementTypes.B:
427
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.b, theme.path);
428
+ break;
429
+ case best50_1.Best50.EAchievementTypes.BB:
430
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.bb, theme.path);
431
+ break;
432
+ case best50_1.Best50.EAchievementTypes.BBB:
433
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.bbb, theme.path);
434
+ break;
435
+ case best50_1.Best50.EAchievementTypes.A:
436
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.a, theme.path);
437
+ break;
438
+ case best50_1.Best50.EAchievementTypes.AA:
439
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.aa, theme.path);
440
+ break;
441
+ case best50_1.Best50.EAchievementTypes.AAA:
442
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.aaa, theme.path);
443
+ break;
444
+ case best50_1.Best50.EAchievementTypes.S:
445
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.s, theme.path);
446
+ break;
447
+ case best50_1.Best50.EAchievementTypes.SP:
448
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.sp, theme.path);
449
+ break;
450
+ case best50_1.Best50.EAchievementTypes.SS:
451
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.ss, theme.path);
452
+ break;
453
+ case best50_1.Best50.EAchievementTypes.SSP:
454
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.ssp, theme.path);
455
+ break;
456
+ case best50_1.Best50.EAchievementTypes.SSS:
457
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.sss, theme.path);
458
+ break;
459
+ default:
460
+ rankImg = this.getThemeFile(theme.manifest.sprites.achievement.sssp, theme.path);
461
+ }
462
+ const img = new canvas_1.Image();
463
+ img.src = rankImg;
464
+ ctx.drawImage(img, x + element.bubble.margin * (1 / 4), y +
465
+ element.bubble.margin +
466
+ titleSize +
467
+ element.bubble.margin * (1 / 2), height * 0.806 * 0.3 * 2.133, height * 0.806 * 0.3);
468
+ }
469
+ }
470
+ /** End Achievement Rank Draw */
471
+ /** Begin Milestone Draw */
472
+ {
473
+ if (score) {
474
+ let comboImg, syncImg;
475
+ switch (score.combo) {
476
+ case best50_1.Best50.EComboTypes.NONE:
477
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.none, theme.path);
478
+ break;
479
+ case best50_1.Best50.EComboTypes.FULL_COMBO:
480
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.fc, theme.path);
481
+ break;
482
+ case best50_1.Best50.EComboTypes.FULL_COMBO_PLUS:
483
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.fcp, theme.path);
484
+ break;
485
+ case best50_1.Best50.EComboTypes.ALL_PERFECT:
486
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.ap, theme.path);
487
+ break;
488
+ case best50_1.Best50.EComboTypes.ALL_PERFECT_PLUS:
489
+ comboImg = this.getThemeFile(theme.manifest.sprites.milestone.app, theme.path);
490
+ break;
491
+ }
492
+ switch (score.sync) {
493
+ case best50_1.Best50.ESyncTypes.NONE:
494
+ syncImg = this.getThemeFile(theme.manifest.sprites.milestone.none, theme.path);
495
+ break;
496
+ case best50_1.Best50.ESyncTypes.SYNC_PLAY:
497
+ syncImg = this.getThemeFile(theme.manifest.sprites.milestone.sync, theme.path);
498
+ break;
499
+ case best50_1.Best50.ESyncTypes.FULL_SYNC:
500
+ syncImg = this.getThemeFile(theme.manifest.sprites.milestone.fs, theme.path);
501
+ break;
502
+ case best50_1.Best50.ESyncTypes.FULL_SYNC_PLUS:
503
+ syncImg = this.getThemeFile(theme.manifest.sprites.milestone.fsp, theme.path);
504
+ break;
505
+ case best50_1.Best50.ESyncTypes.FULL_SYNC_DX:
506
+ syncImg = this.getThemeFile(theme.manifest.sprites.milestone.fdx, theme.path);
507
+ break;
508
+ case best50_1.Best50.ESyncTypes.FULL_SYNC_DX_PLUS:
509
+ syncImg = this.getThemeFile(theme.manifest.sprites.milestone.fdxp, theme.path);
510
+ break;
511
+ }
512
+ const combo = new canvas_1.Image();
513
+ combo.src = comboImg;
514
+ ctx.drawImage(combo, x + height * 0.806 * (0.32 * 2.133 + 0.06 - 0.1), y +
515
+ element.bubble.margin +
516
+ titleSize +
517
+ element.bubble.margin * (1 / 2), height * 0.806 * 0.32, height * 0.806 * 0.32);
518
+ const sync = new canvas_1.Image();
519
+ sync.src = syncImg;
520
+ ctx.drawImage(sync, x + height * 0.806 * (0.32 * 2.133 + 0.04 + 0.32 - 0.1), y +
521
+ element.bubble.margin +
522
+ titleSize +
523
+ element.bubble.margin * (1 / 2), height * 0.806 * 0.32, height * 0.806 * 0.32);
524
+ }
525
+ }
526
+ /** End Milestone Draw */
527
+ /** Begin Version Draw */
528
+ {
529
+ let versions = [];
530
+ for (let i = 0; i < 3; ++i) {
531
+ versions[i] = {
532
+ region: "DX",
533
+ version: undefined,
534
+ EXAppend: false,
535
+ };
536
+ }
537
+ const VER_DX = chart.difficulty == type_1.EDifficulty.REMASTER
538
+ ? chart.events.find((v) => v.type == "existence" &&
539
+ v.version.region == "DX")?.version
540
+ : chart.addVersion.DX;
541
+ const VER_EX = chart.difficulty == type_1.EDifficulty.REMASTER
542
+ ? chart.events.find((v) => v.type == "existence" &&
543
+ v.version.region == "EX")?.version
544
+ : chart.addVersion.EX;
545
+ const VER_CN = chart.difficulty == type_1.EDifficulty.REMASTER
546
+ ? chart.events.find((v) => v.type == "existence" &&
547
+ v.version.region == "CN")?.version
548
+ : chart.addVersion.CN;
549
+ if (VER_DX) {
550
+ const version = versions[0];
551
+ version.version = VER_DX;
552
+ version.region = "DX";
553
+ }
554
+ if (VER_EX) {
555
+ for (let i = 0; i < 3; ++i) {
556
+ const version = versions[i];
557
+ if (version.version) {
558
+ if (!(version.version.gameVersion.isDX &&
559
+ version.version.gameVersion.minor >= 10 &&
560
+ version.version.gameVersion.minor < 20) &&
561
+ lodash_1.default.isEqual(versions[i].version?.gameVersion, VER_EX.gameVersion)) {
562
+ versions[i].EXAppend = true;
563
+ break;
564
+ }
565
+ }
566
+ else {
567
+ version.version = VER_EX;
568
+ version.region = "EX";
569
+ break;
570
+ }
571
+ }
572
+ }
573
+ if (VER_CN) {
574
+ for (let i = 0; i < 3; ++i) {
575
+ const version = versions[i];
576
+ if (version.version) {
577
+ if (!version.version.gameVersion.isDX &&
578
+ lodash_1.default.isEqual(versions[i].version?.gameVersion, VER_CN.gameVersion)) {
579
+ break;
580
+ }
581
+ }
582
+ else {
583
+ version.version = VER_CN;
584
+ version.region = "CN";
585
+ break;
586
+ }
587
+ }
588
+ }
589
+ let counter = 0;
590
+ for (const version of versions) {
591
+ if (version.version) {
592
+ counter++;
593
+ if (!version.version.gameVersion.isDX)
594
+ version.region = "OLD";
595
+ }
596
+ }
597
+ const versionImageHeight = (height - element.bubble.margin * 2) *
598
+ (isShort ? 1 / 4 : 3 / 8);
599
+ const versionImageWidth = (versionImageHeight / 160) * 332;
600
+ const regionTextSize = versionImageHeight * (5 / 8);
601
+ for (let i = 0, curx = x + width - element.bubble.margin, cury = y + element.bubble.margin; i < versions.length; ++i,
602
+ cury =
603
+ curx - versionImageWidth - regionTextSize <
604
+ x + height * 2
605
+ ? cury + versionImageHeight
606
+ : cury,
607
+ curx =
608
+ curx - versionImageWidth - regionTextSize <
609
+ x + height * 2
610
+ ? x + width - element.bubble.margin
611
+ : curx) {
612
+ const version = versions[i];
613
+ if (version.version) {
614
+ let region = version.region;
615
+ if (version.region == "EX" &&
616
+ !(version.version.gameVersion.minor >= 10 &&
617
+ version.version.gameVersion.minor < 20)) {
618
+ region = "DX";
619
+ }
620
+ const versionImage = this.getThemeFile(theme.manifest.sprites.versions[region][version.version.gameVersion
621
+ .minor]);
622
+ if (versionImage) {
623
+ const versionImg = new canvas_1.Image();
624
+ versionImg.src = versionImage;
625
+ let text;
626
+ switch (version.region) {
627
+ case "DX":
628
+ if (version.EXAppend)
629
+ text = "🇯🇵🌏";
630
+ else
631
+ text = "🇯🇵";
632
+ break;
633
+ case "EX":
634
+ text = "🌏";
635
+ break;
636
+ case "CN":
637
+ text = "🇨🇳";
638
+ break;
639
+ default:
640
+ text = "";
641
+ }
642
+ ctx.drawImage(versionImg, (curx -= versionImageWidth), cury, versionImageWidth, versionImageHeight);
643
+ if (version.region != "OLD") {
644
+ await util_1.Util.drawEmojiOrGlyph(ctx, text, curx, cury +
645
+ regionTextSize +
646
+ (versionImageHeight - regionTextSize) /
647
+ 2, regionTextSize, Infinity, "right");
648
+ curx -=
649
+ util_1.Util.visibleLength(text) * regionTextSize +
650
+ element.bubble.margin;
651
+ }
652
+ }
653
+ }
654
+ }
655
+ /** End Version Draw */
656
+ /** Begin Note Count Draw */
657
+ const noteCountTexts = Object.entries(chart.meta.notes).map(([k, v]) => `${util_1.Util.capitalize(k)}: ${v}`);
658
+ const shortCompensation = isShort
659
+ ? versionImageHeight + element.bubble.margin / 2
660
+ : 0;
661
+ const noteCountTextSize = (height - shortCompensation - element.bubble.margin * 4) /
662
+ noteCountTexts.length;
663
+ let noteCountLength = 0;
664
+ noteCountTexts.forEach((v, i) => {
665
+ util_1.Util.drawText(ctx, v, x + element.bubble.margin * (3 / 2) + height * 2, y +
666
+ shortCompensation +
667
+ element.bubble.margin +
668
+ noteCountTextSize +
669
+ (noteCountTextSize + element.bubble.margin / 2) * i, noteCountTextSize, height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
670
+ const length = util_1.Util.measureText(ctx, v, noteCountTextSize, Infinity).width;
671
+ if (length > noteCountLength)
672
+ noteCountLength = length;
673
+ });
674
+ /** End Note Count Draw */
675
+ /** Begin Internal Level Trend Draw */
676
+ if (!isShort) {
677
+ const CURRENT_DX_MINOR = 55;
678
+ const maxWidth = width -
679
+ height * 2 -
680
+ element.bubble.margin * 4 -
681
+ noteCountLength;
682
+ const maxFitTrendCount = Math.floor(maxWidth / versionImageWidth);
683
+ const trendEvents = chart.events.filter((v) => v.type == "existence" && v.version.region == "DX");
684
+ let actualEvents = lodash_1.default.uniqWith(trendEvents, (a, b) => {
685
+ return lodash_1.default.isEqual(a.data.level, b.data.level);
686
+ });
687
+ if (actualEvents.length == maxFitTrendCount) {
688
+ }
689
+ else if (actualEvents.length > maxFitTrendCount) {
690
+ while (actualEvents.length > maxFitTrendCount)
691
+ actualEvents.shift();
692
+ actualEvents.shift();
693
+ actualEvents.shift();
694
+ actualEvents.unshift(trendEvents[0]);
695
+ actualEvents.push(trendEvents[trendEvents.length - 1]);
696
+ }
697
+ else if (trendEvents.length > maxFitTrendCount) {
698
+ actualEvents = lodash_1.default.filter(actualEvents, (v) => !(lodash_1.default.isEqual(v.version.gameVersion, trendEvents[0].version.gameVersion) ||
699
+ lodash_1.default.isEqual(v.version.gameVersion, trendEvents[trendEvents.length - 1]
700
+ .version.gameVersion)));
701
+ for (let i = trendEvents.length - 2; i > 0 && actualEvents.length < maxFitTrendCount - 2; --i) {
702
+ const event = trendEvents[i];
703
+ if (event)
704
+ actualEvents.push(event);
705
+ actualEvents = lodash_1.default.uniqWith(actualEvents, (a, b) => {
706
+ return lodash_1.default.isEqual(a.version.gameVersion, b.version.gameVersion);
707
+ });
708
+ }
709
+ actualEvents.unshift(trendEvents[0]);
710
+ actualEvents.push(trendEvents[trendEvents.length - 1]);
711
+ actualEvents = lodash_1.default.uniqWith(actualEvents, (a, b) => {
712
+ return lodash_1.default.isEqual(a.version.gameVersion, b.version.gameVersion);
713
+ });
714
+ actualEvents = lodash_1.default.sortBy(actualEvents, (v) => v.version.gameVersion.minor);
715
+ if (trendEvents.length > 1) {
716
+ if (actualEvents.length >= maxFitTrendCount)
717
+ actualEvents.pop();
718
+ actualEvents.push(trendEvents[trendEvents.length - 1]);
719
+ }
720
+ }
721
+ else {
722
+ actualEvents = [...trendEvents];
723
+ }
724
+ const addGap = (maxWidth - actualEvents.length * versionImageWidth) /
725
+ (actualEvents.length - 1);
726
+ for (let i = 0, curx = x +
727
+ height * 2 +
728
+ element.bubble.margin * (5 / 2) +
729
+ noteCountLength, cury = y +
730
+ element.bubble.margin * (3 / 2) +
731
+ versionImageHeight; i < maxFitTrendCount; ++i) {
732
+ const event = actualEvents[i];
733
+ if (!event)
734
+ continue;
735
+ const region = event.version.gameVersion.isDX
736
+ ? "DX"
737
+ : "OLD";
738
+ const versionImage = this.getThemeFile(theme.manifest.sprites.versions[region][event.version.gameVersion
739
+ .minor]);
740
+ if (versionImage) {
741
+ const versionImg = new canvas_1.Image();
742
+ versionImg.src = versionImage;
743
+ ctx.drawImage(versionImg, curx, cury, versionImageWidth, versionImageHeight);
744
+ let symbol = "";
745
+ if (i != 0) {
746
+ const lastEvent = actualEvents[i - 1];
747
+ if (lastEvent) {
748
+ if (lastEvent.data.level < event.data.level)
749
+ symbol = "↑";
750
+ else if (lastEvent.data.level > event.data.level)
751
+ symbol = "↓";
752
+ else if (lastEvent.data.level == event.data.level)
753
+ symbol = "→";
754
+ }
755
+ }
756
+ util_1.Util.drawText(ctx, `${symbol}${event.data.level.toFixed(1)}`, curx + versionImageWidth / 2, cury + versionImageHeight + noteCountTextSize, noteCountTextSize, height * 0.806 * 0.04, Infinity, "center", "white", new color_1.default(curColor).darken(0.3).hexa());
757
+ curx += versionImageWidth + addGap;
758
+ }
759
+ }
760
+ }
761
+ }
762
+ /** End Internal Level Trend Draw */
763
+ ctx.restore();
764
+ }
765
+ /** End Main Content Draw */
766
+ ctx.fillStyle = new color_1.default(curColor).lighten(0.4).hexa();
767
+ ctx.beginPath();
768
+ ctx.roundRect(x, y + height * 0.742, height * 2, height * (1 - 0.742), [
769
+ 0,
770
+ (height * 0.806) / 7,
771
+ 0,
772
+ (height * 0.806) / 7,
773
+ ]);
774
+ ctx.fill();
775
+ /** Begin Difficulty & DX Rating Draw */
776
+ {
777
+ util_1.Util.drawText(ctx, chart.designer.name || "-", x + element.bubble.margin, y + height * (0.806 + (1 - 0.806) / 2), height * 0.806 * 0.128, height * 0.806 * 0.04, Infinity, "left", "white", new color_1.default(curColor).darken(0.3).hexa());
778
+ util_1.Util.drawText(ctx, `${score?.dxScore || "MAX DX SCR"}/${chart.meta.maxDXScore}`, x + height * 2 - element.bubble.margin, y + height * (0.806 + (1 - 0.806) / 2), height * 0.806 * 0.128, height * 0.806 * 0.04, Infinity, "right", "white", new color_1.default(curColor).darken(0.3).hexa());
779
+ }
780
+ /** End Difficulty & DX Rating Draw */
781
+ ctx.restore();
782
+ /** End Card Draw */
783
+ }
784
+ static async drawDetailInfoModule(ctx, theme, element, chartId) {
785
+ const jacketMargin = element.margin;
786
+ const textMargin = element.margin;
787
+ const chart = Chart.Database.getLocalChart(chartId, type_1.EDifficulty.BASIC);
788
+ const jacket = await Chart.Database.fecthJacket(chartId);
789
+ /* Begin Background Draw */
790
+ ctx.beginPath();
791
+ ctx.roundRect(element.x, element.y, element.width, element.height, Math.min(theme.manifest.width, theme.manifest.height) * (3 / 128));
792
+ ctx.fillStyle = element.color.card;
793
+ ctx.strokeStyle = new color_1.default(element.color.card).darken(0.6).hex();
794
+ ctx.lineWidth =
795
+ Math.min(theme.manifest.width, theme.manifest.height) * (3 / 512);
796
+ ctx.stroke();
797
+ ctx.fill();
798
+ /* End Background Draw */
799
+ /* Begin jacket draw */
800
+ if (jacket) {
801
+ const jacketImage = new canvas_1.Image();
802
+ const roundRadius = Math.min(theme.manifest.width, theme.manifest.height) *
803
+ (3 / 128);
804
+ jacketImage.src = jacket;
805
+ ctx.beginPath();
806
+ ctx.roundRect(element.x + jacketMargin, element.y + jacketMargin, element.width - jacketMargin * 2, element.width - jacketMargin * 2, [roundRadius, roundRadius, 0, 0]);
807
+ ctx.save();
808
+ ctx.clip();
809
+ ctx.drawImage(jacketImage, element.x + jacketMargin, element.y + jacketMargin, element.width - jacketMargin * 2, element.width - jacketMargin * 2);
810
+ ctx.restore();
811
+ }
812
+ /* End jacket draw */
813
+ /* Begin Detail Draw */
814
+ if (chart) {
815
+ const textSizeTitle = element.width * (1 / 16);
816
+ const textSizeSecondary = element.width * (1 / 24);
817
+ const { actualBoundingBoxAscent: ascent, actualBoundingBoxDescent: decent, } = util_1.Util.measureText(ctx, chart.name, textSizeTitle, Infinity);
818
+ const titleActualHeight = Math.abs(ascent - decent);
819
+ const mode = new canvas_1.Image();
820
+ const chartModeBadgeImg = this.getThemeFile(chart.id > 10000
821
+ ? theme.manifest.sprites.mode.dx
822
+ : theme.manifest.sprites.mode.standard, theme.path);
823
+ const { width: modeWidth, height: modeHeight } = await (0, sharp_1.default)(chartModeBadgeImg).metadata();
824
+ const aspectRatio = (modeWidth ?? 0) / (modeHeight ?? 1) || 3;
825
+ const textLineWidth = element.width * (7 / 512);
826
+ const textColor = new color_1.default(element.color.card).darken(0.5).hex();
827
+ const textTitleMaxWidth = element.width - textMargin * 2 - textSizeTitle * aspectRatio;
828
+ const titleMetrics = util_1.Util.measureText(ctx, chart.name, textSizeTitle, textTitleMaxWidth);
829
+ util_1.Util.drawText(ctx, chart.name, element.x + textMargin, element.y +
830
+ jacketMargin +
831
+ textMargin * (1 / 2) +
832
+ (element.width - jacketMargin * 2) +
833
+ textSizeTitle, textSizeTitle, textLineWidth, textTitleMaxWidth, "left", "white", textColor);
834
+ util_1.Util.drawText(ctx, chart.artist, element.x + textMargin, element.y +
835
+ jacketMargin +
836
+ textMargin * (1 / 2) +
837
+ (element.width - jacketMargin * 2) +
838
+ textSizeTitle * 2, textSizeSecondary, textLineWidth, element.width - textMargin * 2, "left", "white", textColor);
839
+ util_1.Util.drawText(ctx, `BPM: ${chart.bpm}`, element.x + textMargin, element.y +
840
+ jacketMargin +
841
+ textMargin * (1 / 2) +
842
+ (element.width - jacketMargin * 2) +
843
+ textSizeTitle * 3, textSizeSecondary, textLineWidth, element.width - textMargin * 2, "left", "white", textColor);
844
+ const DX_LATEST = 55, EX_LATEST = 50, CN_LATEST = 40;
845
+ const EVENT_DX = chart.events
846
+ .filter((v) => v.version.region == "DX" &&
847
+ v.version.gameVersion.minor >= DX_LATEST)
848
+ .map((v) => v.type);
849
+ const EVENT_EX = chart.events
850
+ .filter((v) => v.version.region == "EX" &&
851
+ v.version.gameVersion.minor >= EX_LATEST)
852
+ .map((v) => v.type);
853
+ const EVENT_CN = chart.events
854
+ .filter((v) => v.version.region == "CN" &&
855
+ v.version.gameVersion.minor >= CN_LATEST)
856
+ .map((v) => v.type);
857
+ const EXIST_DX = EVENT_DX.includes("existence") && !EVENT_DX.includes("removal")
858
+ ? "🇯🇵"
859
+ : "";
860
+ const EXIST_EX = EVENT_EX.includes("existence") && !EVENT_EX.includes("removal")
861
+ ? "🌏"
862
+ : "";
863
+ const EXIST_CN = EVENT_CN.includes("existence") && !EVENT_CN.includes("removal")
864
+ ? "🇨🇳"
865
+ : "";
866
+ const title = [];
867
+ if (EXIST_DX)
868
+ title.push(EXIST_DX);
869
+ if (EXIST_EX)
870
+ title.push(EXIST_EX);
871
+ if (EXIST_CN)
872
+ title.push(EXIST_CN);
873
+ await util_1.Util.drawEmojiOrGlyph(ctx, title.join(" "), element.x + element.width - textMargin, element.y +
874
+ jacketMargin +
875
+ textMargin * (1 / 2) +
876
+ (element.width - jacketMargin * 2) +
877
+ textSizeTitle * 3, textSizeSecondary * (9 / 8), element.width - textMargin * 2, "right", "white", textColor);
878
+ /** Begin Chart Mode Draw */
879
+ {
880
+ mode.src = chartModeBadgeImg;
881
+ ctx.drawImage(mode, element.x + textMargin * (3 / 2) + titleMetrics.width, element.y +
882
+ jacketMargin +
883
+ textMargin * (1 / 2) +
884
+ (element.width - jacketMargin * 2) -
885
+ titleActualHeight / 2 +
886
+ textSizeTitle / 2, textSizeTitle * aspectRatio, textSizeTitle);
887
+ }
888
+ /** End Chart Mode Draw */
889
+ }
890
+ /* End Detail Draw */
891
+ }
892
+ static async drawChartGridModule(ctx, theme, element, chartId, scores) {
893
+ /* Begin Background Draw */
894
+ ctx.roundRect(element.x, element.y, element.width, element.height, Math.min(theme.manifest.width, theme.manifest.height) * (3 / 128));
895
+ ctx.fillStyle = element.color.card;
896
+ ctx.strokeStyle = new color_1.default(element.color.card).darken(0.6).hex();
897
+ ctx.lineWidth =
898
+ Math.min(theme.manifest.width, theme.manifest.height) * (3 / 512);
899
+ ctx.stroke();
900
+ ctx.fill();
901
+ /* End Background Draw */
902
+ const difficulties = [];
903
+ for (let i = type_1.EDifficulty.BASIC; i <= type_1.EDifficulty.REMASTER; ++i) {
904
+ const chart = Chart.Database.getLocalChart(chartId, i);
905
+ if (chart)
906
+ difficulties.push(chart);
907
+ }
908
+ const cardWidth = element.width - element.margin * 2;
909
+ const cardHeight = (element.height - element.margin * 2 - element.gap * 3) / 4;
910
+ for (let y = element.y + element.margin, i = 0; i < difficulties.length; ++i, y += cardHeight + element.gap) {
911
+ const chart = difficulties[i];
912
+ if (chart)
913
+ if (difficulties.length > 4 && i == 0) {
914
+ await this.drawChartGridCard(ctx, theme, element, chart, i, element.x + element.margin, y, (cardWidth - element.margin) / 2, cardHeight, true, scores[i]);
915
+ i++;
916
+ const chartA = difficulties[i];
917
+ if (chartA)
918
+ await this.drawChartGridCard(ctx, theme, element, chartA, i, element.x +
919
+ element.margin +
920
+ (cardWidth + element.margin) / 2, y, (cardWidth - element.margin) / 2, cardHeight, true, scores[i]);
921
+ }
922
+ else {
923
+ await this.drawChartGridCard(ctx, theme, element, chart, i, element.x + element.margin, y, cardWidth, cardHeight, false, scores[i]);
924
+ }
925
+ }
926
+ }
927
+ static async drawProfileModule(ctx, theme, element, username, rating, profilePicture) {
928
+ const nameplate = new canvas_1.Image();
929
+ nameplate.src = this.getThemeFile(theme.manifest.sprites.profile.nameplate, theme.path);
930
+ ctx.drawImage(nameplate, element.x, element.y, element.height * 6.207, element.height);
931
+ /* Begin Profile Picture Draw */
932
+ {
933
+ ctx.save();
934
+ ctx.beginPath();
935
+ ctx.roundRect(element.x + element.height * 0.064, element.y + element.height * 0.064, element.height * 0.872, element.height * 0.872, (element.height * 0.872) / 16);
936
+ ctx.clip();
937
+ ctx.fillStyle = "white";
938
+ ctx.fill();
939
+ const icon = new canvas_1.Image();
940
+ try {
941
+ (0, sharp_1.default)(profilePicture);
942
+ }
943
+ catch {
944
+ // Unknown profile picture binary
945
+ profilePicture = undefined;
946
+ }
947
+ const pfp = profilePicture ||
948
+ this.getThemeFile(theme.manifest.sprites.profile.icon, theme.path);
949
+ const { dominant } = await (0, sharp_1.default)(pfp).stats();
950
+ icon.src = await (0, sharp_1.default)(pfp).png().toBuffer();
951
+ const cropSize = Math.min(icon.width, icon.height);
952
+ ctx.drawImage(icon, (icon.width - cropSize) / 2, (icon.height - cropSize) / 2, cropSize, cropSize, element.x + element.height * 0.064, element.y + element.height * 0.064, element.height * 0.872, element.height * 0.872);
953
+ if (profilePicture) {
954
+ ctx.beginPath();
955
+ ctx.roundRect(element.x + element.height * 0.064, element.y + element.height * 0.064, element.height * 0.872, element.height * 0.872, (element.height * 0.872) / 16);
956
+ ctx.strokeStyle = color_1.default.rgb(dominant).darken(0.3).hex();
957
+ ctx.lineWidth = element.height / 30;
958
+ ctx.stroke();
959
+ }
960
+ ctx.restore();
961
+ }
962
+ /* End Profile Picture Draw */
963
+ /* Begin DX Rating Draw */
964
+ {
965
+ const dxRating = new canvas_1.Image();
966
+ let dxRatingImg;
967
+ switch (true) {
968
+ case rating >= 15000: {
969
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.rainbow, theme.path);
970
+ break;
971
+ }
972
+ case rating >= 14500: {
973
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.platinum, theme.path);
974
+ break;
975
+ }
976
+ case rating >= 14000: {
977
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.gold, theme.path);
978
+ break;
979
+ }
980
+ case rating >= 13000: {
981
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.silver, theme.path);
982
+ break;
983
+ }
984
+ case rating >= 12000: {
985
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.bronze, theme.path);
986
+ break;
987
+ }
988
+ case rating >= 10000: {
989
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.purple, theme.path);
990
+ break;
991
+ }
992
+ case rating >= 8000: {
993
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.red, theme.path);
994
+ break;
995
+ }
996
+ case rating >= 6000: {
997
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.yellow, theme.path);
998
+ break;
999
+ }
1000
+ case rating >= 4000: {
1001
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.green, theme.path);
1002
+ break;
1003
+ }
1004
+ case rating >= 2000: {
1005
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.blue, theme.path);
1006
+ break;
1007
+ }
1008
+ default: {
1009
+ dxRatingImg = this.getThemeFile(theme.manifest.sprites.dxRating.white, theme.path);
1010
+ break;
1011
+ }
1012
+ }
1013
+ dxRating.src = dxRatingImg;
1014
+ ctx.drawImage(dxRating, element.x + element.height, element.y + element.height * 0.064, (element.height / 3) * 5.108, element.height / 3);
1015
+ }
1016
+ /* End DX Rating Draw */
1017
+ /* Begin Username Draw */
1018
+ {
1019
+ ctx.beginPath();
1020
+ ctx.roundRect(element.x + element.height * (1 + 1 / 32), element.y + element.height * (0.064 + 0.333 + 1 / 32), ((element.height / 3) * 5.108 * 6) / 5, (element.height * 7) / 24, element.height / 20);
1021
+ ctx.fillStyle = "white";
1022
+ ctx.strokeStyle = color_1.default.rgb(180, 180, 180).hex();
1023
+ ctx.lineWidth = element.height / 32;
1024
+ ctx.stroke();
1025
+ ctx.fill();
1026
+ const ratingImgBuffer = await this.getRatingNumber(rating, theme);
1027
+ if (ratingImgBuffer) {
1028
+ const { width, height } = await (0, sharp_1.default)(ratingImgBuffer).metadata();
1029
+ if (width && height) {
1030
+ const aspectRatio = width / height;
1031
+ const image = new canvas_1.Image();
1032
+ image.src = ratingImgBuffer;
1033
+ const drawHeight = (element.height * 7) / 32;
1034
+ ctx.drawImage(image, element.x + element.height * 1.785, element.y + element.height * 0.12, drawHeight * aspectRatio * 0.8, drawHeight);
1035
+ }
1036
+ }
1037
+ util_1.Util.drawText(ctx, util_1.Util.HalfFullWidthConvert.toFullWidth(username), element.x + element.height * (1 + 1 / 16), element.y + element.height * (0.064 + 0.333 + 1 / 4), (element.height * 1) / 6, 0, ((element.height / 3) * 5.108 * 6) / 5, "left", "black", "black", "standard-font-username");
1038
+ }
1039
+ /* End Username Draw*/
1040
+ }
1041
+ static async drawTextModule(ctx, theme, element, variables = {}) {
1042
+ let naiveLines = (0, string_template_1.default)(element.content, variables).split("\n");
1043
+ let lines = [];
1044
+ if (element.linebreak) {
1045
+ for (let originalContent of naiveLines) {
1046
+ while (originalContent.length) {
1047
+ const line = util_1.Util.findMaxFitString(ctx, originalContent, element.width || Infinity, "");
1048
+ originalContent = originalContent.replace(line, "").trim();
1049
+ lines.push(line.trim());
1050
+ }
1051
+ }
1052
+ }
1053
+ else {
1054
+ for (const originalContent of naiveLines) {
1055
+ lines.push(util_1.Util.findMaxFitString(ctx, originalContent, element.width || Infinity));
1056
+ }
1057
+ }
1058
+ for (let i = 0; i < lines.length; ++i) {
1059
+ const line = lines[i];
1060
+ util_1.Util.drawText(ctx, line, element.x, element.y + i * element.size * 1.3, element.size, element.size / 3.5, element.width || Infinity, element.align, element.color || "#FFFFFF", element.borderColor
1061
+ ? element.borderColor
1062
+ : color_1.default.rgb(element.color || "#FFFFFF")
1063
+ .darken(0.3)
1064
+ .hex(), element.font, element.linebreak ? "" : "...");
1065
+ }
1066
+ }
1067
+ /* End Draw Tools*/
1068
+ static async draw(name, rating, chartId, scores, options) {
1069
+ let currentTheme = this.primaryTheme;
1070
+ if (options?.theme) {
1071
+ const theme = this.getTheme(options.theme);
1072
+ if (theme) {
1073
+ currentTheme = theme;
1074
+ }
1075
+ }
1076
+ if (currentTheme) {
1077
+ await Chart.Database.cacheJackets([chartId]);
1078
+ const canvas = new canvas_1.Canvas(currentTheme.manifest.width * (options?.scale ?? 1), currentTheme.manifest.height * (options?.scale ?? 1));
1079
+ const ctx = canvas.getContext("2d");
1080
+ if (options?.scale)
1081
+ ctx.scale(options.scale, options.scale);
1082
+ ctx.imageSmoothingEnabled = true;
1083
+ for (const element of currentTheme.manifest.elements) {
1084
+ switch (element.type) {
1085
+ case "image": {
1086
+ await this.drawImageModule(ctx, currentTheme, element);
1087
+ break;
1088
+ }
1089
+ case "chart-grid": {
1090
+ await this.drawChartGridModule(ctx, currentTheme, element, chartId, scores);
1091
+ break;
1092
+ }
1093
+ case "detail-info": {
1094
+ await this.drawDetailInfoModule(ctx, currentTheme, element, chartId);
1095
+ break;
1096
+ }
1097
+ case "profile": {
1098
+ await this.drawProfileModule(ctx, currentTheme, element, name, rating, options?.profilePicture);
1099
+ break;
1100
+ }
1101
+ case "text": {
1102
+ await this.drawTextModule(ctx, currentTheme, element, {
1103
+ username: util_1.Util.HalfFullWidthConvert.toFullWidth(name),
1104
+ rating: rating.toFixed(0),
1105
+ });
1106
+ break;
1107
+ }
1108
+ }
1109
+ }
1110
+ return canvas.toBuffer();
1111
+ }
1112
+ else
1113
+ return null;
1114
+ }
1115
+ static async drawWithScoreSource(source, username, chartId, options) {
1116
+ const profile = await source.getPlayerInfo(username);
1117
+ const score = await source.getPlayerScore(username, chartId);
1118
+ if (!profile || !score)
1119
+ return null;
1120
+ return this.draw(profile.name, profile.rating, chartId, [
1121
+ score.basic,
1122
+ score.advanced,
1123
+ score.expert,
1124
+ score.master,
1125
+ score.remaster,
1126
+ score.utage,
1127
+ ], {
1128
+ ...options,
1129
+ profilePicture: options?.profilePicture === null
1130
+ ? undefined
1131
+ : options?.profilePicture ||
1132
+ (await source.getPlayerProfilePicture(username)) ||
1133
+ undefined,
1134
+ });
1135
+ }
1136
+ static async getRatingNumber(num, theme) {
1137
+ async function getRaingDigit(map, digit, unitWidth, unitHeight) {
1138
+ digit = Math.floor(digit % 10);
1139
+ return await (0, sharp_1.default)(map)
1140
+ .extract({
1141
+ left: (digit % 4) * unitWidth,
1142
+ top: Math.floor(digit / 4) * unitHeight,
1143
+ width: unitWidth,
1144
+ height: unitHeight,
1145
+ })
1146
+ .toBuffer();
1147
+ }
1148
+ if (theme.manifest) {
1149
+ const map = this.getThemeFile(theme.manifest.sprites.dxRatingNumberMap, theme.path);
1150
+ const { width, height } = await (0, sharp_1.default)(map).metadata();
1151
+ if (!(width && height))
1152
+ return null;
1153
+ const unitWidth = width / 4, unitHeight = height / 4;
1154
+ let digits = [];
1155
+ while (num > 0) {
1156
+ digits.push(await getRaingDigit(map, num % 10, unitWidth, unitHeight));
1157
+ num = Math.floor(num / 10);
1158
+ }
1159
+ while (digits.length < 5)
1160
+ digits.push(null);
1161
+ digits = digits.reverse();
1162
+ const canvas = new canvas_1.Canvas(unitWidth * digits.length, unitHeight);
1163
+ const ctx = canvas.getContext("2d");
1164
+ for (let i = 0; i < digits.length; ++i) {
1165
+ const curDigit = digits[i];
1166
+ if (!curDigit)
1167
+ continue;
1168
+ const img = new canvas_1.Image();
1169
+ img.src = curDigit;
1170
+ ctx.drawImage(img, unitWidth * i * 0.88, 0);
1171
+ }
1172
+ return canvas.toBuffer();
1173
+ }
1174
+ return null;
1175
+ }
1176
+ }
1177
+ exports.Chart = Chart;
29
1178
  (function (Chart) {
30
1179
  Chart.Database = database.Database;
31
1180
  })(Chart || (exports.Chart = Chart = {}));