From 449b8c665cbf06d7d96b65df0ab6b63c7152a9b2 Mon Sep 17 00:00:00 2001 From: Haoyu Xu Date: Sun, 5 Feb 2023 11:42:29 -0500 Subject: [PATCH] feat(runner): process charword_table.json --- config.yaml | 1 + libs/background.js | 2 +- libs/charword_table.js | 121 +++++++++++++++++++++++++++++++++++++++++ libs/file.js | 4 ++ package.json | 1 + pnpm-lock.yaml | 41 ++++++++++++++ runner.js | 7 ++- 7 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 libs/charword_table.js diff --git a/config.yaml b/config.yaml index 70cacd4..62c0634 100644 --- a/config.yaml +++ b/config.yaml @@ -3,6 +3,7 @@ folder: release: ./release/ background: background directory: _assets + share: _share operators: chen: !include config/chen.yaml dusk: !include config/dusk.yaml diff --git a/libs/background.js b/libs/background.js index 7e5c40a..061d937 100644 --- a/libs/background.js +++ b/libs/background.js @@ -8,7 +8,7 @@ export default class Background { #files constructor() { - this.#backgroundFolder = path.join(__dirname, __config.folder.operator, '_share', __config.folder.background); + this.#backgroundFolder = path.join(__dirname, __config.folder.operator, __config.folder.share, __config.folder.background); this.#extractFolder = path.join(this.#backgroundFolder, 'extracted'); } diff --git a/libs/charword_table.js b/libs/charword_table.js new file mode 100644 index 0000000..612597f --- /dev/null +++ b/libs/charword_table.js @@ -0,0 +1,121 @@ +import fetch from "node-fetch" +import path from "path" +import { exists, writeSync, readdirSync, rm, readSync } from "./file.js" + +// TODO: zh_TW uses an older version of charword_table.json +// const REGIONS = ["zh_CN", "en_US", "ja_JP", "ko_KR", "zh_TW"] +const REGIONS = ["zh_CN", "en_US", "ja_JP", "ko_KR"] +const DEFAULT_REGION = REGIONS[0] +const NICKNAME = { + "zh_CN": "博士", + "en_US": "Doctor", + "ja_JP": "ドクター", + "ko_KR": "박사", + "zh_TW": "博士", +} + +export default class CharwordTable { + #operatorIDs = Object.values(__config.operators).map(operator => { return operator.filename.replace(/^(dyn_illust_)(char_[\d]+)(_[\w]+)(|(_.+))$/g, '$2$3$4') }) + #charwordTable = { + config: { + default_region: DEFAULT_REGION, + regions: REGIONS, + }, + operators: [], + } + #charwordTablePath = path.join(__dirname, __config.folder.operator, __config.folder.share) + + constructor() { + // TODO: use object instead of array + this.#charwordTable.operators = this.#operatorIDs.map(id => { + return { + mainId: id, + alternativeId: id.replace(/^(char_)([\d]+)(_[\w]+)(|(_.+))$/g, '$1$2$3'), + voice: REGIONS.reduce((acc, cur) => ({ ...acc, [cur]: [] }), {}), // use array to store voice lines + info: REGIONS.reduce((acc, cur) => ({ ...acc, [cur]: {} }), {}), // use object to store voice actor info + } + }) + } + + async process() { + for (const region of REGIONS) { + await this.load(region) + } + } + + async load(region) { + const data = await this.#download(region) + + // put voice actor info into charword_table + this.#charwordTable.operators.forEach(element => { + let operatorId = element.mainId + if (typeof data.voiceLangDict[operatorId] === 'undefined') { + operatorId = element.alternativeId + } + // not available in other region + if (typeof data.voiceLangDict[operatorId] === 'undefined') { + console.log(`Voice actor info of ${element.mainId} is not available in ${region}.`) + return + } + Object.values(data.voiceLangDict[operatorId].dict).forEach(item => { + if (typeof element.info[region][item.wordkey] === 'undefined') { + element.info[region][item.wordkey] = [] + } + element.info[region][item.wordkey].push({ + lang: item.voiceLangType, + cvName: item.cvName, + }) + }) + }); + + // put voice lines into charword_table + Object.values(data.charWords).forEach(item => { + const operatorInfo = this.#charwordTable.operators.filter(element => element.info[region][item.wordKey]) + if (operatorInfo.length > 0) { + operatorInfo[0].voice[region].push({ + ref: false, + id: item.voiceId, + title: item.voiceTitle, + text: item.voiceText.replace(/{@nickname}/g, NICKNAME[region]), + wordKey: item.wordKey, + }) + if (operatorInfo.length > 1) { + operatorInfo[1].voice[region].push({ + ref: true, + id: item.voiceId, + wordKey: item.wordKey, + }) + } + } + }) + + writeSync(JSON.stringify(this.#charwordTable), path.join(this.#charwordTablePath, 'charword_table.json')) + } + + async #download(region) { + const historyResponse = await fetch(`https://api.github.com/repos/Kengxxiao/ArknightsGameData/commits?path=${region}/gamedata/excel/charword_table.json`) + const historyData = await historyResponse.json() + const lastCommit = historyData[0] + const lastCommitDate = new Date(lastCommit.commit.committer.date) + const filepath = path.join(this.#charwordTablePath, `charword_table_${region}_${lastCommitDate.getTime()}.json`) + console.log(`Last commit date: ${lastCommitDate.getTime()}`) + + if (exists(filepath)) { + console.log(`charword_table_${region}.json is the latest version.`) + return JSON.parse(readSync(filepath)) + } + const response = await fetch(`https://raw.githubusercontent.com/Kengxxiao/ArknightsGameData/master/${region}/gamedata/excel/charword_table.json`) + const data = await response.json() + writeSync(JSON.stringify(data), filepath) + console.log(`charword_table_${region}.json is updated.`) + + // remove old file + const files = readdirSync(path.join(__dirname, __config.folder.operator, __config.folder.share)) + for (const file of files) { + if (file.startsWith(`charword_table_${region}`) && file !== path.basename(filepath)) { + rm(path.join(__dirname, __config.folder.operator, __config.folder.share, file)) + } + } + return data + } +} diff --git a/libs/file.js b/libs/file.js index 3b2d593..c75388c 100644 --- a/libs/file.js +++ b/libs/file.js @@ -53,3 +53,7 @@ export async function copy(sourcePath, targetPath) { export function appendSync(content, filePath) { return fs.appendFileSync(filePath, content, 'utf8'); } + +export function readdirSync(dir) { + return fs.readdirSync(dir) +} diff --git a/package.json b/package.json index bf05b3d..5f30612 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "vite": "^4.0.0" }, "dependencies": { + "node-fetch": "^3.3.0", "sharp": "^0.31.3", "yaml": "^2.2.1" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index adadfbd..05b297c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,11 +1,13 @@ lockfileVersion: 5.4 specifiers: + node-fetch: ^3.3.0 sharp: ^0.31.3 vite: ^4.0.0 yaml: ^2.2.1 dependencies: + node-fetch: 3.3.0 sharp: 0.31.3 yaml: 2.2.1 @@ -261,6 +263,11 @@ packages: color-string: 1.9.1 dev: false + /data-uri-to-buffer/4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + dev: false + /decompress-response/6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -319,6 +326,21 @@ packages: engines: {node: '>=6'} dev: false + /fetch-blob/3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.2.1 + dev: false + + /formdata-polyfill/4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + dependencies: + fetch-blob: 3.2.0 + dev: false + /fs-constants/1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} dev: false @@ -409,6 +431,20 @@ packages: resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==} dev: false + /node-domexception/1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + dev: false + + /node-fetch/3.3.0: + resolution: {integrity: sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + dev: false + /once/1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -623,6 +659,11 @@ packages: fsevents: 2.3.2 dev: true + /web-streams-polyfill/3.2.1: + resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==} + engines: {node: '>= 8'} + dev: false + /wrappy/1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: false diff --git a/runner.js b/runner.js index bd46e2c..7823193 100644 --- a/runner.js +++ b/runner.js @@ -11,6 +11,7 @@ import init from './libs/initializer.js' import directory from './libs/directory.js' import { appendReadme } from './libs/append.js' import Background from './libs/background.js' +import CharwordTable from './libs/charword_table.js'; async function main() { global.__dirname = path.dirname(fileURLToPath(import.meta.url)) @@ -37,6 +38,10 @@ async function main() { for (const [key, _] of Object.entries(__config.operators)) { OPERATOR_NAMES.push(key) } + case 'test': + const charwordTable = new CharwordTable() + await charwordTable.process() + process.exit(0) default: break } @@ -48,7 +53,7 @@ async function main() { const OPERATOR_RELEASE_FOLDER = path.join(__dirname, __config.folder.release, OPERATOR_NAME) const SHOWCASE_PUBLIC_ASSSETS_FOLDER = path.join(OPERATOR_RELEASE_FOLDER, "assets") const EXTRACTED_FOLDER = path.join(OPERATOR_SOURCE_FOLDER, OPERATOR_NAME, 'extracted') - const OPERATOR_SHARE_FOLDER = path.join(OPERATOR_SOURCE_FOLDER, '_share') + const OPERATOR_SHARE_FOLDER = path.join(OPERATOR_SOURCE_FOLDER, __config.folder.share) /** * Skip assets generation part