From 1f5455219b56d28f8501a131bdcfe6feb6d74c8c Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Tue, 14 Oct 2025 14:06:40 -0400 Subject: [PATCH 01/30] feat: port original BOSS gRPC implementation into v1 folder --- package-lock.json | 40 +++--- package.json | 2 +- src/services/grpc/boss/implementation.ts | 22 ---- .../grpc/boss/{ => v1}/delete-file.ts | 6 +- .../grpc/boss/{ => v1}/delete-task.ts | 6 +- src/services/grpc/boss/v1/implementation.ts | 22 ++++ src/services/grpc/boss/{ => v1}/list-files.ts | 0 .../boss/{ => v1}/list-known-boss-apps.ts | 114 +++++++++--------- src/services/grpc/boss/{ => v1}/list-tasks.ts | 2 +- .../{ => v1}/middleware/api-key-middleware.ts | 0 .../middleware/authentication-middleware.ts | 0 .../grpc/boss/{ => v1}/register-task.ts | 8 +- .../boss/{ => v1}/update-file-metadata.ts | 6 +- .../grpc/boss/{ => v1}/update-task.ts | 9 +- .../grpc/boss/{ => v1}/upload-file.ts | 4 +- src/services/grpc/server.ts | 10 +- 16 files changed, 132 insertions(+), 119 deletions(-) delete mode 100644 src/services/grpc/boss/implementation.ts rename src/services/grpc/boss/{ => v1}/delete-file.ts (82%) rename src/services/grpc/boss/{ => v1}/delete-task.ts (83%) create mode 100644 src/services/grpc/boss/v1/implementation.ts rename src/services/grpc/boss/{ => v1}/list-files.ts (100%) rename src/services/grpc/boss/{ => v1}/list-known-boss-apps.ts (77%) rename src/services/grpc/boss/{ => v1}/list-tasks.ts (91%) rename src/services/grpc/boss/{ => v1}/middleware/api-key-middleware.ts (100%) rename src/services/grpc/boss/{ => v1}/middleware/authentication-middleware.ts (100%) rename src/services/grpc/boss/{ => v1}/register-task.ts (90%) rename src/services/grpc/boss/{ => v1}/update-file-metadata.ts (90%) rename src/services/grpc/boss/{ => v1}/update-task.ts (83%) rename src/services/grpc/boss/{ => v1}/upload-file.ts (97%) diff --git a/package-lock.json b/package-lock.json index 6c229dc..8f23ae2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@aws-sdk/client-s3": "^3.723.0", "@pretendonetwork/boss-crypto": "^1.0.0", - "@pretendonetwork/grpc": "^1.0.6", + "@pretendonetwork/grpc": "^2.2.4", "@typegoose/auto-increment": "^4.13.0", "commander": "^14.0.0", "cron": "^4.3.3", @@ -917,6 +917,12 @@ "node": ">=18.0.0" } }, + "node_modules/@bufbuild/protobuf": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.9.0.tgz", + "integrity": "sha512-rnJenoStJ8nvmt9Gzye8nkYd6V22xUAnu4086ER7h1zJ508vStko4pMvDeQ446ilDTFpV5wnoc5YS7XvMwwMqA==", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, "node_modules/@emnapi/core": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", @@ -2057,12 +2063,14 @@ } }, "node_modules/@pretendonetwork/grpc": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-1.0.6.tgz", - "integrity": "sha512-kTK4lO8AdrQ5GOvYdJ7sqvIP3ubn5TGqGGqjVpgCTSiVBvBmlnz3fQkoDHmYw2WeA0CNtUx2dROG3Juiy5t7BQ==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.2.4.tgz", + "integrity": "sha512-fqhKbc7QFGtMyap5wxCPXmBH+ydPy6W4gAmlDS1wLTkE/Q4o7QVcrXGv+WMRzL8dsHpLqKNnhB1hm6K8Sbn80w==", + "license": "AGPL-3.0-only", "dependencies": { - "long": "^5.2.1", - "protobufjs": "^7.2.3" + "@bufbuild/protobuf": "^2.2.2", + "nice-grpc-common": "^2.0.2", + "typescript": "^5.7.2" } }, "node_modules/@protobufjs/aspromise": { @@ -9467,7 +9475,6 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10622,6 +10629,11 @@ "tslib": "^2.6.2" } }, + "@bufbuild/protobuf": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.9.0.tgz", + "integrity": "sha512-rnJenoStJ8nvmt9Gzye8nkYd6V22xUAnu4086ER7h1zJ508vStko4pMvDeQ446ilDTFpV5wnoc5YS7XvMwwMqA==" + }, "@emnapi/core": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", @@ -11292,12 +11304,13 @@ } }, "@pretendonetwork/grpc": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-1.0.6.tgz", - "integrity": "sha512-kTK4lO8AdrQ5GOvYdJ7sqvIP3ubn5TGqGGqjVpgCTSiVBvBmlnz3fQkoDHmYw2WeA0CNtUx2dROG3Juiy5t7BQ==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.2.4.tgz", + "integrity": "sha512-fqhKbc7QFGtMyap5wxCPXmBH+ydPy6W4gAmlDS1wLTkE/Q4o7QVcrXGv+WMRzL8dsHpLqKNnhB1hm6K8Sbn80w==", "requires": { - "long": "^5.2.1", - "protobufjs": "^7.2.3" + "@bufbuild/protobuf": "^2.2.2", + "nice-grpc-common": "^2.0.2", + "typescript": "^5.7.2" } }, "@protobufjs/aspromise": { @@ -16295,8 +16308,7 @@ "typescript": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "dev": true + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==" }, "typescript-eslint": { "version": "8.39.1", diff --git a/package.json b/package.json index 58cabfd..051820a 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "dependencies": { "@aws-sdk/client-s3": "^3.723.0", "@pretendonetwork/boss-crypto": "^1.0.0", - "@pretendonetwork/grpc": "^1.0.6", + "@pretendonetwork/grpc": "^2.2.4", "@typegoose/auto-increment": "^4.13.0", "commander": "^14.0.0", "cron": "^4.3.3", diff --git a/src/services/grpc/boss/implementation.ts b/src/services/grpc/boss/implementation.ts deleted file mode 100644 index c674db7..0000000 --- a/src/services/grpc/boss/implementation.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { listKnownBOSSApps } from '@/services/grpc/boss/list-known-boss-apps'; -import { listTasks } from '@/services/grpc/boss/list-tasks'; -import { registerTask } from '@/services/grpc/boss/register-task'; -import { updateTask } from '@/services/grpc/boss/update-task'; -import { deleteTask } from '@/services/grpc/boss/delete-task'; -import { listFiles } from '@/services/grpc/boss/list-files'; -import { uploadFile } from '@/services/grpc/boss/upload-file'; -import { updateFileMetadata } from '@/services/grpc/boss/update-file-metadata'; -import { deleteFile } from '@/services/grpc/boss/delete-file'; -import type { BOSSServiceImplementation } from '@pretendonetwork/grpc/boss/boss_service'; - -export const implementation: BOSSServiceImplementation = { - listKnownBOSSApps, - listTasks, - registerTask, - updateTask, - deleteTask, - listFiles, - uploadFile, - updateFileMetadata, - deleteFile -}; diff --git a/src/services/grpc/boss/delete-file.ts b/src/services/grpc/boss/v1/delete-file.ts similarity index 82% rename from src/services/grpc/boss/delete-file.ts rename to src/services/grpc/boss/v1/delete-file.ts index 518acb5..e1da284 100644 --- a/src/services/grpc/boss/delete-file.ts +++ b/src/services/grpc/boss/v1/delete-file.ts @@ -1,10 +1,10 @@ import { Status, ServerError } from 'nice-grpc'; import { getTaskFileByDataID } from '@/database'; -import { hasPermission } from '@/services/grpc/boss/middleware/authentication-middleware'; -import type { AuthenticationCallContextExt } from '@/services/grpc/boss/middleware/authentication-middleware'; +import { hasPermission } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; +import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; import type { CallContext } from 'nice-grpc'; import type { DeleteFileRequest } from '@pretendonetwork/grpc/boss/delete_file'; -import type { Empty } from '@pretendonetwork/grpc/boss/google/protobuf/empty'; +import type { Empty } from '@pretendonetwork/grpc/google/protobuf/empty'; export async function deleteFile(request: DeleteFileRequest, context: CallContext & AuthenticationCallContextExt): Promise { if (!hasPermission(context, 'deleteBossFiles')) { diff --git a/src/services/grpc/boss/delete-task.ts b/src/services/grpc/boss/v1/delete-task.ts similarity index 83% rename from src/services/grpc/boss/delete-task.ts rename to src/services/grpc/boss/v1/delete-task.ts index 8df48f9..4d850e3 100644 --- a/src/services/grpc/boss/delete-task.ts +++ b/src/services/grpc/boss/v1/delete-task.ts @@ -1,10 +1,10 @@ import { Status, ServerError } from 'nice-grpc'; import { getTask } from '@/database'; -import { hasPermission } from '@/services/grpc/boss/middleware/authentication-middleware'; -import type { AuthenticationCallContextExt } from '@/services/grpc/boss/middleware/authentication-middleware'; +import { hasPermission } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; +import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; import type { CallContext } from 'nice-grpc'; import type { DeleteTaskRequest } from '@pretendonetwork/grpc/boss/delete_task'; -import type { Empty } from '@pretendonetwork/grpc/boss/google/protobuf/empty'; +import type { Empty } from '@pretendonetwork/grpc/google/protobuf/empty'; export async function deleteTask(request: DeleteTaskRequest, context: CallContext & AuthenticationCallContextExt): Promise { if (!hasPermission(context, 'deleteBossTasks')) { diff --git a/src/services/grpc/boss/v1/implementation.ts b/src/services/grpc/boss/v1/implementation.ts new file mode 100644 index 0000000..fa09885 --- /dev/null +++ b/src/services/grpc/boss/v1/implementation.ts @@ -0,0 +1,22 @@ +import { listKnownBOSSApps } from '@/services/grpc/boss/v1/list-known-boss-apps'; +import { listTasks } from '@/services/grpc/boss/v1/list-tasks'; +import { registerTask } from '@/services/grpc/boss/v1/register-task'; +import { updateTask } from '@/services/grpc/boss/v1/update-task'; +import { deleteTask } from '@/services/grpc/boss/v1/delete-task'; +import { listFiles } from '@/services/grpc/boss/v1/list-files'; +import { uploadFile } from '@/services/grpc/boss/v1/upload-file'; +import { updateFileMetadata } from '@/services/grpc/boss/v1/update-file-metadata'; +import { deleteFile } from '@/services/grpc/boss/v1/delete-file'; +import type { BOSSServiceImplementation } from '@pretendonetwork/grpc/boss/boss_service'; + +export const bossServiceImplementationV1: BOSSServiceImplementation = { + listKnownBOSSApps, + listTasks, + registerTask, + updateTask, + deleteTask, + listFiles, + uploadFile, + updateFileMetadata, + deleteFile +}; diff --git a/src/services/grpc/boss/list-files.ts b/src/services/grpc/boss/v1/list-files.ts similarity index 100% rename from src/services/grpc/boss/list-files.ts rename to src/services/grpc/boss/v1/list-files.ts diff --git a/src/services/grpc/boss/list-known-boss-apps.ts b/src/services/grpc/boss/v1/list-known-boss-apps.ts similarity index 77% rename from src/services/grpc/boss/list-known-boss-apps.ts rename to src/services/grpc/boss/v1/list-known-boss-apps.ts index 039d0c9..ebb3da9 100644 --- a/src/services/grpc/boss/list-known-boss-apps.ts +++ b/src/services/grpc/boss/v1/list-known-boss-apps.ts @@ -5,399 +5,399 @@ export async function listKnownBOSSApps(): Promise { apps: [ { bossAppId: 'WJDaV6ePVgrS0TRa', - titleId: '0005003010016000', + titleId: BigInt(0x0005003010016000), titleRegion: 'UNK', name: 'Unknown', tasks: ['olvinfo'] }, { bossAppId: 'VFoY6V7u7UUq1EG5', - titleId: '0005003010016100', + titleId: BigInt(0x0005003010016100), titleRegion: 'UNK', name: 'Unknown', tasks: ['olvinfo'] }, { bossAppId: '8MNOVprfNVAJjfCM', - titleId: '0005003010016200', + titleId: BigInt(0x0005003010016200), titleRegion: 'UNK', name: 'Unknown', tasks: ['olvinfo'] }, { bossAppId: 'v1cqzWykBKUg0rHQ', - titleId: '000500301001900A', + titleId: BigInt(0x000500301001900A), titleRegion: 'JPN', name: 'Miiverse Post All', tasks: ['solv'] }, { bossAppId: 'bieC9ACJlisFg5xS', - titleId: '000500301001910A', + titleId: BigInt(0x000500301001910A), titleRegion: 'USA', name: 'Miiverse Post All', tasks: ['solv'] }, { bossAppId: 'tOaQcoBLtPTgVN3Y', - titleId: '000500301001920A', + titleId: BigInt(0x000500301001920A), titleRegion: 'EUR', name: 'Miiverse Post All', tasks: ['solv'] }, { bossAppId: 'HX8a16MMNn6i1z0Y', - titleId: '000500301001400A', + titleId: BigInt(0x000500301001400A), titleRegion: 'JPN', name: 'Nintendo eShop', tasks: ['wood1', 'woodBGM'] }, { bossAppId: '07E3nY6lAwlwrQRo', - titleId: '000500301001410A', + titleId: BigInt(0x000500301001410A), titleRegion: 'USA', name: 'Nintendo eShop', tasks: ['wood1', 'woodBGM'] }, { bossAppId: '8UsM86l8xgkjFk8z', - titleId: '000500301001420A', + titleId: BigInt(0x000500301001420A), titleRegion: 'EUR', name: 'Nintendo eShop', tasks: ['wood1', 'woodBGM'] }, { bossAppId: 'IXmFUqR2qenXfF61', - titleId: '0005001010066000', + titleId: BigInt(0x0005001010066000), titleRegion: 'ALL', name: 'ECO Process', tasks: ['promo1', 'promo2', 'promo3', 'push'] }, { bossAppId: 'BMQAm5iUVtPsJVsU', - titleId: '000500101004D000', + titleId: BigInt(0x000500101004D000), titleRegion: 'JPN', name: 'Notifications', tasks: ['sysmsg1', 'sysmsg2'] }, { bossAppId: 'LRmanFo4Tx3kEGDp', - titleId: '000500101004D100', + titleId: BigInt(0x000500101004D100), titleRegion: 'USA', name: 'Notifications', tasks: ['sysmsg1', 'sysmsg2'] }, { bossAppId: 'TZr27FE8wzKiEaTO', - titleId: '000500101004D200', + titleId: BigInt(0x000500101004D200), titleRegion: 'EUR', name: 'Notifications', tasks: ['sysmsg1', 'sysmsg2'] }, { bossAppId: 'JnIrm9c4E9JBmxBo', - titleId: '0005000010185200', + titleId: BigInt(0x0005000010185200), titleRegion: 'JPN', name: 'NewスーパーマリオブラザーズU 無料お試し版 (New SUPER MARIO BROS. U (Trial))', tasks: ['news'] }, { bossAppId: 'dadlI27Ww8H2d56x', - titleId: '0005000010101C00', + titleId: BigInt(0x0005000010101C00), titleRegion: 'JPN', name: 'NewスーパーマリオブラザーズU (New SUPER MARIO BROS. U)', tasks: ['news'] }, { bossAppId: 'RaPn5saabzliYrpo', - titleId: '0005000010101D00', + titleId: BigInt(0x0005000010101D00), titleRegion: 'USA', name: 'New SUPER MARIO BROS. U', tasks: ['news'] }, { bossAppId: '14VFIK3rY2SP0WRE', - titleId: '0005000010101E00', + titleId: BigInt(0x0005000010101E00), titleRegion: 'EUR', name: 'New SUPER MARIO BROS. U', tasks: ['news'] }, { bossAppId: 'RbEQ44t2AocC4rvu', - titleId: '000500001014B700', + titleId: BigInt(0x000500001014B700), titleRegion: 'USA', name: 'New SUPER MARIO BROS. U + New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: '287gv3WZdxo1QRhl', - titleId: '000500001014B800', + titleId: BigInt(0x000500001014B800), titleRegion: 'EUR', name: 'New SUPER MARIO BROS. U + New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: 'bb6tOEckvgZ50ciH', - titleId: '0005000010162B00', + titleId: BigInt(0x0005000010162B00), titleRegion: 'JPN', name: 'スプラトゥーン (Splatoon)', tasks: ['optdat2', 'schdat2', 'schdata'] }, { bossAppId: 'rjVlM7hUXPxmYQJh', - titleId: '0005000010176900', + titleId: BigInt(0x0005000010176900), titleRegion: 'USA', name: 'Splatoon', tasks: ['optdat2', 'schdat2', 'schdata', 'optdata2', 'schdata2'] }, { bossAppId: 'zvGSM4kOrXpkKnpT', - titleId: '0005000010176A00', + titleId: BigInt(0x0005000010176A00), titleRegion: 'EUR', name: 'Splatoon', tasks: ['optdat2', 'schdat2', 'schdata', 'optdata'] }, { bossAppId: 'm8KJPtmPweiPuETE', - titleId: '000500001012F100', + titleId: BigInt(0x000500001012F100), titleRegion: 'JPN', name: 'Wii Sports Club', tasks: ['sp1_ans'] }, { bossAppId: 'pO72Hi5uqf5yuNd8', - titleId: '0005000010144D00', + titleId: BigInt(0x0005000010144D00), titleRegion: 'USA', name: 'Wii Sports Club', tasks: ['sp1_ans'] }, { bossAppId: '4m8Xme1wKgzwslTJ', - titleId: '0005000010144E00', + titleId: BigInt(0x0005000010144E00), titleRegion: 'EUR', name: 'Wii Sports Club', tasks: ['sp1_ans'] }, { bossAppId: 'ESLqtAhxS8KQU4eu', - titleId: '000500001018DB00', + titleId: BigInt(0x000500001018DB00), titleRegion: 'JPN', name: 'Super Mario Maker (スーパーマリオメーカー)', tasks: ['CHARA'] }, { bossAppId: 'vGwChBW1ExOoHDsm', - titleId: '000500001018DC00', + titleId: BigInt(0x000500001018DC00), titleRegion: 'USA', name: 'Super Mario Maker', tasks: ['CHARA'] }, { bossAppId: 'IeUc4hQsKKe9rJHB', - titleId: '000500001018DD00', + titleId: BigInt(0x000500001018DD00), titleRegion: 'EUA', name: 'Super Mario Maker', tasks: ['CHARA'] }, { bossAppId: '4krJA4Gx3jF5nhQf', - titleId: '000500001012BC00', + titleId: BigInt(0x000500001012BC00), titleRegion: 'JPN', name: 'ピクミン3 (PIKMIN 3)', tasks: ['histgrm'] }, { bossAppId: '9jRZEoWYLc3OG9a8', - titleId: '000500001012BD00', + titleId: BigInt(0x000500001012BD00), titleRegion: 'USA', name: 'PIKMIN 3', tasks: ['histgrm'] }, { bossAppId: 'VWqUTspR5YtjDjxa', - titleId: '000500001012BE00', + titleId: BigInt(0x000500001012BE00), titleRegion: 'EUR', name: 'PIKMIN 3', tasks: ['histgrm'] }, { bossAppId: 'Ge1KtMu8tYlf4AUM', - titleId: '0005000010192000', + titleId: BigInt(0x0005000010192000), titleRegion: 'JPN', name: '太鼓の達人 特盛り! (Taiko no Tatsujin Tokumori!)', tasks: ['notice1'] }, { bossAppId: 'gycVtTzCouZmukZ6', - titleId: '0005000010110E00', + titleId: BigInt(0x0005000010110E00), titleRegion: 'JPN', name: '大乱闘スマッシュブラザーズ for Wii U (Super Smash Bros. for Wii U)', tasks: ['NEWS', 'amiibo'] }, { bossAppId: 'o2Ug1pIp9Uhri6Nh', - titleId: '0005000010144F00', + titleId: BigInt(0x0005000010144F00), titleRegion: 'USA', name: 'Super Smash Bros. for Wii U', tasks: ['amiibo', 'NEWS', 'friend', 'CONQ'] }, { bossAppId: 'n6rAJ1nnfC1Sgcpl', - titleId: '0005000010145000', + titleId: BigInt(0x0005000010145000), titleRegion: 'EUR', name: 'Super Smash Bros. for Wii U', tasks: ['amiibo', 'NEWS', 'friend', 'CONQ'] }, { bossAppId: 'CHUN6T1m7Xk4EBg4', - titleId: '00050000101DFF00', + titleId: BigInt(0x00050000101DFF00), titleRegion: 'JPN', name: 'プチコンBIG (Petitcom BIG)', tasks: ['ptcbnws'] }, { bossAppId: 'zyXdCW9jGdi9rjaz', - titleId: '0005000010142200', + titleId: BigInt(0x0005000010142200), titleRegion: 'JPN', name: 'NewスーパールイージU (New SUPER LUIGI U)', tasks: ['news'] }, { bossAppId: 'jPHLlJr2fJyTzffp', - titleId: '0005000010142300', + titleId: BigInt(0x0005000010142300), titleRegion: 'USA', name: 'New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: 'YsXB6IRGSI56tPxl', - titleId: '0005000010142400', + titleId: BigInt(0x0005000010142400), titleRegion: 'EUR', name: 'New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: 'Lbqp9Sg1i0xUzFFa', - titleId: '0005000010113800', + titleId: BigInt(0x0005000010113800), titleRegion: 'EUR', name: 'Zen Pinball 2', tasks: ['PTS'] }, { bossAppId: 'DwU7n0FidGrLNiOo', - titleId: '000500001014D900', + titleId: BigInt(0x000500001014D900), titleRegion: 'JPN', name: 'ぷよぷよテトリス (PUYOPUYOTETRIS)', tasks: ['boss1', 'boss2', 'boss3'] }, { bossAppId: 'yIUkFmuGVkGP8pDb', - titleId: '0005000010132200', + titleId: BigInt(0x0005000010132200), titleRegion: 'JPN', name: '太鼓の達人 Wii Uば~じょん! (Taiko no Tatsujin Wii U version!)', tasks: ['notice1'] }, { bossAppId: 'v4WRObSzD7VU3dcJ', - titleId: '00050000101D3000', + titleId: BigInt(0x00050000101D3000), titleRegion: 'JPN', name: '太鼓の達人 あつめて★ともだち大作戦! (Taiko no Tatsujin Atsumete★ TomodachiDaisakusen!)', tasks: ['notice1'] }, { bossAppId: '3zDjXIA57bSceyaw', - titleId: '00050000101BEC00', + titleId: BigInt(0x00050000101BEC00), titleRegion: 'USA', name: 'Star Fox Guard', tasks: ['param'] }, { bossAppId: 'NL38jhExI2CQqhWd', - titleId: '00050000101CDB00', + titleId: BigInt(0x00050000101CDB00), titleRegion: 'JPN', name: 'Splatoon Pre-Launch Review', tasks: ['schdata'] }, { bossAppId: 'sE6KwEpQYyg6tdU7', - titleId: '00050000101CDC00', + titleId: BigInt(0x00050000101CDC00), titleRegion: 'USA', name: 'Splatoon Pre-Launch Review', tasks: ['schdata'] }, { bossAppId: 'pTKZ9q5KrCP3gBag', - titleId: '00050000101CDD00', + titleId: BigInt(0x00050000101CDD00), titleRegion: 'EUR', name: 'Splatoon Pre-Launch Review', tasks: ['schdata'] }, { bossAppId: 'CJT88RO008LAnD51', - titleId: '0005000010170600', + titleId: BigInt(0x0005000010170600), titleRegion: 'JPN', name: '仮面ライダー バトライド・ウォーⅡ プレミアムTV&MOVIEサウンドED. (KAMEN RIDER BATTRIDE WAR Ⅱ PREMIUM TV&MOVIE SOUND ED.)', tasks: ['PE_GAK', 'PE_ZNG'] }, { bossAppId: 'FyyMFzEByuQJc6sJ', - titleId: '0005000010135200', + titleId: BigInt(0x0005000010135200), titleRegion: 'USA', name: 'Star Wars Pinball', tasks: ['PTS'] }, { bossAppId: 'A4yyXWKZZUToFtrt', - titleId: '0005000010132A00', + titleId: BigInt(0x0005000010132A00), titleRegion: 'EUR', name: 'Star Wars Pinball', tasks: ['PTS'] }, { bossAppId: 'HauaFQ1sPsnQ6rBj', - titleId: '0005000010171F00', + titleId: BigInt(0x0005000010171F00), titleRegion: 'USA', name: 'Pushmo World', tasks: ['annouce'] }, { bossAppId: 'qDUeFmk0Az71nHyD', - titleId: '0005000010110900', + titleId: BigInt(0x0005000010110900), titleRegion: 'JPN', name: 'NINJA GAIDEN 3: Razor\'s Edge', tasks: ['DLCINFO'] }, { bossAppId: 'yVsSPM2E0DEOxroT', - titleId: '0005000010110A00', + titleId: BigInt(0x0005000010110A00), titleRegion: 'USA', name: 'NINJA GAIDEN 3: Razor\'s Edge', tasks: ['DLCINFO'] }, { bossAppId: 'Xw6OvZkQofQ3O8Bi', - titleId: '0005000010110B00', + titleId: BigInt(0x0005000010110B00), titleRegion: 'EUR', name: 'Ninja Gaiden 3: Razor\'s Edge', tasks: ['DLCINFO'] }, { bossAppId: 'LUQX5swEjBUPQ8nR', - titleId: '0005000010110200', + titleId: BigInt(0x0005000010110200), titleRegion: 'USA', name: 'WARRIORS OROCHI 3 Hyper(NA)', tasks: ['OR2H000'] }, { bossAppId: 'y4pXrgLe0JGao3No', - titleId: '0005000010112B00', + titleId: BigInt(0x0005000010112B00), titleRegion: 'EUR', name: 'WARRIORS OROCHI 3 Hyper(EU)', tasks: ['OR2H000'] }, { bossAppId: 'j01mRJ9sNe00MWPC', - titleId: '0005000010170700', + titleId: BigInt(0x0005000010170700), titleRegion: 'JPN', name: '仮面ライダー バトライド・ウォーⅡ (KAMEN RIDER BATTRIDE WAR Ⅱ)', tasks: ['CHR_GAK', 'CHR_ZNG'] diff --git a/src/services/grpc/boss/list-tasks.ts b/src/services/grpc/boss/v1/list-tasks.ts similarity index 91% rename from src/services/grpc/boss/list-tasks.ts rename to src/services/grpc/boss/v1/list-tasks.ts index 983afc4..a3b93ea 100644 --- a/src/services/grpc/boss/list-tasks.ts +++ b/src/services/grpc/boss/v1/list-tasks.ts @@ -12,7 +12,7 @@ export async function listTasks(): Promise { bossAppId: task.boss_app_id, creatorPid: task.creator_pid, status: task.status, - titleId: task.title_id, + titleId: BigInt(parseInt(task.title_id, 16)), description: task.description, createdTimestamp: task.created, updatedTimestamp: task.updated diff --git a/src/services/grpc/boss/middleware/api-key-middleware.ts b/src/services/grpc/boss/v1/middleware/api-key-middleware.ts similarity index 100% rename from src/services/grpc/boss/middleware/api-key-middleware.ts rename to src/services/grpc/boss/v1/middleware/api-key-middleware.ts diff --git a/src/services/grpc/boss/middleware/authentication-middleware.ts b/src/services/grpc/boss/v1/middleware/authentication-middleware.ts similarity index 100% rename from src/services/grpc/boss/middleware/authentication-middleware.ts rename to src/services/grpc/boss/v1/middleware/authentication-middleware.ts diff --git a/src/services/grpc/boss/register-task.ts b/src/services/grpc/boss/v1/register-task.ts similarity index 90% rename from src/services/grpc/boss/register-task.ts rename to src/services/grpc/boss/v1/register-task.ts index 36c03b8..cade547 100644 --- a/src/services/grpc/boss/register-task.ts +++ b/src/services/grpc/boss/v1/register-task.ts @@ -1,8 +1,8 @@ import { ServerError, Status } from 'nice-grpc'; import { getTask } from '@/database'; import { Task } from '@/models/task'; -import { hasPermission } from '@/services/grpc/boss/middleware/authentication-middleware'; -import type { AuthenticationCallContextExt } from '@/services/grpc/boss/middleware/authentication-middleware'; +import { hasPermission } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; +import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; import type { CallContext } from 'nice-grpc'; import type { RegisterTaskRequest, RegisterTaskResponse } from '@pretendonetwork/grpc/boss/register_task'; @@ -15,7 +15,7 @@ export async function registerTask(request: RegisterTaskRequest, context: CallCo const taskID = request.id.trim(); const bossAppID = request.bossAppId.trim(); - const titleID = request.titleId.trim().toLocaleLowerCase(); + const titleID = request.titleId.toString(16).toLowerCase().padStart(16, '0'); const description = request.description.trim(); if (!taskID) { @@ -68,7 +68,7 @@ export async function registerTask(request: RegisterTaskRequest, context: CallCo bossAppId: task.boss_app_id, creatorPid: task.creator_pid, status: task.status, - titleId: task.title_id, + titleId: BigInt(parseInt(task.title_id, 16)), description: task.description, createdTimestamp: task.created, updatedTimestamp: task.updated diff --git a/src/services/grpc/boss/update-file-metadata.ts b/src/services/grpc/boss/v1/update-file-metadata.ts similarity index 90% rename from src/services/grpc/boss/update-file-metadata.ts rename to src/services/grpc/boss/v1/update-file-metadata.ts index 7c7bac1..0a23437 100644 --- a/src/services/grpc/boss/update-file-metadata.ts +++ b/src/services/grpc/boss/v1/update-file-metadata.ts @@ -1,11 +1,11 @@ import { Status, ServerError } from 'nice-grpc'; import { getTaskFileByDataID } from '@/database'; import { isValidFileNotifyCondition, isValidFileType } from '@/util'; -import { hasPermission } from '@/services/grpc/boss/middleware/authentication-middleware'; -import type { AuthenticationCallContextExt } from '@/services/grpc/boss/middleware/authentication-middleware'; +import { hasPermission } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; +import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; import type { CallContext } from 'nice-grpc'; import type { UpdateFileMetadataRequest } from '@pretendonetwork/grpc/boss/update_file_metadata'; -import type { Empty } from '@pretendonetwork/grpc/boss/google/protobuf/empty'; +import type { Empty } from '@pretendonetwork/grpc/google/protobuf/empty'; export async function updateFileMetadata(request: UpdateFileMetadataRequest, context: CallContext & AuthenticationCallContextExt): Promise { if (!hasPermission(context, 'updateBossFiles')) { diff --git a/src/services/grpc/boss/update-task.ts b/src/services/grpc/boss/v1/update-task.ts similarity index 83% rename from src/services/grpc/boss/update-task.ts rename to src/services/grpc/boss/v1/update-task.ts index f18a2a1..69c7c42 100644 --- a/src/services/grpc/boss/update-task.ts +++ b/src/services/grpc/boss/v1/update-task.ts @@ -1,10 +1,10 @@ import { Status, ServerError } from 'nice-grpc'; import { getTask } from '@/database'; -import { hasPermission } from '@/services/grpc/boss/middleware/authentication-middleware'; -import type { AuthenticationCallContextExt } from '@/services/grpc/boss/middleware/authentication-middleware'; +import { hasPermission } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; +import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; import type { CallContext } from 'nice-grpc'; import type { UpdateTaskRequest } from '@pretendonetwork/grpc/boss/update_task'; -import type { Empty } from '@pretendonetwork/grpc/boss/google/protobuf/empty'; +import type { Empty } from '@pretendonetwork/grpc/google/protobuf/empty'; export async function updateTask(request: UpdateTaskRequest, context: CallContext & AuthenticationCallContextExt): Promise { if (!hasPermission(context, 'updateBossTasks')) { @@ -41,8 +41,9 @@ export async function updateTask(request: UpdateTaskRequest, context: CallContex task.id = updateData.id.slice(0, 7); task.in_game_id = updateData.id; } + task.boss_app_id = updateData.bossAppId ? updateData.bossAppId : task.boss_app_id; - task.title_id = updateData.titleId ? updateData.titleId : task.title_id; + task.title_id = updateData.titleId ? updateData.titleId.toString(16).toLowerCase().padStart(16, '0') : task.title_id; task.status = updateData.status ? updateData.status : task.status; task.description = updateData.description ? updateData.description : task.description; task.updated = BigInt(Date.now()); diff --git a/src/services/grpc/boss/upload-file.ts b/src/services/grpc/boss/v1/upload-file.ts similarity index 97% rename from src/services/grpc/boss/upload-file.ts rename to src/services/grpc/boss/v1/upload-file.ts index 95d6dd2..be7d873 100644 --- a/src/services/grpc/boss/upload-file.ts +++ b/src/services/grpc/boss/v1/upload-file.ts @@ -5,8 +5,8 @@ import { getTask, getTaskFile } from '@/database'; import { File } from '@/models/file'; import { config } from '@/config-manager'; import { uploadCDNFile } from '@/cdn'; -import { hasPermission } from '@/services/grpc/boss/middleware/authentication-middleware'; -import type { AuthenticationCallContextExt } from '@/services/grpc/boss/middleware/authentication-middleware'; +import { hasPermission } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; +import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; import type { CallContext } from 'nice-grpc'; import type { UploadFileRequest, UploadFileResponse } from '@pretendonetwork/grpc/boss/upload_file'; diff --git a/src/services/grpc/server.ts b/src/services/grpc/server.ts index f156275..2c09231 100644 --- a/src/services/grpc/server.ts +++ b/src/services/grpc/server.ts @@ -1,15 +1,15 @@ import { createServer } from 'nice-grpc'; -import { BOSSDefinition } from '@pretendonetwork/grpc/boss/boss_service'; -import { apiKeyMiddleware } from '@/services/grpc/boss/middleware/api-key-middleware'; -import { authenticationMiddleware } from '@/services/grpc/boss/middleware/authentication-middleware'; -import { implementation } from '@/services/grpc/boss/implementation'; +import { BOSSDefinition as BossServiceDefinitionV1 } from '@pretendonetwork/grpc/boss/boss_service'; +import { apiKeyMiddleware as apiKeyMiddlewareV1 } from '@/services/grpc/boss/v1/middleware/api-key-middleware'; +import { authenticationMiddleware as authenticationMiddlewareV1 } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; +import { bossServiceImplementationV1 } from '@/services/grpc/boss/v1/implementation'; import { config } from '@/config-manager'; import type { Server } from 'nice-grpc'; export async function startGRPCServer(): Promise { const server: Server = createServer(); - server.with(apiKeyMiddleware).with(authenticationMiddleware).add(BOSSDefinition, implementation); + server.with(apiKeyMiddlewareV1).with(authenticationMiddlewareV1).add(BossServiceDefinitionV1, bossServiceImplementationV1); await server.listen(`${config.grpc.boss.address}:${config.grpc.boss.port}`); } From 22f3016f5057f6c888100e7c4e982bf110e35bd9 Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Tue, 14 Oct 2025 14:47:25 -0400 Subject: [PATCH 02/30] fix: update cli create command to use bigint title ID --- src/cli/tasks.cmd.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/tasks.cmd.ts b/src/cli/tasks.cmd.ts index ef41894..13b4282 100644 --- a/src/cli/tasks.cmd.ts +++ b/src/cli/tasks.cmd.ts @@ -62,7 +62,7 @@ const createCmd = new Command('create') const { task } = await ctx.grpc.registerTask({ bossAppId: appId, id: opts.id, - titleId: opts.titleId, + titleId: BigInt(parseInt(opts.titleId, 16)), description: opts.desc ?? '', country: 'This value isnt used' }); From 4186968576c3975d5309204757a47e1ce5dd3f9c Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Tue, 14 Oct 2025 14:49:07 -0400 Subject: [PATCH 03/30] feat: begin gRPC 2.2.4 port. missing CTR some of these changes seem like they need database changes as well, that needs to be talked about --- src/services/grpc/boss/v2/delete-file.ts | 33 ++ src/services/grpc/boss/v2/delete-task.ts | 37 ++ src/services/grpc/boss/v2/implementation.ts | 28 ++ src/services/grpc/boss/v2/list-files-ctr.ts | 64 +++ src/services/grpc/boss/v2/list-files-wup.ts | 67 +++ .../grpc/boss/v2/list-known-boss-apps.ts | 407 ++++++++++++++++++ src/services/grpc/boss/v2/list-tasks.ts | 22 + .../boss/v2/middleware/api-key-middleware.ts | 16 + .../middleware/authentication-middleware.ts | 52 +++ src/services/grpc/boss/v2/register-task.ts | 78 ++++ .../grpc/boss/v2/update-file-metadata-wup.ts | 59 +++ src/services/grpc/boss/v2/update-task.ts | 54 +++ src/services/grpc/boss/v2/upload-file-wup.ts | 174 ++++++++ src/services/grpc/server.ts | 7 + 14 files changed, 1098 insertions(+) create mode 100644 src/services/grpc/boss/v2/delete-file.ts create mode 100644 src/services/grpc/boss/v2/delete-task.ts create mode 100644 src/services/grpc/boss/v2/implementation.ts create mode 100644 src/services/grpc/boss/v2/list-files-ctr.ts create mode 100644 src/services/grpc/boss/v2/list-files-wup.ts create mode 100644 src/services/grpc/boss/v2/list-known-boss-apps.ts create mode 100644 src/services/grpc/boss/v2/list-tasks.ts create mode 100644 src/services/grpc/boss/v2/middleware/api-key-middleware.ts create mode 100644 src/services/grpc/boss/v2/middleware/authentication-middleware.ts create mode 100644 src/services/grpc/boss/v2/register-task.ts create mode 100644 src/services/grpc/boss/v2/update-file-metadata-wup.ts create mode 100644 src/services/grpc/boss/v2/update-task.ts create mode 100644 src/services/grpc/boss/v2/upload-file-wup.ts diff --git a/src/services/grpc/boss/v2/delete-file.ts b/src/services/grpc/boss/v2/delete-file.ts new file mode 100644 index 0000000..462194e --- /dev/null +++ b/src/services/grpc/boss/v2/delete-file.ts @@ -0,0 +1,33 @@ +import { Status, ServerError } from 'nice-grpc'; +import { getTaskFileByDataID } from '@/database'; +import { hasPermission } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { CallContext } from 'nice-grpc'; +import type { DeleteFileRequest } from '@pretendonetwork/grpc/boss/v2/delete_file'; +import type { Empty } from '@pretendonetwork/grpc/google/protobuf/empty'; + +export async function deleteFile(request: DeleteFileRequest, context: CallContext & AuthenticationCallContextExt): Promise { + if (!hasPermission(context, 'deleteBossFiles')) { + throw new ServerError(Status.PERMISSION_DENIED, 'PNID not authorized to delete files'); + } + + const dataID = request.dataId; + const bossAppID = request.bossAppId.trim(); + + if (!dataID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing file data ID'); + } + + const file = await getTaskFileByDataID(dataID); + + if (!file || file.boss_app_id !== bossAppID) { + throw new ServerError(Status.INVALID_ARGUMENT, `File ${dataID} not found for BOSS app ${bossAppID}`); + } + + file.deleted = true; + file.updated = BigInt(Date.now()); + + await file.save(); + + return {}; +} diff --git a/src/services/grpc/boss/v2/delete-task.ts b/src/services/grpc/boss/v2/delete-task.ts new file mode 100644 index 0000000..6f5129a --- /dev/null +++ b/src/services/grpc/boss/v2/delete-task.ts @@ -0,0 +1,37 @@ +import { Status, ServerError } from 'nice-grpc'; +import { getTask } from '@/database'; +import { hasPermission } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { CallContext } from 'nice-grpc'; +import type { DeleteTaskRequest } from '@pretendonetwork/grpc/boss/v2/delete_task'; +import type { Empty } from '@pretendonetwork/grpc/google/protobuf/empty'; + +export async function deleteTask(request: DeleteTaskRequest, context: CallContext & AuthenticationCallContextExt): Promise { + if (!hasPermission(context, 'deleteBossTasks')) { + throw new ServerError(Status.PERMISSION_DENIED, 'PNID not authorized to delete tasks'); + } + + const taskID = request.id.trim(); + const bossAppID = request.bossAppId.trim(); + + if (!taskID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing task ID'); + } + + if (!bossAppID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing BOSS app ID'); + } + + const task = await getTask(bossAppID, taskID); + + if (!task) { + throw new ServerError(Status.INVALID_ARGUMENT, `Task ${taskID} not found for BOSS app ${bossAppID}`); + } + + task.deleted = true; + task.updated = BigInt(Date.now()); + + await task.save(); + + return {}; +} diff --git a/src/services/grpc/boss/v2/implementation.ts b/src/services/grpc/boss/v2/implementation.ts new file mode 100644 index 0000000..86d45da --- /dev/null +++ b/src/services/grpc/boss/v2/implementation.ts @@ -0,0 +1,28 @@ +import { listKnownBOSSApps } from '@/services/grpc/boss/v2/list-known-boss-apps'; +import { listTasks } from '@/services/grpc/boss/v2/list-tasks'; +import { registerTask } from '@/services/grpc/boss/v2/register-task'; +import { updateTask } from '@/services/grpc/boss/v2/update-task'; +import { deleteTask } from '@/services/grpc/boss/v2/delete-task'; +import { deleteFile } from '@/services/grpc/boss/v2/delete-file'; +import { listFilesWUP } from '@/services/grpc/boss/v2/list-files-wup'; +import { uploadFileWUP } from '@/services/grpc/boss/v2/upload-file-wup'; +import { listFilesCTR } from '@/services/grpc/boss/v2/list-files-ctr'; +// import { uploadFileCTR } from '@/services/grpc/boss/v2/upload-file-ctr'; +// import { updateFileMetadataCTR } from '@/services/grpc/boss/v2/update-file-metadata-ctr'; +import { updateFileMetadataWUP } from '@/services/grpc/boss/v2/update-file-metadata-wup'; +import type { BossServiceImplementation } from '@pretendonetwork/grpc/boss/v2/boss_service'; + +export const bossServiceImplementationV2: BossServiceImplementation = { + listKnownBOSSApps, + listTasks, + registerTask, + updateTask, + deleteTask, + deleteFile, + listFilesWUP, + uploadFileWUP, + listFilesCTR, + // uploadFileCTR, + // updateFileMetadataCTR, + updateFileMetadataWUP +}; diff --git a/src/services/grpc/boss/v2/list-files-ctr.ts b/src/services/grpc/boss/v2/list-files-ctr.ts new file mode 100644 index 0000000..1b7bd9c --- /dev/null +++ b/src/services/grpc/boss/v2/list-files-ctr.ts @@ -0,0 +1,64 @@ +import { Status, ServerError } from 'nice-grpc'; +import { isValidCountryCode, isValidLanguage } from '@/util'; +import { getTaskFiles } from '@/database'; +import type { ListFilesCTRRequest, ListFilesCTRResponse } from '@pretendonetwork/grpc/boss/v2/list_files_ctr'; + +const BOSS_APP_ID_FILTER_REGEX = /^[A-Za-z0-9]*$/; + +export async function listFilesCTR(request: ListFilesCTRRequest): Promise { + const taskID = request.taskId.trim(); + const bossAppID = request.bossAppId.trim(); + const country = request.country?.trim(); + const language = request.language?.trim(); + + if (!taskID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing task ID'); + } + + if (!bossAppID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing BOSS app ID'); + } + + if (bossAppID.length !== 16) { + throw new ServerError(Status.INVALID_ARGUMENT, 'BOSS app ID must be 16 characters'); + } + + if (!BOSS_APP_ID_FILTER_REGEX.test(bossAppID)) { + throw new ServerError(Status.INVALID_ARGUMENT, 'BOSS app ID must only contain letters and numbers'); + } + + if (country && !isValidCountryCode(country)) { + throw new ServerError(Status.INVALID_ARGUMENT, `${country} is not a valid country`); + } + + if (language && !isValidLanguage(language)) { + throw new ServerError(Status.INVALID_ARGUMENT, `${language} is not a valid language`); + } + + const files = await getTaskFiles(false, bossAppID, taskID, country, language); + + return { + files: files.map(file => ({ + deleted: file.deleted, + dataId: file.data_id, + taskId: file.task_id, + bossAppId: file.boss_app_id, + supportedCountries: file.supported_countries, + supportedLanguages: file.supported_languages, + attributes: { + attribute1: file.attribute1, + attribute2: file.attribute2, + attribute3: file.attribute3, + description: file.password + }, + creatorPid: file.creator_pid, + name: file.name, + hash: file.hash, + serialNumber: 0, // TODO - Don't stub this + payloadContents: [], // TODO - Don't stub this + size: file.size, + createdTimestamp: file.created, + updatedTimestamp: file.updated + })) + }; +} diff --git a/src/services/grpc/boss/v2/list-files-wup.ts b/src/services/grpc/boss/v2/list-files-wup.ts new file mode 100644 index 0000000..80f429c --- /dev/null +++ b/src/services/grpc/boss/v2/list-files-wup.ts @@ -0,0 +1,67 @@ +import { Status, ServerError } from 'nice-grpc'; +import { isValidCountryCode, isValidLanguage } from '@/util'; +import { getTaskFiles } from '@/database'; +import type { ListFilesWUPRequest, ListFilesWUPResponse } from '@pretendonetwork/grpc/boss/v2/list_files_wup'; + +const BOSS_APP_ID_FILTER_REGEX = /^[A-Za-z0-9]*$/; + +export async function listFilesWUP(request: ListFilesWUPRequest): Promise { + const taskID = request.taskId.trim(); + const bossAppID = request.bossAppId.trim(); + const country = request.country?.trim(); + const language = request.language?.trim(); + + if (!taskID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing task ID'); + } + + if (!bossAppID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing BOSS app ID'); + } + + if (bossAppID.length !== 16) { + throw new ServerError(Status.INVALID_ARGUMENT, 'BOSS app ID must be 16 characters'); + } + + if (!BOSS_APP_ID_FILTER_REGEX.test(bossAppID)) { + throw new ServerError(Status.INVALID_ARGUMENT, 'BOSS app ID must only contain letters and numbers'); + } + + if (country && !isValidCountryCode(country)) { + throw new ServerError(Status.INVALID_ARGUMENT, `${country} is not a valid country`); + } + + if (language && !isValidLanguage(language)) { + throw new ServerError(Status.INVALID_ARGUMENT, `${language} is not a valid language`); + } + + const files = await getTaskFiles(false, bossAppID, taskID, country, language); + + return { + files: files.map(file => ({ + deleted: file.deleted, + dataId: file.data_id, + taskId: file.task_id, + bossAppId: file.boss_app_id, + supportedCountries: file.supported_countries, + supportedLanguages: file.supported_languages, + attributes: { + attribute1: file.attribute1, + attribute2: file.attribute2, + attribute3: file.attribute3, + description: file.password + }, + creatorPid: file.creator_pid, + name: file.name, + type: file.type, + hash: file.hash, + size: file.size, + notifyOnNew: file.notify_on_new, + notifyLed: file.notify_led, + conditionPlayed: 0n, // TODO - Don't stub this + autoDelete: false, // TODO - Don't stub this + createdTimestamp: file.created, + updatedTimestamp: file.updated + })) + }; +} diff --git a/src/services/grpc/boss/v2/list-known-boss-apps.ts b/src/services/grpc/boss/v2/list-known-boss-apps.ts new file mode 100644 index 0000000..7e7f157 --- /dev/null +++ b/src/services/grpc/boss/v2/list-known-boss-apps.ts @@ -0,0 +1,407 @@ +import type { ListKnownBOSSAppsResponse } from '@pretendonetwork/grpc/boss/v2/list_known_boss_apps'; + +export async function listKnownBOSSApps(): Promise { + return { + apps: [ + { + bossAppId: 'WJDaV6ePVgrS0TRa', + titleId: BigInt(0x0005003010016000), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['olvinfo'] + }, + { + bossAppId: 'VFoY6V7u7UUq1EG5', + titleId: BigInt(0x0005003010016100), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['olvinfo'] + }, + { + bossAppId: '8MNOVprfNVAJjfCM', + titleId: BigInt(0x0005003010016200), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['olvinfo'] + }, + { + bossAppId: 'v1cqzWykBKUg0rHQ', + titleId: BigInt(0x000500301001900A), + titleRegion: 'JPN', + name: 'Miiverse Post All', + tasks: ['solv'] + }, + { + bossAppId: 'bieC9ACJlisFg5xS', + titleId: BigInt(0x000500301001910A), + titleRegion: 'USA', + name: 'Miiverse Post All', + tasks: ['solv'] + }, + { + bossAppId: 'tOaQcoBLtPTgVN3Y', + titleId: BigInt(0x000500301001920A), + titleRegion: 'EUR', + name: 'Miiverse Post All', + tasks: ['solv'] + }, + { + bossAppId: 'HX8a16MMNn6i1z0Y', + titleId: BigInt(0x000500301001400A), + titleRegion: 'JPN', + name: 'Nintendo eShop', + tasks: ['wood1', 'woodBGM'] + }, + { + bossAppId: '07E3nY6lAwlwrQRo', + titleId: BigInt(0x000500301001410A), + titleRegion: 'USA', + name: 'Nintendo eShop', + tasks: ['wood1', 'woodBGM'] + }, + { + bossAppId: '8UsM86l8xgkjFk8z', + titleId: BigInt(0x000500301001420A), + titleRegion: 'EUR', + name: 'Nintendo eShop', + tasks: ['wood1', 'woodBGM'] + }, + { + bossAppId: 'IXmFUqR2qenXfF61', + titleId: BigInt(0x0005001010066000), + titleRegion: 'ALL', + name: 'ECO Process', + tasks: ['promo1', 'promo2', 'promo3', 'push'] + }, + { + bossAppId: 'BMQAm5iUVtPsJVsU', + titleId: BigInt(0x000500101004D000), + titleRegion: 'JPN', + name: 'Notifications', + tasks: ['sysmsg1', 'sysmsg2'] + }, + { + bossAppId: 'LRmanFo4Tx3kEGDp', + titleId: BigInt(0x000500101004D100), + titleRegion: 'USA', + name: 'Notifications', + tasks: ['sysmsg1', 'sysmsg2'] + }, + { + bossAppId: 'TZr27FE8wzKiEaTO', + titleId: BigInt(0x000500101004D200), + titleRegion: 'EUR', + name: 'Notifications', + tasks: ['sysmsg1', 'sysmsg2'] + }, + { + bossAppId: 'JnIrm9c4E9JBmxBo', + titleId: BigInt(0x0005000010185200), + titleRegion: 'JPN', + name: 'NewスーパーマリオブラザーズU 無料お試し版 (New SUPER MARIO BROS. U (Trial))', + tasks: ['news'] + }, + { + bossAppId: 'dadlI27Ww8H2d56x', + titleId: BigInt(0x0005000010101C00), + titleRegion: 'JPN', + name: 'NewスーパーマリオブラザーズU (New SUPER MARIO BROS. U)', + tasks: ['news'] + }, + { + bossAppId: 'RaPn5saabzliYrpo', + titleId: BigInt(0x0005000010101D00), + titleRegion: 'USA', + name: 'New SUPER MARIO BROS. U', + tasks: ['news'] + }, + { + bossAppId: '14VFIK3rY2SP0WRE', + titleId: BigInt(0x0005000010101E00), + titleRegion: 'EUR', + name: 'New SUPER MARIO BROS. U', + tasks: ['news'] + }, + { + bossAppId: 'RbEQ44t2AocC4rvu', + titleId: BigInt(0x000500001014B700), + titleRegion: 'USA', + name: 'New SUPER MARIO BROS. U + New SUPER LUIGI U', + tasks: ['news'] + }, + { + bossAppId: '287gv3WZdxo1QRhl', + titleId: BigInt(0x000500001014B800), + titleRegion: 'EUR', + name: 'New SUPER MARIO BROS. U + New SUPER LUIGI U', + tasks: ['news'] + }, + { + bossAppId: 'bb6tOEckvgZ50ciH', + titleId: BigInt(0x0005000010162B00), + titleRegion: 'JPN', + name: 'スプラトゥーン (Splatoon)', + tasks: ['optdat2', 'schdat2', 'schdata'] + }, + { + bossAppId: 'rjVlM7hUXPxmYQJh', + titleId: BigInt(0x0005000010176900), + titleRegion: 'USA', + name: 'Splatoon', + tasks: ['optdat2', 'schdat2', 'schdata', 'optdata2', 'schdata2'] + }, + { + bossAppId: 'zvGSM4kOrXpkKnpT', + titleId: BigInt(0x0005000010176A00), + titleRegion: 'EUR', + name: 'Splatoon', + tasks: ['optdat2', 'schdat2', 'schdata', 'optdata'] + }, + { + bossAppId: 'm8KJPtmPweiPuETE', + titleId: BigInt(0x000500001012F100), + titleRegion: 'JPN', + name: 'Wii Sports Club', + tasks: ['sp1_ans'] + }, + { + bossAppId: 'pO72Hi5uqf5yuNd8', + titleId: BigInt(0x0005000010144D00), + titleRegion: 'USA', + name: 'Wii Sports Club', + tasks: ['sp1_ans'] + }, + { + bossAppId: '4m8Xme1wKgzwslTJ', + titleId: BigInt(0x0005000010144E00), + titleRegion: 'EUR', + name: 'Wii Sports Club', + tasks: ['sp1_ans'] + }, + { + bossAppId: 'ESLqtAhxS8KQU4eu', + titleId: BigInt(0x000500001018DB00), + titleRegion: 'JPN', + name: 'Super Mario Maker (スーパーマリオメーカー)', + tasks: ['CHARA'] + }, + { + bossAppId: 'vGwChBW1ExOoHDsm', + titleId: BigInt(0x000500001018DC00), + titleRegion: 'USA', + name: 'Super Mario Maker', + tasks: ['CHARA'] + }, + { + bossAppId: 'IeUc4hQsKKe9rJHB', + titleId: BigInt(0x000500001018DD00), + titleRegion: 'EUA', + name: 'Super Mario Maker', + tasks: ['CHARA'] + }, + { + bossAppId: '4krJA4Gx3jF5nhQf', + titleId: BigInt(0x000500001012BC00), + titleRegion: 'JPN', + name: 'ピクミン3 (PIKMIN 3)', + tasks: ['histgrm'] + }, + { + bossAppId: '9jRZEoWYLc3OG9a8', + titleId: BigInt(0x000500001012BD00), + titleRegion: 'USA', + name: 'PIKMIN 3', + tasks: ['histgrm'] + }, + { + bossAppId: 'VWqUTspR5YtjDjxa', + titleId: BigInt(0x000500001012BE00), + titleRegion: 'EUR', + name: 'PIKMIN 3', + tasks: ['histgrm'] + }, + { + bossAppId: 'Ge1KtMu8tYlf4AUM', + titleId: BigInt(0x0005000010192000), + titleRegion: 'JPN', + name: '太鼓の達人 特盛り! (Taiko no Tatsujin Tokumori!)', + tasks: ['notice1'] + }, + { + bossAppId: 'gycVtTzCouZmukZ6', + titleId: BigInt(0x0005000010110E00), + titleRegion: 'JPN', + name: '大乱闘スマッシュブラザーズ for Wii U (Super Smash Bros. for Wii U)', + tasks: ['NEWS', 'amiibo'] + }, + { + bossAppId: 'o2Ug1pIp9Uhri6Nh', + titleId: BigInt(0x0005000010144F00), + titleRegion: 'USA', + name: 'Super Smash Bros. for Wii U', + tasks: ['amiibo', 'NEWS', 'friend', 'CONQ'] + }, + { + bossAppId: 'n6rAJ1nnfC1Sgcpl', + titleId: BigInt(0x0005000010145000), + titleRegion: 'EUR', + name: 'Super Smash Bros. for Wii U', + tasks: ['amiibo', 'NEWS', 'friend', 'CONQ'] + }, + { + bossAppId: 'CHUN6T1m7Xk4EBg4', + titleId: BigInt(0x00050000101DFF00), + titleRegion: 'JPN', + name: 'プチコンBIG (Petitcom BIG)', + tasks: ['ptcbnws'] + }, + { + bossAppId: 'zyXdCW9jGdi9rjaz', + titleId: BigInt(0x0005000010142200), + titleRegion: 'JPN', + name: 'NewスーパールイージU (New SUPER LUIGI U)', + tasks: ['news'] + }, + { + bossAppId: 'jPHLlJr2fJyTzffp', + titleId: BigInt(0x0005000010142300), + titleRegion: 'USA', + name: 'New SUPER LUIGI U', + tasks: ['news'] + }, + { + bossAppId: 'YsXB6IRGSI56tPxl', + titleId: BigInt(0x0005000010142400), + titleRegion: 'EUR', + name: 'New SUPER LUIGI U', + tasks: ['news'] + }, + { + bossAppId: 'Lbqp9Sg1i0xUzFFa', + titleId: BigInt(0x0005000010113800), + titleRegion: 'EUR', + name: 'Zen Pinball 2', + tasks: ['PTS'] + }, + { + bossAppId: 'DwU7n0FidGrLNiOo', + titleId: BigInt(0x000500001014D900), + titleRegion: 'JPN', + name: 'ぷよぷよテトリス (PUYOPUYOTETRIS)', + tasks: ['boss1', 'boss2', 'boss3'] + }, + { + bossAppId: 'yIUkFmuGVkGP8pDb', + titleId: BigInt(0x0005000010132200), + titleRegion: 'JPN', + name: '太鼓の達人 Wii Uば~じょん! (Taiko no Tatsujin Wii U version!)', + tasks: ['notice1'] + }, + { + bossAppId: 'v4WRObSzD7VU3dcJ', + titleId: BigInt(0x00050000101D3000), + titleRegion: 'JPN', + name: '太鼓の達人 あつめて★ともだち大作戦! (Taiko no Tatsujin Atsumete★ TomodachiDaisakusen!)', + tasks: ['notice1'] + }, + { + bossAppId: '3zDjXIA57bSceyaw', + titleId: BigInt(0x00050000101BEC00), + titleRegion: 'USA', + name: 'Star Fox Guard', + tasks: ['param'] + }, + { + bossAppId: 'NL38jhExI2CQqhWd', + titleId: BigInt(0x00050000101CDB00), + titleRegion: 'JPN', + name: 'Splatoon Pre-Launch Review', + tasks: ['schdata'] + }, + { + bossAppId: 'sE6KwEpQYyg6tdU7', + titleId: BigInt(0x00050000101CDC00), + titleRegion: 'USA', + name: 'Splatoon Pre-Launch Review', + tasks: ['schdata'] + }, + { + bossAppId: 'pTKZ9q5KrCP3gBag', + titleId: BigInt(0x00050000101CDD00), + titleRegion: 'EUR', + name: 'Splatoon Pre-Launch Review', + tasks: ['schdata'] + }, + { + bossAppId: 'CJT88RO008LAnD51', + titleId: BigInt(0x0005000010170600), + titleRegion: 'JPN', + name: '仮面ライダー バトライド・ウォーⅡ プレミアムTV&MOVIEサウンドED. (KAMEN RIDER BATTRIDE WAR Ⅱ PREMIUM TV&MOVIE SOUND ED.)', + tasks: ['PE_GAK', 'PE_ZNG'] + }, + { + bossAppId: 'FyyMFzEByuQJc6sJ', + titleId: BigInt(0x0005000010135200), + titleRegion: 'USA', + name: 'Star Wars Pinball', + tasks: ['PTS'] + }, + { + bossAppId: 'A4yyXWKZZUToFtrt', + titleId: BigInt(0x0005000010132A00), + titleRegion: 'EUR', + name: 'Star Wars Pinball', + tasks: ['PTS'] + }, + { + bossAppId: 'HauaFQ1sPsnQ6rBj', + titleId: BigInt(0x0005000010171F00), + titleRegion: 'USA', + name: 'Pushmo World', + tasks: ['annouce'] + }, + { + bossAppId: 'qDUeFmk0Az71nHyD', + titleId: BigInt(0x0005000010110900), + titleRegion: 'JPN', + name: 'NINJA GAIDEN 3: Razor\'s Edge', + tasks: ['DLCINFO'] + }, + { + bossAppId: 'yVsSPM2E0DEOxroT', + titleId: BigInt(0x0005000010110A00), + titleRegion: 'USA', + name: 'NINJA GAIDEN 3: Razor\'s Edge', + tasks: ['DLCINFO'] + }, + { + bossAppId: 'Xw6OvZkQofQ3O8Bi', + titleId: BigInt(0x0005000010110B00), + titleRegion: 'EUR', + name: 'Ninja Gaiden 3: Razor\'s Edge', + tasks: ['DLCINFO'] + }, + { + bossAppId: 'LUQX5swEjBUPQ8nR', + titleId: BigInt(0x0005000010110200), + titleRegion: 'USA', + name: 'WARRIORS OROCHI 3 Hyper(NA)', + tasks: ['OR2H000'] + }, + { + bossAppId: 'y4pXrgLe0JGao3No', + titleId: BigInt(0x0005000010112B00), + titleRegion: 'EUR', + name: 'WARRIORS OROCHI 3 Hyper(EU)', + tasks: ['OR2H000'] + }, + { + bossAppId: 'j01mRJ9sNe00MWPC', + titleId: BigInt(0x0005000010170700), + titleRegion: 'JPN', + name: '仮面ライダー バトライド・ウォーⅡ (KAMEN RIDER BATTRIDE WAR Ⅱ)', + tasks: ['CHR_GAK', 'CHR_ZNG'] + } + ] + }; +} diff --git a/src/services/grpc/boss/v2/list-tasks.ts b/src/services/grpc/boss/v2/list-tasks.ts new file mode 100644 index 0000000..5164e2d --- /dev/null +++ b/src/services/grpc/boss/v2/list-tasks.ts @@ -0,0 +1,22 @@ +import { getAllTasks } from '@/database'; +import type { ListTasksResponse } from '@pretendonetwork/grpc/boss/v2/list_tasks'; + +export async function listTasks(): Promise { + const tasks = await getAllTasks(false); + + return { + tasks: tasks.map(task => ({ + deleted: task.deleted, + id: task.id, + inGameId: task.in_game_id, + bossAppId: task.boss_app_id, + creatorPid: task.creator_pid, + status: task.status, + interval: 0, // TODO - Don't stub this + titleId: BigInt(parseInt(task.title_id, 16)), + description: task.description, + createdTimestamp: task.created, + updatedTimestamp: task.updated + })) + }; +} diff --git a/src/services/grpc/boss/v2/middleware/api-key-middleware.ts b/src/services/grpc/boss/v2/middleware/api-key-middleware.ts new file mode 100644 index 0000000..211c2cc --- /dev/null +++ b/src/services/grpc/boss/v2/middleware/api-key-middleware.ts @@ -0,0 +1,16 @@ +import { Status, ServerError } from 'nice-grpc'; +import { config } from '@/config-manager'; +import type { ServerMiddlewareCall, CallContext } from 'nice-grpc'; + +export async function* apiKeyMiddleware( + call: ServerMiddlewareCall, + context: CallContext +): AsyncGenerator { + const apiKey: string | undefined = context.metadata.get('X-API-Key'); + + if (!apiKey || apiKey !== config.grpc.boss.api_key) { + throw new ServerError(Status.UNAUTHENTICATED, 'Missing or invalid API key'); + } + + return yield* call.next(call.request, context); +} diff --git a/src/services/grpc/boss/v2/middleware/authentication-middleware.ts b/src/services/grpc/boss/v2/middleware/authentication-middleware.ts new file mode 100644 index 0000000..7456dd4 --- /dev/null +++ b/src/services/grpc/boss/v2/middleware/authentication-middleware.ts @@ -0,0 +1,52 @@ +import { Status, ServerError } from 'nice-grpc'; +import { getUserDataByToken } from '@/util'; +import type { ServerMiddlewareCall, CallContext } from 'nice-grpc'; +import type { GetUserDataResponse } from '@pretendonetwork/grpc/account/get_user_data_rpc'; +import type { PNIDPermissionFlags } from '@pretendonetwork/grpc/account/pnid_permission_flags'; + +export type AuthenticationCallContextExt = { + user: GetUserDataResponse | null; +}; + +export async function* authenticationMiddleware( + call: ServerMiddlewareCall, + context: CallContext +): AsyncGenerator { + const token: string | undefined = context.metadata.get('X-Token')?.trim(); + + try { + let user: GetUserDataResponse | null = null; + + if (token) { + user = await getUserDataByToken(token); + if (!user) { + throw new ServerError(Status.UNAUTHENTICATED, 'User could not be found'); + } + } + + return yield* call.next(call.request, { + ...context, + user + }); + } catch (error) { + let message: string = 'Unknown server error'; + + console.log(error); + + if (error instanceof Error) { + message = error.message; + } + + throw new ServerError(Status.INVALID_ARGUMENT, message); + } +} + +export function hasPermission(ctx: AuthenticationCallContextExt, perm: keyof PNIDPermissionFlags): boolean { + if (!ctx.user) { + return true; // Non users are always allowed + } + if (!ctx.user.permissions) { + return false; // No permissions, no entry + } + return ctx.user.permissions[perm]; +} diff --git a/src/services/grpc/boss/v2/register-task.ts b/src/services/grpc/boss/v2/register-task.ts new file mode 100644 index 0000000..e39df3e --- /dev/null +++ b/src/services/grpc/boss/v2/register-task.ts @@ -0,0 +1,78 @@ +import { ServerError, Status } from 'nice-grpc'; +import { getTask } from '@/database'; +import { Task } from '@/models/task'; +import { hasPermission } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { CallContext } from 'nice-grpc'; +import type { RegisterTaskRequest, RegisterTaskResponse } from '@pretendonetwork/grpc/boss/v2/register_task'; + +const BOSS_APP_ID_FILTER_REGEX = /^[A-Za-z0-9]*$/; + +export async function registerTask(request: RegisterTaskRequest, context: CallContext & AuthenticationCallContextExt): Promise { + if (!hasPermission(context, 'createBossTasks')) { + throw new ServerError(Status.PERMISSION_DENIED, 'PNID not authorized to register new tasks'); + } + + const taskID = request.id.trim(); + const bossAppID = request.bossAppId.trim(); + const titleID = request.titleId.toString(16).toLowerCase().padStart(16, '0'); + const description = request.description.trim(); + + if (!taskID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing task ID'); + } + + if (!bossAppID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing BOSS app ID'); + } + + if (bossAppID.length !== 16) { + throw new ServerError(Status.INVALID_ARGUMENT, 'BOSS app ID must be 16 characters'); + } + + if (!BOSS_APP_ID_FILTER_REGEX.test(bossAppID)) { + throw new ServerError(Status.INVALID_ARGUMENT, 'BOSS app ID must only contain letters and numbers'); + } + + if (await getTask(bossAppID, taskID)) { + throw new ServerError(Status.ALREADY_EXISTS, `Task ${taskID} already exists for BOSS app ${bossAppID}`); + } + + // * BOSS tasks have 2 IDs + // * - 1: The ID which is registered in-game + // * - 2: The ID which is registered on the server + // * The in-game task ID can be any length, but the + // * ID registered on the server is capped at 7 characters. + // * When querying tasks in the API, the server ignores + // * all characters after the 7th. For example, Splatoon + // * registers task optdata2 in-game, but the server + // * tracks it as task optdata + + const task = await Task.create({ + id: taskID.slice(0, 7), + in_game_id: taskID, + boss_app_id: bossAppID, + creator_pid: context.user?.pid, + status: 'open', // TODO - Make this configurable + title_id: titleID, + description: description, + created: Date.now(), + updated: Date.now() + }); + + return { + task: { + deleted: task.deleted, + id: task.id, + inGameId: task.in_game_id, + bossAppId: task.boss_app_id, + creatorPid: task.creator_pid, + status: task.status, + interval: 0, // TODO - Don't stub this + titleId: BigInt(parseInt(task.title_id, 16)), + description: task.description, + createdTimestamp: task.created, + updatedTimestamp: task.updated + } + }; +} diff --git a/src/services/grpc/boss/v2/update-file-metadata-wup.ts b/src/services/grpc/boss/v2/update-file-metadata-wup.ts new file mode 100644 index 0000000..4fd7e72 --- /dev/null +++ b/src/services/grpc/boss/v2/update-file-metadata-wup.ts @@ -0,0 +1,59 @@ +import { Status, ServerError } from 'nice-grpc'; +import { getTaskFileByDataID } from '@/database'; +import { isValidFileNotifyCondition, isValidFileType } from '@/util'; +import { hasPermission } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { CallContext } from 'nice-grpc'; +import type { UpdateFileMetadataWUPRequest } from '@pretendonetwork/grpc/boss/v2/update_file_metadata_wup'; +import type { Empty } from '@pretendonetwork/grpc/google/protobuf/empty'; + +export async function updateFileMetadataWUP(request: UpdateFileMetadataWUPRequest, context: CallContext & AuthenticationCallContextExt): Promise { + if (!hasPermission(context, 'updateBossFiles')) { + throw new ServerError(Status.PERMISSION_DENIED, 'PNID not authorized to update file metadata'); + } + + const dataID = request.dataId; + const updateData = request.updateData; + + if (!dataID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing file data ID'); + } + + if (!updateData) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing file update data'); + } + + const file = await getTaskFileByDataID(dataID); + + if (!file || file.deleted) { + throw new ServerError(Status.INVALID_ARGUMENT, `File ${dataID} not found`); + } + + if (!isValidFileType(updateData.type)) { + throw new ServerError(Status.INVALID_ARGUMENT, `${updateData.type} is not a valid type`); + } + + for (const notifyCondition of updateData.notifyOnNew) { + if (!isValidFileNotifyCondition(notifyCondition)) { + throw new ServerError(Status.INVALID_ARGUMENT, `${notifyCondition} is not a valid notify condition`); + } + } + + file.task_id = updateData.taskId.slice(0, 7); + file.boss_app_id = updateData.bossAppId; + file.supported_countries = updateData.supportedCountries; + file.supported_languages = updateData.supportedLanguages; + file.password = updateData.attributes ? updateData.attributes.description : file.password; + file.attribute1 = updateData.attributes ? updateData.attributes.attribute1 : file.attribute1; + file.attribute2 = updateData.attributes ? updateData.attributes.attribute2 : file.attribute2; + file.attribute3 = updateData.attributes ? updateData.attributes.attribute3 : file.attribute3; + file.name = updateData.name; + file.type = updateData.type; + file.notify_on_new = updateData.notifyOnNew; + file.notify_led = updateData.notifyLed; + file.updated = BigInt(Date.now()); + + await file.save(); + + return {}; +} diff --git a/src/services/grpc/boss/v2/update-task.ts b/src/services/grpc/boss/v2/update-task.ts new file mode 100644 index 0000000..34cd712 --- /dev/null +++ b/src/services/grpc/boss/v2/update-task.ts @@ -0,0 +1,54 @@ +import { Status, ServerError } from 'nice-grpc'; +import { getTask } from '@/database'; +import { hasPermission } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { CallContext } from 'nice-grpc'; +import type { UpdateTaskRequest } from '@pretendonetwork/grpc/boss/v2/update_task'; +import type { Empty } from '@pretendonetwork/grpc/google/protobuf/empty'; + +export async function updateTask(request: UpdateTaskRequest, context: CallContext & AuthenticationCallContextExt): Promise { + if (!hasPermission(context, 'updateBossTasks')) { + throw new ServerError(Status.PERMISSION_DENIED, 'PNID not authorized to update tasks'); + } + + const taskID = request.id.trim(); + const bossAppID = request.bossAppId.trim(); + const updateData = request.updateData; + + if (!taskID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing task ID'); + } + + if (!bossAppID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing BOSS app ID'); + } + + if (!updateData) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing task update data'); + } + + const task = await getTask(bossAppID, taskID); + + if (!task) { + throw new ServerError(Status.INVALID_ARGUMENT, `Task ${taskID} not found for BOSS app ${bossAppID}`); + } + + if (updateData.status !== 'open') { + throw new ServerError(Status.INVALID_ARGUMENT, `Status ${updateData.status} is invalid`); + } + + if (updateData.id) { + task.id = updateData.id.slice(0, 7); + task.in_game_id = updateData.id; + } + + task.boss_app_id = updateData.bossAppId ? updateData.bossAppId : task.boss_app_id; + task.title_id = updateData.titleId ? updateData.titleId.toString(16).toLowerCase().padStart(16, '0') : task.title_id; + task.status = updateData.status ? updateData.status : task.status; + task.description = updateData.description ? updateData.description : task.description; + task.updated = BigInt(Date.now()); + + await task.save(); + + return {}; +} diff --git a/src/services/grpc/boss/v2/upload-file-wup.ts b/src/services/grpc/boss/v2/upload-file-wup.ts new file mode 100644 index 0000000..26959b0 --- /dev/null +++ b/src/services/grpc/boss/v2/upload-file-wup.ts @@ -0,0 +1,174 @@ +import { Status, ServerError } from 'nice-grpc'; +import { encryptWiiU } from '@pretendonetwork/boss-crypto'; +import { isValidCountryCode, isValidFileNotifyCondition, isValidFileType, isValidLanguage, md5 } from '@/util'; +import { getTask, getTaskFile } from '@/database'; +import { File } from '@/models/file'; +import { config } from '@/config-manager'; +import { uploadCDNFile } from '@/cdn'; +import { hasPermission } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { CallContext } from 'nice-grpc'; +import type { UploadFileWUPRequest, UploadFileWUPResponse } from '@pretendonetwork/grpc/boss/v2/upload_file_wup'; + +const BOSS_APP_ID_FILTER_REGEX = /^[A-Za-z0-9]*$/; + +export async function uploadFileWUP(request: UploadFileWUPRequest, context: CallContext & AuthenticationCallContextExt): Promise { + if (!hasPermission(context, 'uploadBossFiles')) { + throw new ServerError(Status.PERMISSION_DENIED, 'PNID not authorized to upload new files'); + } + + const taskID = request.taskId.trim(); + const bossAppID = request.bossAppId.trim(); + const supportedCountries = request.supportedCountries; + const supportedLanguages = request.supportedLanguages; + const name = request.name.trim(); + const type = request.type.trim(); + const notifyOnNew = [...new Set(request.notifyOnNew)]; + const notifyLed = request.notifyLed; + const data = request.data; + const nameEqualsDataID = request.nameEqualsDataId; + + if (!taskID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing task ID'); + } + + if (!bossAppID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing BOSS app ID'); + } + + if (bossAppID.length !== 16) { + throw new ServerError(Status.INVALID_ARGUMENT, 'BOSS app ID must be 16 characters'); + } + + if (!BOSS_APP_ID_FILTER_REGEX.test(bossAppID)) { + throw new ServerError(Status.INVALID_ARGUMENT, 'BOSS app ID must only contain letters and numbers'); + } + + if (!(await getTask(bossAppID, taskID))) { + throw new ServerError(Status.NOT_FOUND, `Task ${taskID} does not exist for BOSS app ${bossAppID}`); + } + + for (const country of supportedCountries) { + if (!isValidCountryCode(country)) { + throw new ServerError(Status.INVALID_ARGUMENT, `${country} is not a valid country`); + } + } + + for (const language of supportedLanguages) { + if (!isValidLanguage(language)) { + throw new ServerError(Status.INVALID_ARGUMENT, `${language} is not a valid language`); + } + } + + if (!name && !nameEqualsDataID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Must provide a file name is enable nameEqualsDataId'); + } + + if (!isValidFileType(type)) { + throw new ServerError(Status.INVALID_ARGUMENT, `${type} is not a valid type`); + } + + for (const notifyCondition of notifyOnNew) { + if (!isValidFileNotifyCondition(notifyCondition)) { + throw new ServerError(Status.INVALID_ARGUMENT, `${notifyCondition} is not a valid notify condition`); + } + } + + if (data.length === 0) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Cannot upload empty file'); + } + + let encryptedData: Buffer; + + try { + encryptedData = encryptWiiU(data, config.crypto.wup.aes_key, config.crypto.wup.hmac_key); + } catch (error: unknown) { + let message = 'Unknown file encryption error'; + + if (error instanceof Error) { + message = error.message; + } + + throw new ServerError(Status.ABORTED, message); + } + + const contentHash = md5(encryptedData); + + // * Upload file first to prevent ghost DB entries on upload failures + const key = `${bossAppID}/${taskID}/${contentHash}`; + try { + // * Some tasks have file names which are dynamic. + // * They change depending on the files data ID. + // * Because of this, using the file name in the + // * upload key is not viable, as it is not always + // * known during upload + await uploadCDNFile('taskFile', key, encryptedData); + } catch (error: unknown) { + let message = 'Unknown file upload error'; + + if (error instanceof Error) { + message = error.message; + } + + throw new ServerError(Status.ABORTED, message); + } + + let file = await getTaskFile(bossAppID, taskID, name); + + if (file) { + file.deleted = true; + file.updated = BigInt(Date.now()); + + await file.save(); + } + + file = await File.create({ + task_id: taskID.slice(0, 7), + boss_app_id: bossAppID, + file_key: key, + supported_countries: supportedCountries, + supported_languages: supportedLanguages, + creator_pid: context.user?.pid, + name: name, + type: type, + hash: contentHash, + size: BigInt(encryptedData.length), + notify_on_new: notifyOnNew, + notify_led: notifyLed, + created: Date.now(), + updated: Date.now() + }); + + if (nameEqualsDataID) { + file.name = file.data_id.toString(16).padStart(8, '0'); + await file.save(); + } + + return { + file: { + deleted: file.deleted, + dataId: file.data_id, + taskId: file.task_id, + bossAppId: file.boss_app_id, + supportedCountries: file.supported_countries, + supportedLanguages: file.supported_languages, + attributes: { + attribute1: file.attribute1, + attribute2: file.attribute2, + attribute3: file.attribute3, + description: file.password + }, + creatorPid: file.creator_pid, + name: file.name, + type: file.type, + hash: file.hash, + size: file.size, + notifyOnNew: file.notify_on_new, + notifyLed: file.notify_led, + conditionPlayed: 0n, // TODO - Don't stub this + autoDelete: false, // TODO - Don't stub this + createdTimestamp: file.created, + updatedTimestamp: file.updated + } + }; +} diff --git a/src/services/grpc/server.ts b/src/services/grpc/server.ts index 2c09231..9696505 100644 --- a/src/services/grpc/server.ts +++ b/src/services/grpc/server.ts @@ -3,6 +3,12 @@ import { BOSSDefinition as BossServiceDefinitionV1 } from '@pretendonetwork/grpc import { apiKeyMiddleware as apiKeyMiddlewareV1 } from '@/services/grpc/boss/v1/middleware/api-key-middleware'; import { authenticationMiddleware as authenticationMiddlewareV1 } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; import { bossServiceImplementationV1 } from '@/services/grpc/boss/v1/implementation'; + +import { BossServiceDefinition as BossServiceDefinitionV2 } from '@pretendonetwork/grpc/boss/v2/boss_service'; +import { apiKeyMiddleware as apiKeyMiddlewareV2 } from '@/services/grpc/boss/v2/middleware/api-key-middleware'; +import { authenticationMiddleware as authenticationMiddlewareV2 } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import { bossServiceImplementationV2 } from '@/services/grpc/boss/v2/implementation'; + import { config } from '@/config-manager'; import type { Server } from 'nice-grpc'; @@ -10,6 +16,7 @@ export async function startGRPCServer(): Promise { const server: Server = createServer(); server.with(apiKeyMiddlewareV1).with(authenticationMiddlewareV1).add(BossServiceDefinitionV1, bossServiceImplementationV1); + server.with(apiKeyMiddlewareV2).with(authenticationMiddlewareV2).add(BossServiceDefinitionV2, bossServiceImplementationV2); await server.listen(`${config.grpc.boss.address}:${config.grpc.boss.port}`); } From 0078fbf69005d91a5ca9fbab73788d59e5e50239 Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Sun, 19 Oct 2025 18:08:41 -0400 Subject: [PATCH 04/30] chore: bump @pretendonetwork/grpc --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8f23ae2..1b79212 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@aws-sdk/client-s3": "^3.723.0", "@pretendonetwork/boss-crypto": "^1.0.0", - "@pretendonetwork/grpc": "^2.2.4", + "@pretendonetwork/grpc": "^2.3.2", "@typegoose/auto-increment": "^4.13.0", "commander": "^14.0.0", "cron": "^4.3.3", @@ -2063,9 +2063,9 @@ } }, "node_modules/@pretendonetwork/grpc": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.2.4.tgz", - "integrity": "sha512-fqhKbc7QFGtMyap5wxCPXmBH+ydPy6W4gAmlDS1wLTkE/Q4o7QVcrXGv+WMRzL8dsHpLqKNnhB1hm6K8Sbn80w==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.2.tgz", + "integrity": "sha512-gnSHLHkYVgxb64chdvQGuKfAz14l6KWrjG0ch7l6YwRhnuVYMZNeUM7FNQ0zjwFKo0VAW1uI8UzGnWdzq50MOA==", "license": "AGPL-3.0-only", "dependencies": { "@bufbuild/protobuf": "^2.2.2", @@ -11304,9 +11304,9 @@ } }, "@pretendonetwork/grpc": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.2.4.tgz", - "integrity": "sha512-fqhKbc7QFGtMyap5wxCPXmBH+ydPy6W4gAmlDS1wLTkE/Q4o7QVcrXGv+WMRzL8dsHpLqKNnhB1hm6K8Sbn80w==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.2.tgz", + "integrity": "sha512-gnSHLHkYVgxb64chdvQGuKfAz14l6KWrjG0ch7l6YwRhnuVYMZNeUM7FNQ0zjwFKo0VAW1uI8UzGnWdzq50MOA==", "requires": { "@bufbuild/protobuf": "^2.2.2", "nice-grpc-common": "^2.0.2", diff --git a/package.json b/package.json index 051820a..64e2759 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "dependencies": { "@aws-sdk/client-s3": "^3.723.0", "@pretendonetwork/boss-crypto": "^1.0.0", - "@pretendonetwork/grpc": "^2.2.4", + "@pretendonetwork/grpc": "^2.3.2", "@typegoose/auto-increment": "^4.13.0", "commander": "^14.0.0", "cron": "^4.3.3", From 9be53c03825769cdb00faef4817117b55b958ada Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Sun, 19 Oct 2025 18:09:04 -0400 Subject: [PATCH 05/30] chore: bump @pretendonetwork/boss-crypto --- package-lock.json | 15 ++++++++------- package.json | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1b79212..633d796 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "AGPL-3.0-only", "dependencies": { "@aws-sdk/client-s3": "^3.723.0", - "@pretendonetwork/boss-crypto": "^1.0.0", + "@pretendonetwork/boss-crypto": "^1.1.2", "@pretendonetwork/grpc": "^2.3.2", "@typegoose/auto-increment": "^4.13.0", "commander": "^14.0.0", @@ -2039,9 +2039,10 @@ } }, "node_modules/@pretendonetwork/boss-crypto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@pretendonetwork/boss-crypto/-/boss-crypto-1.0.0.tgz", - "integrity": "sha512-ybd3sB356v5Azxj99R62+7kytgAzfUYuXRJbdOznGL6infgCJ056TjTadN4V48m7t+3f6sPOUgo9YWUFNxlLLg==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@pretendonetwork/boss-crypto/-/boss-crypto-1.1.2.tgz", + "integrity": "sha512-3rDp29vAmOPvQ7L/Vyqon52Wqqz2dvhDdHAOqxruHo5Ag2k0wAnIDBNq5NaTGW+H1OYzkouIR/9nnaOmY1GHXQ==", + "license": "LGPL-3.0-only" }, "node_modules/@pretendonetwork/eslint-config": { "version": "0.1.1", @@ -11283,9 +11284,9 @@ "optional": true }, "@pretendonetwork/boss-crypto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@pretendonetwork/boss-crypto/-/boss-crypto-1.0.0.tgz", - "integrity": "sha512-ybd3sB356v5Azxj99R62+7kytgAzfUYuXRJbdOznGL6infgCJ056TjTadN4V48m7t+3f6sPOUgo9YWUFNxlLLg==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@pretendonetwork/boss-crypto/-/boss-crypto-1.1.2.tgz", + "integrity": "sha512-3rDp29vAmOPvQ7L/Vyqon52Wqqz2dvhDdHAOqxruHo5Ag2k0wAnIDBNq5NaTGW+H1OYzkouIR/9nnaOmY1GHXQ==" }, "@pretendonetwork/eslint-config": { "version": "0.1.1", diff --git a/package.json b/package.json index 64e2759..dc002ac 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.723.0", - "@pretendonetwork/boss-crypto": "^1.0.0", + "@pretendonetwork/boss-crypto": "^1.1.2", "@pretendonetwork/grpc": "^2.3.2", "@typegoose/auto-increment": "^4.13.0", "commander": "^14.0.0", From 436a8eda7427d9b494b38b0f4d321928366a3b85 Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Sun, 19 Oct 2025 18:09:23 -0400 Subject: [PATCH 06/30] chore: npm audit fix --- package-lock.json | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 633d796..bffa542 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2028,6 +2028,12 @@ "@noble/hashes": "^1.1.5" } }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -5816,15 +5822,6 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "node_modules/fast-redact": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", - "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -7827,13 +7824,13 @@ } }, "node_modules/pino": { - "version": "9.9.1", - "resolved": "https://registry.npmjs.org/pino/-/pino-9.9.1.tgz", - "integrity": "sha512-40SszWPOPwGhUIJ3zj0PsbMNV1bfg8nw5Qp/tP2FE2p3EuycmhDeYimKOMBAu6rtxcSw2QpjJsuK5A6v+en8Yw==", + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", + "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", "license": "MIT", "dependencies": { + "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", @@ -11276,6 +11273,11 @@ "@noble/hashes": "^1.1.5" } }, + "@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==" + }, "@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -13891,11 +13893,6 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "fast-redact": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", - "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==" - }, "fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", @@ -15205,12 +15202,12 @@ "dev": true }, "pino": { - "version": "9.9.1", - "resolved": "https://registry.npmjs.org/pino/-/pino-9.9.1.tgz", - "integrity": "sha512-40SszWPOPwGhUIJ3zj0PsbMNV1bfg8nw5Qp/tP2FE2p3EuycmhDeYimKOMBAu6rtxcSw2QpjJsuK5A6v+en8Yw==", + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", + "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", "requires": { + "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", From 4c07a01ed77d28b51e759da2f224b78b7ab54066 Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Mon, 20 Oct 2025 14:13:56 -0400 Subject: [PATCH 07/30] feat: full upgrade to boss grpc v2 --- src/database.ts | 111 ++++++++++-- src/models/file-ctr.ts | 48 +++++ src/models/{file.ts => file-wup.ts} | 14 +- src/services/grpc/boss/v1/delete-file.ts | 4 +- src/services/grpc/boss/v1/list-files.ts | 4 +- .../grpc/boss/v1/update-file-metadata.ts | 4 +- src/services/grpc/boss/v1/upload-file.ts | 8 +- src/services/grpc/boss/v2/delete-file.ts | 15 +- src/services/grpc/boss/v2/implementation.ts | 8 +- src/services/grpc/boss/v2/list-files-ctr.ts | 28 +-- src/services/grpc/boss/v2/list-files-wup.ts | 8 +- .../grpc/boss/v2/update-file-metadata-ctr.ts | 55 ++++++ .../grpc/boss/v2/update-file-metadata-wup.ts | 6 +- src/services/grpc/boss/v2/upload-file-ctr.ts | 168 ++++++++++++++++++ src/services/grpc/boss/v2/upload-file-wup.ts | 15 +- src/services/npdi.ts | 4 +- src/services/npdl.ts | 4 +- src/services/npfl.ts | 4 +- src/services/npts.ts | 10 +- src/types/mongoose/file-ctr.ts | 41 +++++ src/types/mongoose/{file.ts => file-wup.ts} | 12 +- 21 files changed, 496 insertions(+), 75 deletions(-) create mode 100644 src/models/file-ctr.ts rename src/models/{file.ts => file-wup.ts} (58%) create mode 100644 src/services/grpc/boss/v2/update-file-metadata-ctr.ts create mode 100644 src/services/grpc/boss/v2/upload-file-ctr.ts create mode 100644 src/types/mongoose/file-ctr.ts rename src/types/mongoose/{file.ts => file-wup.ts} (60%) diff --git a/src/database.ts b/src/database.ts index 0c41186..5161e58 100644 --- a/src/database.ts +++ b/src/database.ts @@ -2,12 +2,14 @@ import mongoose from 'mongoose'; import { CECData } from '@/models/cec-data'; import { CECSlot } from '@/models/cec-slot'; import { Task } from '@/models/task'; -import { File } from '@/models/file'; +import { FileCTR } from '@/models/file-ctr'; +import { FileWUP } from '@/models/file-wup'; import { config } from '@/config-manager'; import type { HydratedCECDataDocument } from '@/types/mongoose/cec-data'; import type { HydratedCECSlotDocument, ICECSlot } from '@/types/mongoose/cec-slot'; import type { HydratedTaskDocument, ITask } from '@/types/mongoose/task'; -import type { HydratedFileDocument, IFile } from '@/types/mongoose/file'; +import type { HydratedFileCTRDocument, IFileCTR } from '@/types/mongoose/file-ctr'; +import type { HydratedFileWUPDocument, IFileWUP } from '@/types/mongoose/file-wup'; const connection_string: string = config.mongoose.connection_string; @@ -52,10 +54,10 @@ export function getTask(bossAppID: string, taskID: string): Promise { +export function getCTRTaskFiles(allowDeleted: boolean, bossAppID: string, taskID: string, country?: string, language?: string): Promise { verifyConnected(); - const filter: mongoose.FilterQuery = { + const filter: mongoose.FilterQuery = { task_id: taskID.slice(0, 7), boss_app_id: bossAppID, $and: [] @@ -87,13 +89,51 @@ export function getTaskFiles(allowDeleted: boolean, bossAppID: string, taskID: s delete filter.$and; } - return File.find(filter); + return FileCTR.find(filter); } -export function getTaskFilesWithAttributes(allowDeleted: boolean, bossAppID: string, taskID: string, country?: string, language?: string, attribute1?: string, attribute2?: string, attribute3?: string): Promise { +export function getWUPTaskFiles(allowDeleted: boolean, bossAppID: string, taskID: string, country?: string, language?: string): Promise { verifyConnected(); - const filter: mongoose.FilterQuery = { + const filter: mongoose.FilterQuery = { + task_id: taskID.slice(0, 7), + boss_app_id: bossAppID, + $and: [] + }; + + if (!allowDeleted) { + filter.deleted = false; + } + + if (country) { + filter.$and?.push({ + $or: [ + { supported_countries: { $eq: [] } }, + { supported_countries: country } + ] + }); + } + + if (language) { + filter.$and?.push({ + $or: [ + { supported_languages: { $eq: [] } }, + { supported_languages: language } + ] + }); + } + + if (filter.$and?.length === 0) { + delete filter.$and; + } + + return FileWUP.find(filter); +} + +export function getCTRTaskFilesWithAttributes(allowDeleted: boolean, bossAppID: string, taskID: string, country?: string, language?: string, attribute1?: string, attribute2?: string, attribute3?: string): Promise { + verifyConnected(); + + const filter: mongoose.FilterQuery = { task_id: taskID.slice(0, 7), boss_app_id: bossAppID, $and: [] @@ -137,13 +177,13 @@ export function getTaskFilesWithAttributes(allowDeleted: boolean, bossAppID: str delete filter.$and; } - return File.find(filter); + return FileWUP.find(filter); } -export function getTaskFile(bossAppID: string, taskID: string, name: string, country?: string, language?: string): Promise { +export function getCTRTaskFile(bossAppID: string, taskID: string, name: string, country?: string, language?: string): Promise { verifyConnected(); - const filter: mongoose.FilterQuery = { + const filter: mongoose.FilterQuery = { deleted: false, boss_app_id: bossAppID, task_id: taskID.slice(0, 7), @@ -173,13 +213,58 @@ export function getTaskFile(bossAppID: string, taskID: string, name: string, cou delete filter.$and; } - return File.findOne(filter); + return FileCTR.findOne(filter); } -export function getTaskFileByDataID(dataID: bigint): Promise { +export function getWUPTaskFile(bossAppID: string, taskID: string, name: string, country?: string, language?: string): Promise { verifyConnected(); - return File.findOne({ + const filter: mongoose.FilterQuery = { + deleted: false, + boss_app_id: bossAppID, + task_id: taskID.slice(0, 7), + name: name, + $and: [] + }; + + if (country) { + filter.$and?.push({ + $or: [ + { supported_countries: { $eq: [] } }, + { supported_countries: country } + ] + }); + } + + if (language) { + filter.$and?.push({ + $or: [ + { supported_languages: { $eq: [] } }, + { supported_languages: language } + ] + }); + } + + if (filter.$and?.length === 0) { + delete filter.$and; + } + + return FileWUP.findOne(filter); +} + +export function getCTRTaskFileBySerialNumber(serialNumber: number): Promise { + verifyConnected(); + + return FileCTR.findOne({ + deleted: false, + serial_number: serialNumber + }); +} + +export function getWUPTaskFileByDataID(dataID: bigint): Promise { + verifyConnected(); + + return FileWUP.findOne({ deleted: false, data_id: Number(dataID) }); diff --git a/src/models/file-ctr.ts b/src/models/file-ctr.ts new file mode 100644 index 0000000..dfcbdd7 --- /dev/null +++ b/src/models/file-ctr.ts @@ -0,0 +1,48 @@ +import mongoose from 'mongoose'; +import { AutoIncrementID } from '@typegoose/auto-increment'; +import type { IFileCTR, IFileCTRMethods, FileCTRModel } from '@/types/mongoose/file-ctr'; + +const FileCTRSchema = new mongoose.Schema({ + deleted: { + type: Boolean, + default: false + }, + creator_pid: Number, + hash: String, + file_key: String, + size: BigInt, + task_id: String, + boss_app_id: String, + supported_countries: [String], + supported_languages: [String], + attributes: { + attribute1: String, + attribute2: String, + attribute3: String, + description: String + }, + name: String, + serial_number: Number, // * This is effectively the predecessor of the Wii U DataID. TODO - 3DBrew says this is a uint64? + payload_contents: [{ + title_id: BigInt, + content_datatype: Number, + ns_data_id: Number, // * Should payload contents be put in their own collection with their own autoincrementing IDs? + version: Number, + size: Number + }], + flags: { + mark_arrived_privileged: Boolean + }, + created: BigInt, + updated: BigInt +}, { id: false }); + +FileCTRSchema.plugin(AutoIncrementID, { + startAt: 50000, // * Start very high to avoid conflicts with Nintendo Data IDs + field: 'serial_number' +}); + +FileCTRSchema.index({ task_id: 1, boss_app_id: 1 }); +FileCTRSchema.index({ task_id: 1, boss_app_id: 1, name: 1 }); + +export const FileCTR = mongoose.model('FileCTR', FileCTRSchema, 'files-ctr'); diff --git a/src/models/file.ts b/src/models/file-wup.ts similarity index 58% rename from src/models/file.ts rename to src/models/file-wup.ts index d918cb8..b329a18 100644 --- a/src/models/file.ts +++ b/src/models/file-wup.ts @@ -1,8 +1,8 @@ import mongoose from 'mongoose'; import { AutoIncrementID } from '@typegoose/auto-increment'; -import type { IFile, IFileMethods, FileModel } from '@/types/mongoose/file'; +import type { IFileWUP, IFileWUPMethods, FileWUPModel } from '@/types/mongoose/file-wup'; -const FileSchema = new mongoose.Schema({ +const FileWUPSchema = new mongoose.Schema({ deleted: { type: Boolean, default: false @@ -24,16 +24,18 @@ const FileSchema = new mongoose.Schema({ size: BigInt, notify_on_new: [String], notify_led: Boolean, + condition_played: BigInt, + auto_delete: Boolean, // * We don't know what this does, but it exists on WUP tasks. So track it created: BigInt, updated: BigInt }, { id: false }); -FileSchema.plugin(AutoIncrementID, { +FileWUPSchema.plugin(AutoIncrementID, { startAt: 50000, // * Start very high to avoid conflicts with Nintendo Data IDs field: 'data_id' }); -FileSchema.index({ task_id: 1, boss_app_id: 1 }); -FileSchema.index({ task_id: 1, boss_app_id: 1, name: 1 }); +FileWUPSchema.index({ task_id: 1, boss_app_id: 1 }); +FileWUPSchema.index({ task_id: 1, boss_app_id: 1, name: 1 }); -export const File = mongoose.model('File', FileSchema); +export const FileWUP = mongoose.model('FileWUP', FileWUPSchema, 'files-wup'); diff --git a/src/services/grpc/boss/v1/delete-file.ts b/src/services/grpc/boss/v1/delete-file.ts index e1da284..0947aad 100644 --- a/src/services/grpc/boss/v1/delete-file.ts +++ b/src/services/grpc/boss/v1/delete-file.ts @@ -1,5 +1,5 @@ import { Status, ServerError } from 'nice-grpc'; -import { getTaskFileByDataID } from '@/database'; +import { getWUPTaskFileByDataID } from '@/database'; import { hasPermission } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; import type { CallContext } from 'nice-grpc'; @@ -18,7 +18,7 @@ export async function deleteFile(request: DeleteFileRequest, context: CallContex throw new ServerError(Status.INVALID_ARGUMENT, 'Missing file data ID'); } - const file = await getTaskFileByDataID(dataID); + const file = await getWUPTaskFileByDataID(dataID); if (!file || file.boss_app_id !== bossAppID) { throw new ServerError(Status.INVALID_ARGUMENT, `File ${dataID} not found for BOSS app ${bossAppID}`); diff --git a/src/services/grpc/boss/v1/list-files.ts b/src/services/grpc/boss/v1/list-files.ts index 112caa9..d71f819 100644 --- a/src/services/grpc/boss/v1/list-files.ts +++ b/src/services/grpc/boss/v1/list-files.ts @@ -1,6 +1,6 @@ import { Status, ServerError } from 'nice-grpc'; import { isValidCountryCode, isValidLanguage } from '@/util'; -import { getTaskFiles } from '@/database'; +import { getWUPTaskFiles } from '@/database'; import type { ListFilesRequest, ListFilesResponse } from '@pretendonetwork/grpc/boss/list_files'; const BOSS_APP_ID_FILTER_REGEX = /^[A-Za-z0-9]*$/; @@ -35,7 +35,7 @@ export async function listFiles(request: ListFilesRequest): Promise ({ diff --git a/src/services/grpc/boss/v1/update-file-metadata.ts b/src/services/grpc/boss/v1/update-file-metadata.ts index 0a23437..406fe38 100644 --- a/src/services/grpc/boss/v1/update-file-metadata.ts +++ b/src/services/grpc/boss/v1/update-file-metadata.ts @@ -1,5 +1,5 @@ import { Status, ServerError } from 'nice-grpc'; -import { getTaskFileByDataID } from '@/database'; +import { getWUPTaskFileByDataID } from '@/database'; import { isValidFileNotifyCondition, isValidFileType } from '@/util'; import { hasPermission } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; @@ -23,7 +23,7 @@ export async function updateFileMetadata(request: UpdateFileMetadataRequest, con throw new ServerError(Status.INVALID_ARGUMENT, 'Missing file update data'); } - const file = await getTaskFileByDataID(dataID); + const file = await getWUPTaskFileByDataID(dataID); if (!file || file.deleted) { throw new ServerError(Status.INVALID_ARGUMENT, `File ${dataID} not found`); diff --git a/src/services/grpc/boss/v1/upload-file.ts b/src/services/grpc/boss/v1/upload-file.ts index be7d873..6fc63f6 100644 --- a/src/services/grpc/boss/v1/upload-file.ts +++ b/src/services/grpc/boss/v1/upload-file.ts @@ -1,8 +1,8 @@ import { Status, ServerError } from 'nice-grpc'; import { encryptWiiU } from '@pretendonetwork/boss-crypto'; import { isValidCountryCode, isValidFileNotifyCondition, isValidFileType, isValidLanguage, md5 } from '@/util'; -import { getTask, getTaskFile } from '@/database'; -import { File } from '@/models/file'; +import { getTask, getWUPTaskFile } from '@/database'; +import { FileWUP } from '@/models/file-wup'; import { config } from '@/config-manager'; import { uploadCDNFile } from '@/cdn'; import { hasPermission } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; @@ -113,7 +113,7 @@ export async function uploadFile(request: UploadFileRequest, context: CallContex throw new ServerError(Status.ABORTED, message); } - let file = await getTaskFile(bossAppID, taskID, name); + let file = await getWUPTaskFile(bossAppID, taskID, name); if (file) { file.deleted = true; @@ -122,7 +122,7 @@ export async function uploadFile(request: UploadFileRequest, context: CallContex await file.save(); } - file = await File.create({ + file = await FileWUP.create({ task_id: taskID.slice(0, 7), boss_app_id: bossAppID, file_key: key, diff --git a/src/services/grpc/boss/v2/delete-file.ts b/src/services/grpc/boss/v2/delete-file.ts index 462194e..740976c 100644 --- a/src/services/grpc/boss/v2/delete-file.ts +++ b/src/services/grpc/boss/v2/delete-file.ts @@ -1,10 +1,13 @@ import { Status, ServerError } from 'nice-grpc'; -import { getTaskFileByDataID } from '@/database'; +import { getCTRTaskFileBySerialNumber, getWUPTaskFileByDataID } from '@/database'; import { hasPermission } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import { PlatformType } from '@pretendonetwork/grpc/boss/v2/platform_type'; import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; import type { CallContext } from 'nice-grpc'; import type { DeleteFileRequest } from '@pretendonetwork/grpc/boss/v2/delete_file'; import type { Empty } from '@pretendonetwork/grpc/google/protobuf/empty'; +import type { HydratedFileCTRDocument } from '@/types/mongoose/file-ctr'; +import type { HydratedFileWUPDocument } from '@/types/mongoose/file-wup'; export async function deleteFile(request: DeleteFileRequest, context: CallContext & AuthenticationCallContextExt): Promise { if (!hasPermission(context, 'deleteBossFiles')) { @@ -18,7 +21,15 @@ export async function deleteFile(request: DeleteFileRequest, context: CallContex throw new ServerError(Status.INVALID_ARGUMENT, 'Missing file data ID'); } - const file = await getTaskFileByDataID(dataID); + let file: HydratedFileCTRDocument | HydratedFileWUPDocument | null; + + if (request.platformType === PlatformType.PLATFORM_TYPE_CTR) { + file = await getCTRTaskFileBySerialNumber(Number(dataID)); + } else if (request.platformType === PlatformType.PLATFORM_TYPE_WUP) { + file = await getWUPTaskFileByDataID(dataID); + } else { + throw new ServerError(Status.INVALID_ARGUMENT, 'Invalid platform type'); + } if (!file || file.boss_app_id !== bossAppID) { throw new ServerError(Status.INVALID_ARGUMENT, `File ${dataID} not found for BOSS app ${bossAppID}`); diff --git a/src/services/grpc/boss/v2/implementation.ts b/src/services/grpc/boss/v2/implementation.ts index 86d45da..11376a7 100644 --- a/src/services/grpc/boss/v2/implementation.ts +++ b/src/services/grpc/boss/v2/implementation.ts @@ -7,8 +7,8 @@ import { deleteFile } from '@/services/grpc/boss/v2/delete-file'; import { listFilesWUP } from '@/services/grpc/boss/v2/list-files-wup'; import { uploadFileWUP } from '@/services/grpc/boss/v2/upload-file-wup'; import { listFilesCTR } from '@/services/grpc/boss/v2/list-files-ctr'; -// import { uploadFileCTR } from '@/services/grpc/boss/v2/upload-file-ctr'; -// import { updateFileMetadataCTR } from '@/services/grpc/boss/v2/update-file-metadata-ctr'; +import { uploadFileCTR } from '@/services/grpc/boss/v2/upload-file-ctr'; +import { updateFileMetadataCTR } from '@/services/grpc/boss/v2/update-file-metadata-ctr'; import { updateFileMetadataWUP } from '@/services/grpc/boss/v2/update-file-metadata-wup'; import type { BossServiceImplementation } from '@pretendonetwork/grpc/boss/v2/boss_service'; @@ -22,7 +22,7 @@ export const bossServiceImplementationV2: BossServiceImplementation = { listFilesWUP, uploadFileWUP, listFilesCTR, - // uploadFileCTR, - // updateFileMetadataCTR, + uploadFileCTR, + updateFileMetadataCTR, updateFileMetadataWUP }; diff --git a/src/services/grpc/boss/v2/list-files-ctr.ts b/src/services/grpc/boss/v2/list-files-ctr.ts index 1b7bd9c..c2a79e0 100644 --- a/src/services/grpc/boss/v2/list-files-ctr.ts +++ b/src/services/grpc/boss/v2/list-files-ctr.ts @@ -1,6 +1,6 @@ import { Status, ServerError } from 'nice-grpc'; import { isValidCountryCode, isValidLanguage } from '@/util'; -import { getTaskFiles } from '@/database'; +import { getCTRTaskFiles } from '@/database'; import type { ListFilesCTRRequest, ListFilesCTRResponse } from '@pretendonetwork/grpc/boss/v2/list_files_ctr'; const BOSS_APP_ID_FILTER_REGEX = /^[A-Za-z0-9]*$/; @@ -35,30 +35,34 @@ export async function listFilesCTR(request: ListFilesCTRRequest): Promise ({ deleted: file.deleted, - dataId: file.data_id, + dataId: BigInt(file.serial_number), // TODO - Is this okay? taskId: file.task_id, bossAppId: file.boss_app_id, supportedCountries: file.supported_countries, supportedLanguages: file.supported_languages, - attributes: { - attribute1: file.attribute1, - attribute2: file.attribute2, - attribute3: file.attribute3, - description: file.password - }, + attributes: file.attributes, creatorPid: file.creator_pid, name: file.name, hash: file.hash, - serialNumber: 0, // TODO - Don't stub this - payloadContents: [], // TODO - Don't stub this + serialNumber: file.serial_number, + payloadContents: file.payload_contents.map(payloadContentInfo => ({ + titleId: payloadContentInfo.title_id, + contentDatatype: payloadContentInfo.content_datatype, + nsDataId: payloadContentInfo.ns_data_id, + version: payloadContentInfo.version, + size: payloadContentInfo.size + })), size: file.size, createdTimestamp: file.created, - updatedTimestamp: file.updated + updatedTimestamp: file.updated, + flags: { + markArrivedPrivileged: file.flags.mark_arrived_privileged + } })) }; } diff --git a/src/services/grpc/boss/v2/list-files-wup.ts b/src/services/grpc/boss/v2/list-files-wup.ts index 80f429c..f2ec63a 100644 --- a/src/services/grpc/boss/v2/list-files-wup.ts +++ b/src/services/grpc/boss/v2/list-files-wup.ts @@ -1,6 +1,6 @@ import { Status, ServerError } from 'nice-grpc'; import { isValidCountryCode, isValidLanguage } from '@/util'; -import { getTaskFiles } from '@/database'; +import { getWUPTaskFiles } from '@/database'; import type { ListFilesWUPRequest, ListFilesWUPResponse } from '@pretendonetwork/grpc/boss/v2/list_files_wup'; const BOSS_APP_ID_FILTER_REGEX = /^[A-Za-z0-9]*$/; @@ -35,7 +35,7 @@ export async function listFilesWUP(request: ListFilesWUPRequest): Promise ({ @@ -58,8 +58,8 @@ export async function listFilesWUP(request: ListFilesWUPRequest): Promise { + if (!hasPermission(context, 'updateBossFiles')) { + throw new ServerError(Status.PERMISSION_DENIED, 'PNID not authorized to update file metadata'); + } + + const serialNumber = request.dataId; + const updateData = request.updateData; + + if (!serialNumber) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing file serial number'); + } + + if (!updateData) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing file update data'); + } + + const file = await getCTRTaskFileBySerialNumber(Number(serialNumber)); + + if (!file || file.deleted) { + throw new ServerError(Status.INVALID_ARGUMENT, `File ${serialNumber} not found`); + } + + file.task_id = updateData.taskId.slice(0, 7); + file.boss_app_id = updateData.bossAppId; + file.supported_countries = updateData.supportedCountries; + file.supported_languages = updateData.supportedLanguages; + file.attributes.attribute1 = updateData.attributes ? updateData.attributes.attribute1 : file.attributes.attribute1; + file.attributes.attribute2 = updateData.attributes ? updateData.attributes.attribute2 : file.attributes.attribute2; + file.attributes.attribute3 = updateData.attributes ? updateData.attributes.attribute3 : file.attributes.attribute3; + file.attributes.description = updateData.attributes ? updateData.attributes.description : file.attributes.description; + file.name = updateData.name; + file.updated = BigInt(Date.now()); + + if (updateData.payloadContents.length !== 0) { + file.payload_contents = updateData.payloadContents.map(payload => ({ + title_id: payload.titleId, + content_datatype: payload.contentDatatype, + ns_data_id: payload.nsDataId, + version: payload.version, + size: payload.size + })); + } + + await file.save(); + + return {}; +} diff --git a/src/services/grpc/boss/v2/update-file-metadata-wup.ts b/src/services/grpc/boss/v2/update-file-metadata-wup.ts index 4fd7e72..2a4e39a 100644 --- a/src/services/grpc/boss/v2/update-file-metadata-wup.ts +++ b/src/services/grpc/boss/v2/update-file-metadata-wup.ts @@ -1,5 +1,5 @@ import { Status, ServerError } from 'nice-grpc'; -import { getTaskFileByDataID } from '@/database'; +import { getWUPTaskFileByDataID } from '@/database'; import { isValidFileNotifyCondition, isValidFileType } from '@/util'; import { hasPermission } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; @@ -23,7 +23,7 @@ export async function updateFileMetadataWUP(request: UpdateFileMetadataWUPReques throw new ServerError(Status.INVALID_ARGUMENT, 'Missing file update data'); } - const file = await getTaskFileByDataID(dataID); + const file = await getWUPTaskFileByDataID(dataID); if (!file || file.deleted) { throw new ServerError(Status.INVALID_ARGUMENT, `File ${dataID} not found`); @@ -51,6 +51,8 @@ export async function updateFileMetadataWUP(request: UpdateFileMetadataWUPReques file.type = updateData.type; file.notify_on_new = updateData.notifyOnNew; file.notify_led = updateData.notifyLed; + file.condition_played = updateData.conditionPlayed; + file.auto_delete = updateData.autoDelete; file.updated = BigInt(Date.now()); await file.save(); diff --git a/src/services/grpc/boss/v2/upload-file-ctr.ts b/src/services/grpc/boss/v2/upload-file-ctr.ts new file mode 100644 index 0000000..31c6123 --- /dev/null +++ b/src/services/grpc/boss/v2/upload-file-ctr.ts @@ -0,0 +1,168 @@ +import { Status, ServerError } from 'nice-grpc'; +import { encrypt3DS } from '@pretendonetwork/boss-crypto'; +import { isValidCountryCode, isValidLanguage, md5 } from '@/util'; +import { connection as databaseConnection, getTask, getCTRTaskFile } from '@/database'; +import { FileCTR } from '@/models/file-ctr'; +import { config } from '@/config-manager'; +import { uploadCDNFile } from '@/cdn'; +import { hasPermission } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; +import type { CallContext } from 'nice-grpc'; +import type { UploadFileCTRRequest, UploadFileCTRResponse } from '@pretendonetwork/grpc/boss/v2/upload_file_ctr'; +import type { HydratedFileCTRDocument } from '@/types/mongoose/file-ctr'; + +const BOSS_APP_ID_FILTER_REGEX = /^[A-Za-z0-9]*$/; + +export async function uploadFileCTR(request: UploadFileCTRRequest, context: CallContext & AuthenticationCallContextExt): Promise { + if (!hasPermission(context, 'uploadBossFiles')) { + throw new ServerError(Status.PERMISSION_DENIED, 'PNID not authorized to upload new files'); + } + + const taskID = request.taskId.trim(); + const bossAppID = request.bossAppId.trim(); + const supportedCountries = request.supportedCountries; + const supportedLanguages = request.supportedLanguages; + const name = request.name.trim(); + const payloads = request.payloadContents; + + if (!taskID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing task ID'); + } + + if (!bossAppID) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Missing BOSS app ID'); + } + + if (bossAppID.length !== 16) { + throw new ServerError(Status.INVALID_ARGUMENT, 'BOSS app ID must be 16 characters'); + } + + if (!BOSS_APP_ID_FILTER_REGEX.test(bossAppID)) { + throw new ServerError(Status.INVALID_ARGUMENT, 'BOSS app ID must only contain letters and numbers'); + } + + if (!(await getTask(bossAppID, taskID))) { + throw new ServerError(Status.NOT_FOUND, `Task ${taskID} does not exist for BOSS app ${bossAppID}`); + } + + for (const country of supportedCountries) { + if (!isValidCountryCode(country)) { + throw new ServerError(Status.INVALID_ARGUMENT, `${country} is not a valid country`); + } + } + + for (const language of supportedLanguages) { + if (!isValidLanguage(language)) { + throw new ServerError(Status.INVALID_ARGUMENT, `${language} is not a valid language`); + } + } + + if (payloads.length === 0) { + throw new ServerError(Status.INVALID_ARGUMENT, 'Cannot upload empty file'); + } + + const session = await databaseConnection().startSession(); + await session.startTransaction(); + + let file: HydratedFileCTRDocument | null; + + try { + // * Create the FileCTR first since encrypt3DS relies on the serial number + file = await getCTRTaskFile(bossAppID, taskID, name); + + if (file) { + file.deleted = true; + file.updated = BigInt(Date.now()); + + await file.save({ session }); + } + + file = await FileCTR.create({ + creator_pid: context.user?.pid, + // * hash: String, + // * file_key: String, + // * size: BigInt, + task_id: taskID, + boss_app_id: bossAppID, + supported_countries: supportedCountries, + supported_languages: supportedLanguages, + attributes: request.attributes, + name: name, + payload_contents: payloads.map(payload => ({ + title_id: payload.titleId, + content_datatype: payload.contentDatatype, + ns_data_id: payload.nsDataId, + version: payload.version, + size: payload.content.length + })), + flags: { + mark_arrived_privileged: request.flags?.markArrivedPrivileged || false + }, + created: Date.now(), + updated: Date.now() + }); + + const cryptoOptions = payloads.map(payload => ({ + program_id: payload.titleId, + content_datatype: payload.contentDatatype, + ns_data_id: payload.nsDataId, + version: payload.version, + content: payload.content + })); + + // TODO - Add flags here. @pretendonetwork/boss-crypto does not export CTR_BOSS_FLAGS at the moment + + // TODO - Somehow support pre-encrypted content? + const encryptedData = encrypt3DS(config.crypto.ctr.aes_key, BigInt(file.serial_number), cryptoOptions); + const contentHash = md5(encryptedData); + const key = `${bossAppID}/${taskID}/${contentHash}`; + + await uploadCDNFile('taskFile', key, encryptedData); + + file.hash = contentHash; + file.file_key = key; + file.size = BigInt(encryptedData.length); + + await file.save({ session }); + await session.commitTransaction(); + } catch (error: unknown) { + let message = 'Unknown file upload error'; + + if (error instanceof Error) { + message = error.message; + } + + throw new ServerError(Status.ABORTED, message); + } finally { + await session.endSession(); + } + + return { + file: { + deleted: file.deleted, + dataId: BigInt(file.serial_number), // TODO - Is this okay? + taskId: file.task_id, + bossAppId: file.boss_app_id, + supportedCountries: file.supported_countries, + supportedLanguages: file.supported_languages, + attributes: file.attributes, + creatorPid: file.creator_pid, + name: file.name, + hash: file.hash, + serialNumber: file.serial_number, + payloadContents: file.payload_contents.map(payloadContentInfo => ({ + titleId: payloadContentInfo.title_id, + contentDatatype: payloadContentInfo.content_datatype, + nsDataId: payloadContentInfo.ns_data_id, + version: payloadContentInfo.version, + size: payloadContentInfo.size + })), + size: file.size, + createdTimestamp: file.created, + updatedTimestamp: file.updated, + flags: { + markArrivedPrivileged: file.flags.mark_arrived_privileged + } + } + }; +} diff --git a/src/services/grpc/boss/v2/upload-file-wup.ts b/src/services/grpc/boss/v2/upload-file-wup.ts index 26959b0..58da35d 100644 --- a/src/services/grpc/boss/v2/upload-file-wup.ts +++ b/src/services/grpc/boss/v2/upload-file-wup.ts @@ -1,8 +1,8 @@ import { Status, ServerError } from 'nice-grpc'; import { encryptWiiU } from '@pretendonetwork/boss-crypto'; import { isValidCountryCode, isValidFileNotifyCondition, isValidFileType, isValidLanguage, md5 } from '@/util'; -import { getTask, getTaskFile } from '@/database'; -import { File } from '@/models/file'; +import { getTask, getWUPTaskFile } from '@/database'; +import { FileWUP } from '@/models/file-wup'; import { config } from '@/config-manager'; import { uploadCDNFile } from '@/cdn'; import { hasPermission } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; @@ -81,6 +81,7 @@ export async function uploadFileWUP(request: UploadFileWUPRequest, context: Call let encryptedData: Buffer; try { + // TODO - Check the first few bytes of the uploaded content to see if it's encrypted already, to support pre-encrypted content? encryptedData = encryptWiiU(data, config.crypto.wup.aes_key, config.crypto.wup.hmac_key); } catch (error: unknown) { let message = 'Unknown file encryption error'; @@ -113,7 +114,7 @@ export async function uploadFileWUP(request: UploadFileWUPRequest, context: Call throw new ServerError(Status.ABORTED, message); } - let file = await getTaskFile(bossAppID, taskID, name); + let file = await getWUPTaskFile(bossAppID, taskID, name); if (file) { file.deleted = true; @@ -122,7 +123,7 @@ export async function uploadFileWUP(request: UploadFileWUPRequest, context: Call await file.save(); } - file = await File.create({ + file = await FileWUP.create({ task_id: taskID.slice(0, 7), boss_app_id: bossAppID, file_key: key, @@ -135,6 +136,8 @@ export async function uploadFileWUP(request: UploadFileWUPRequest, context: Call size: BigInt(encryptedData.length), notify_on_new: notifyOnNew, notify_led: notifyLed, + condition_played: request.conditionPlayed, + auto_delete: request.autoDelete, created: Date.now(), updated: Date.now() }); @@ -165,8 +168,8 @@ export async function uploadFileWUP(request: UploadFileWUPRequest, context: Call size: file.size, notifyOnNew: file.notify_on_new, notifyLed: file.notify_led, - conditionPlayed: 0n, // TODO - Don't stub this - autoDelete: false, // TODO - Don't stub this + conditionPlayed: request.conditionPlayed, + autoDelete: request.autoDelete, createdTimestamp: file.created, updatedTimestamp: file.updated } diff --git a/src/services/npdi.ts b/src/services/npdi.ts index 5af9c89..df3a9b2 100644 --- a/src/services/npdi.ts +++ b/src/services/npdi.ts @@ -2,7 +2,7 @@ import express from 'express'; import { restrictHostnames } from '@/middleware/host-limit'; import { config } from '@/config-manager'; import { getCDNFileAsStream, streamFileToResponse } from '@/cdn'; -import { getTaskFileByDataID } from '@/database'; +import { getWUPTaskFileByDataID } from '@/database'; import { handleEtag, sendEtagCacheResponse } from '@/util'; const npdi = express.Router(); @@ -10,7 +10,7 @@ const npdi = express.Router(); npdi.get('/p01/data/1/:bossAppId/:dataId/:fileHash', async (request, response) => { const { dataId, fileHash, bossAppId } = request.params; - const file = await getTaskFileByDataID(BigInt(dataId)); + const file = await getWUPTaskFileByDataID(BigInt(dataId)); if (!file) { return response.sendStatus(404); } diff --git a/src/services/npdl.ts b/src/services/npdl.ts index d41a998..c9a5d5d 100644 --- a/src/services/npdl.ts +++ b/src/services/npdl.ts @@ -1,5 +1,5 @@ import express from 'express'; -import { getTaskFile } from '@/database'; +import { getCTRTaskFile } from '@/database'; import { config } from '@/config-manager'; import { restrictHostnames } from '@/middleware/host-limit'; import { getCDNFileAsStream, streamFileToResponse } from '@/cdn'; @@ -23,7 +23,7 @@ npdl.get([ }>, response) => { const { appID, taskID, countryCode, languageCode, fileName } = request.params; - const file = await getTaskFile(appID, taskID, fileName, countryCode, languageCode); + const file = await getCTRTaskFile(appID, taskID, fileName, countryCode, languageCode); if (!file) { response.sendStatus(404); diff --git a/src/services/npfl.ts b/src/services/npfl.ts index c4329b1..ece6132 100644 --- a/src/services/npfl.ts +++ b/src/services/npfl.ts @@ -1,6 +1,6 @@ import crypto from 'node:crypto'; import express from 'express'; -import { getTaskFilesWithAttributes } from '@/database'; +import { getCTRTaskFilesWithAttributes } from '@/database'; import { restrictHostnames } from '@/middleware/host-limit'; import { config } from '@/config-manager'; @@ -40,7 +40,7 @@ npfl.get('/p01/filelist/:appID/:taskID', async (request: express.Request<{ const attribute2 = request.query.a2; const attribute3 = request.query.a3; - const files = await getTaskFilesWithAttributes(false, appID, taskID, country, language, attribute1, attribute2, attribute3); + const files = await getCTRTaskFilesWithAttributes(false, appID, taskID, country, language, attribute1, attribute2, attribute3); // * https://gist.github.com/DaniElectra/ada7ecc930a84432f2045f6fcabac84f#nintendo-boss-file-list-server-npfl // * diff --git a/src/services/npts.ts b/src/services/npts.ts index 93c3753..83f5e9e 100644 --- a/src/services/npts.ts +++ b/src/services/npts.ts @@ -2,15 +2,15 @@ import express from 'express'; import xmlbuilder from 'xmlbuilder'; import { config } from '@/config-manager'; import { restrictHostnames } from '@/middleware/host-limit'; -import { getTask, getTaskFile, getTaskFiles } from '@/database'; -import type { HydratedFileDocument } from '@/types/mongoose/file'; +import { getTask, getWUPTaskFile, getWUPTaskFiles } from '@/database'; +import type { HydratedFileWUPDocument } from '@/types/mongoose/file-wup'; import type { HydratedTaskDocument } from '@/types/mongoose/task'; const npts = express.Router(); const xmlHeadSettings = { encoding: 'UTF-8', version: '1.0' }; -function buildFile(task: HydratedTaskDocument, file: HydratedFileDocument): any { +function buildFile(task: HydratedTaskDocument, file: HydratedFileWUPDocument): any { return { Filename: file.name, DataId: file.data_id, @@ -32,7 +32,7 @@ npts.get('/p01/tasksheet/:id/:bossAppId/:taskId', async (request, response) => { return response.sendStatus(404); } - const files = await getTaskFiles(false, bossAppId, taskId); + const files = await getWUPTaskFiles(false, bossAppId, taskId); const xmlContent = { TaskSheet: { @@ -57,7 +57,7 @@ npts.get('/p01/tasksheet/:id/:bossAppId/:taskId/:fileName', async (request, resp return response.sendStatus(404); } - const file = await getTaskFile(bossAppId, taskId, fileName); + const file = await getWUPTaskFile(bossAppId, taskId, fileName); if (!file) { return response.sendStatus(404); } diff --git a/src/types/mongoose/file-ctr.ts b/src/types/mongoose/file-ctr.ts new file mode 100644 index 0000000..0e9c796 --- /dev/null +++ b/src/types/mongoose/file-ctr.ts @@ -0,0 +1,41 @@ +import type { Model, HydratedDocument } from 'mongoose'; + +export interface IFileCTR { + deleted: boolean; + creator_pid: number; + hash: string; + file_key: string; + size: bigint; + task_id: string; + boss_app_id: string; + supported_countries: string[]; + supported_languages: string[]; + attributes: { + attribute1: string; + attribute2: string; + attribute3: string; + description: string; + }; + name: string; + serial_number: number; // * This is effectively the predecessor of the Wii U DataID + payload_contents: { + title_id: bigint; + content_datatype: number; + ns_data_id: number; + version: number; + size: number; + }[]; + flags: { + mark_arrived_privileged: boolean; + }; + created: bigint; + updated: bigint; +} + +export interface IFileCTRMethods {} + +interface IFileCTRQueryHelpers {} + +export type FileCTRModel = Model; + +export type HydratedFileCTRDocument = HydratedDocument; diff --git a/src/types/mongoose/file.ts b/src/types/mongoose/file-wup.ts similarity index 60% rename from src/types/mongoose/file.ts rename to src/types/mongoose/file-wup.ts index 845f3c8..68b116e 100644 --- a/src/types/mongoose/file.ts +++ b/src/types/mongoose/file-wup.ts @@ -1,6 +1,6 @@ import type { Model, HydratedDocument } from 'mongoose'; -export interface IFile { +export interface IFileWUP { deleted: boolean; file_key: string; data_id: bigint; @@ -19,14 +19,16 @@ export interface IFile { size: bigint; notify_on_new: string[]; notify_led: boolean; + condition_played: bigint; + auto_delete: boolean; created: bigint; updated: bigint; } -export interface IFileMethods {} +export interface IFileWUPMethods {} -interface IFileQueryHelpers {} +interface IFileWUPQueryHelpers {} -export type FileModel = Model; +export type FileWUPModel = Model; -export type HydratedFileDocument = HydratedDocument; +export type HydratedFileWUPDocument = HydratedDocument; From 92f9c09a3709ef958c44add086cf76f3f6559c85 Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Mon, 20 Oct 2025 14:17:57 -0400 Subject: [PATCH 08/30] fix: npm run lint:fix --- src/services/grpc/boss/v2/delete-file.ts | 2 +- src/services/grpc/server.ts | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/services/grpc/boss/v2/delete-file.ts b/src/services/grpc/boss/v2/delete-file.ts index 740976c..c5311bc 100644 --- a/src/services/grpc/boss/v2/delete-file.ts +++ b/src/services/grpc/boss/v2/delete-file.ts @@ -1,7 +1,7 @@ import { Status, ServerError } from 'nice-grpc'; +import { PlatformType } from '@pretendonetwork/grpc/boss/v2/platform_type'; import { getCTRTaskFileBySerialNumber, getWUPTaskFileByDataID } from '@/database'; import { hasPermission } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; -import { PlatformType } from '@pretendonetwork/grpc/boss/v2/platform_type'; import type { AuthenticationCallContextExt } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; import type { CallContext } from 'nice-grpc'; import type { DeleteFileRequest } from '@pretendonetwork/grpc/boss/v2/delete_file'; diff --git a/src/services/grpc/server.ts b/src/services/grpc/server.ts index 9696505..b41b699 100644 --- a/src/services/grpc/server.ts +++ b/src/services/grpc/server.ts @@ -1,14 +1,12 @@ import { createServer } from 'nice-grpc'; import { BOSSDefinition as BossServiceDefinitionV1 } from '@pretendonetwork/grpc/boss/boss_service'; +import { BossServiceDefinition as BossServiceDefinitionV2 } from '@pretendonetwork/grpc/boss/v2/boss_service'; import { apiKeyMiddleware as apiKeyMiddlewareV1 } from '@/services/grpc/boss/v1/middleware/api-key-middleware'; import { authenticationMiddleware as authenticationMiddlewareV1 } from '@/services/grpc/boss/v1/middleware/authentication-middleware'; import { bossServiceImplementationV1 } from '@/services/grpc/boss/v1/implementation'; - -import { BossServiceDefinition as BossServiceDefinitionV2 } from '@pretendonetwork/grpc/boss/v2/boss_service'; import { apiKeyMiddleware as apiKeyMiddlewareV2 } from '@/services/grpc/boss/v2/middleware/api-key-middleware'; import { authenticationMiddleware as authenticationMiddlewareV2 } from '@/services/grpc/boss/v2/middleware/authentication-middleware'; import { bossServiceImplementationV2 } from '@/services/grpc/boss/v2/implementation'; - import { config } from '@/config-manager'; import type { Server } from 'nice-grpc'; From 3bf0334c0c514df028b771c47ae3302f6519f363 Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Sat, 1 Nov 2025 16:21:23 -0400 Subject: [PATCH 09/30] chore: update @pretendonetwork/boss-crypto and @pretendonetwork/grpc --- package-lock.json | 40 +++++++++---------- package.json | 4 +- src/database.ts | 2 +- src/models/file-ctr.ts | 2 +- src/services/grpc/boss/v2/delete-file.ts | 2 +- src/services/grpc/boss/v2/list-files-ctr.ts | 2 +- .../grpc/boss/v2/update-file-metadata-ctr.ts | 2 +- src/services/grpc/boss/v2/upload-file-ctr.ts | 11 +++-- src/types/mongoose/file-ctr.ts | 2 +- 9 files changed, 35 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index bffa542..2c2b01d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,8 @@ "license": "AGPL-3.0-only", "dependencies": { "@aws-sdk/client-s3": "^3.723.0", - "@pretendonetwork/boss-crypto": "^1.1.2", - "@pretendonetwork/grpc": "^2.3.2", + "@pretendonetwork/boss-crypto": "^1.1.3", + "@pretendonetwork/grpc": "^2.3.3", "@typegoose/auto-increment": "^4.13.0", "commander": "^14.0.0", "cron": "^4.3.3", @@ -918,9 +918,9 @@ } }, "node_modules/@bufbuild/protobuf": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.9.0.tgz", - "integrity": "sha512-rnJenoStJ8nvmt9Gzye8nkYd6V22xUAnu4086ER7h1zJ508vStko4pMvDeQ446ilDTFpV5wnoc5YS7XvMwwMqA==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.10.0.tgz", + "integrity": "sha512-fdRs9PSrBF7QUntpZpq6BTw58fhgGJojgg39m9oFOJGZT+nip9b0so5cYY1oWl5pvemDLr0cPPsH46vwThEbpQ==", "license": "(Apache-2.0 AND BSD-3-Clause)" }, "node_modules/@emnapi/core": { @@ -2045,9 +2045,9 @@ } }, "node_modules/@pretendonetwork/boss-crypto": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@pretendonetwork/boss-crypto/-/boss-crypto-1.1.2.tgz", - "integrity": "sha512-3rDp29vAmOPvQ7L/Vyqon52Wqqz2dvhDdHAOqxruHo5Ag2k0wAnIDBNq5NaTGW+H1OYzkouIR/9nnaOmY1GHXQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@pretendonetwork/boss-crypto/-/boss-crypto-1.1.3.tgz", + "integrity": "sha512-7SJFgpR7RVsfhWL9SGMbChYrE/IOU5PmO1kWIifXSX5H6GEAIvpdV9HV1werru/pA6DyWJw0dGWlPmHvT3AWnA==", "license": "LGPL-3.0-only" }, "node_modules/@pretendonetwork/eslint-config": { @@ -2070,9 +2070,9 @@ } }, "node_modules/@pretendonetwork/grpc": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.2.tgz", - "integrity": "sha512-gnSHLHkYVgxb64chdvQGuKfAz14l6KWrjG0ch7l6YwRhnuVYMZNeUM7FNQ0zjwFKo0VAW1uI8UzGnWdzq50MOA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.3.tgz", + "integrity": "sha512-65QH9XneS1xQDTmZ/0XhHEXCPd4Sx3fMt9RDlCc0m7nl2LhaupkAW//GJhJ4QTGbN3gggv5JKME0ZfFqO6Pqbg==", "license": "AGPL-3.0-only", "dependencies": { "@bufbuild/protobuf": "^2.2.2", @@ -10628,9 +10628,9 @@ } }, "@bufbuild/protobuf": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.9.0.tgz", - "integrity": "sha512-rnJenoStJ8nvmt9Gzye8nkYd6V22xUAnu4086ER7h1zJ508vStko4pMvDeQ446ilDTFpV5wnoc5YS7XvMwwMqA==" + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.10.0.tgz", + "integrity": "sha512-fdRs9PSrBF7QUntpZpq6BTw58fhgGJojgg39m9oFOJGZT+nip9b0so5cYY1oWl5pvemDLr0cPPsH46vwThEbpQ==" }, "@emnapi/core": { "version": "1.4.5", @@ -11286,9 +11286,9 @@ "optional": true }, "@pretendonetwork/boss-crypto": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@pretendonetwork/boss-crypto/-/boss-crypto-1.1.2.tgz", - "integrity": "sha512-3rDp29vAmOPvQ7L/Vyqon52Wqqz2dvhDdHAOqxruHo5Ag2k0wAnIDBNq5NaTGW+H1OYzkouIR/9nnaOmY1GHXQ==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@pretendonetwork/boss-crypto/-/boss-crypto-1.1.3.tgz", + "integrity": "sha512-7SJFgpR7RVsfhWL9SGMbChYrE/IOU5PmO1kWIifXSX5H6GEAIvpdV9HV1werru/pA6DyWJw0dGWlPmHvT3AWnA==" }, "@pretendonetwork/eslint-config": { "version": "0.1.1", @@ -11307,9 +11307,9 @@ } }, "@pretendonetwork/grpc": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.2.tgz", - "integrity": "sha512-gnSHLHkYVgxb64chdvQGuKfAz14l6KWrjG0ch7l6YwRhnuVYMZNeUM7FNQ0zjwFKo0VAW1uI8UzGnWdzq50MOA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.3.tgz", + "integrity": "sha512-65QH9XneS1xQDTmZ/0XhHEXCPd4Sx3fMt9RDlCc0m7nl2LhaupkAW//GJhJ4QTGbN3gggv5JKME0ZfFqO6Pqbg==", "requires": { "@bufbuild/protobuf": "^2.2.2", "nice-grpc-common": "^2.0.2", diff --git a/package.json b/package.json index dc002ac..2392789 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.723.0", - "@pretendonetwork/boss-crypto": "^1.1.2", - "@pretendonetwork/grpc": "^2.3.2", + "@pretendonetwork/boss-crypto": "^1.1.3", + "@pretendonetwork/grpc": "^2.3.3", "@typegoose/auto-increment": "^4.13.0", "commander": "^14.0.0", "cron": "^4.3.3", diff --git a/src/database.ts b/src/database.ts index 5161e58..55b3b31 100644 --- a/src/database.ts +++ b/src/database.ts @@ -252,7 +252,7 @@ export function getWUPTaskFile(bossAppID: string, taskID: string, name: string, return FileWUP.findOne(filter); } -export function getCTRTaskFileBySerialNumber(serialNumber: number): Promise { +export function getCTRTaskFileBySerialNumber(serialNumber: bigint): Promise { verifyConnected(); return FileCTR.findOne({ diff --git a/src/models/file-ctr.ts b/src/models/file-ctr.ts index dfcbdd7..1b3fa18 100644 --- a/src/models/file-ctr.ts +++ b/src/models/file-ctr.ts @@ -22,7 +22,7 @@ const FileCTRSchema = new mongoose.Schema ({ deleted: file.deleted, - dataId: BigInt(file.serial_number), // TODO - Is this okay? + dataId: file.serial_number, // TODO - Is this okay? taskId: file.task_id, bossAppId: file.boss_app_id, supportedCountries: file.supported_countries, diff --git a/src/services/grpc/boss/v2/update-file-metadata-ctr.ts b/src/services/grpc/boss/v2/update-file-metadata-ctr.ts index eac3adf..5819e7e 100644 --- a/src/services/grpc/boss/v2/update-file-metadata-ctr.ts +++ b/src/services/grpc/boss/v2/update-file-metadata-ctr.ts @@ -22,7 +22,7 @@ export async function updateFileMetadataCTR(request: UpdateFileMetadataCTRReques throw new ServerError(Status.INVALID_ARGUMENT, 'Missing file update data'); } - const file = await getCTRTaskFileBySerialNumber(Number(serialNumber)); + const file = await getCTRTaskFileBySerialNumber(serialNumber); if (!file || file.deleted) { throw new ServerError(Status.INVALID_ARGUMENT, `File ${serialNumber} not found`); diff --git a/src/services/grpc/boss/v2/upload-file-ctr.ts b/src/services/grpc/boss/v2/upload-file-ctr.ts index 31c6123..e9090ad 100644 --- a/src/services/grpc/boss/v2/upload-file-ctr.ts +++ b/src/services/grpc/boss/v2/upload-file-ctr.ts @@ -1,5 +1,5 @@ import { Status, ServerError } from 'nice-grpc'; -import { encrypt3DS } from '@pretendonetwork/boss-crypto'; +import { CTR_BOSS_FLAGS, encrypt3DS } from '@pretendonetwork/boss-crypto'; import { isValidCountryCode, isValidLanguage, md5 } from '@/util'; import { connection as databaseConnection, getTask, getCTRTaskFile } from '@/database'; import { FileCTR } from '@/models/file-ctr'; @@ -110,10 +110,13 @@ export async function uploadFileCTR(request: UploadFileCTRRequest, context: Call content: payload.content })); - // TODO - Add flags here. @pretendonetwork/boss-crypto does not export CTR_BOSS_FLAGS at the moment + let flags = 0n; + if (request.flags?.markArrivedPrivileged) { + flags |= CTR_BOSS_FLAGS.MARK_ARRIVED_PRIVILEGED; + } // TODO - Somehow support pre-encrypted content? - const encryptedData = encrypt3DS(config.crypto.ctr.aes_key, BigInt(file.serial_number), cryptoOptions); + const encryptedData = encrypt3DS(config.crypto.ctr.aes_key, file.serial_number, cryptoOptions, flags); const contentHash = md5(encryptedData); const key = `${bossAppID}/${taskID}/${contentHash}`; @@ -140,7 +143,7 @@ export async function uploadFileCTR(request: UploadFileCTRRequest, context: Call return { file: { deleted: file.deleted, - dataId: BigInt(file.serial_number), // TODO - Is this okay? + dataId: file.serial_number, taskId: file.task_id, bossAppId: file.boss_app_id, supportedCountries: file.supported_countries, diff --git a/src/types/mongoose/file-ctr.ts b/src/types/mongoose/file-ctr.ts index 0e9c796..60d38b2 100644 --- a/src/types/mongoose/file-ctr.ts +++ b/src/types/mongoose/file-ctr.ts @@ -17,7 +17,7 @@ export interface IFileCTR { description: string; }; name: string; - serial_number: number; // * This is effectively the predecessor of the Wii U DataID + serial_number: bigint; // * This is effectively the predecessor of the Wii U DataID payload_contents: { title_id: bigint; content_datatype: number; From 3a55e46fa17801719048cbc07787caf4a5436510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sat, 1 Nov 2025 12:11:14 +0000 Subject: [PATCH 10/30] fix(npfl): Query files from FileCTR Also use proper size on content size column. --- src/database.ts | 12 ++++++------ src/services/npfl.ts | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/database.ts b/src/database.ts index 55b3b31..e7bf865 100644 --- a/src/database.ts +++ b/src/database.ts @@ -130,10 +130,10 @@ export function getWUPTaskFiles(allowDeleted: boolean, bossAppID: string, taskID return FileWUP.find(filter); } -export function getCTRTaskFilesWithAttributes(allowDeleted: boolean, bossAppID: string, taskID: string, country?: string, language?: string, attribute1?: string, attribute2?: string, attribute3?: string): Promise { +export function getCTRTaskFilesWithAttributes(allowDeleted: boolean, bossAppID: string, taskID: string, country?: string, language?: string, attribute1?: string, attribute2?: string, attribute3?: string): Promise { verifyConnected(); - const filter: mongoose.FilterQuery = { + const filter: mongoose.FilterQuery = { task_id: taskID.slice(0, 7), boss_app_id: bossAppID, $and: [] @@ -162,22 +162,22 @@ export function getCTRTaskFilesWithAttributes(allowDeleted: boolean, bossAppID: } if (attribute1) { - filter.attribute1 = attribute1; + filter.attributes.attribute1 = attribute1; } if (attribute2) { - filter.attribute2 = attribute2; + filter.attributes.attribute2 = attribute2; } if (attribute3) { - filter.attribute3 = attribute3; + filter.attributes.attribute3 = attribute3; } if (filter.$and?.length === 0) { delete filter.$and; } - return FileWUP.find(filter); + return FileCTR.find(filter); } export function getCTRTaskFile(bossAppID: string, taskID: string, name: string, country?: string, language?: string): Promise { diff --git a/src/services/npfl.ts b/src/services/npfl.ts index ece6132..327bf3f 100644 --- a/src/services/npfl.ts +++ b/src/services/npfl.ts @@ -55,11 +55,11 @@ npfl.get('/p01/filelist/:appID/:taskID', async (request: express.Request<{ // * File lines have the following fields: // * // * - File name - // * - Unknown (password?) + // * - Description // * - Attribute 1 // * - Attribute 2 // * - Attribute 3 - // * - File size (0 is allowed) + // * - Content size (size of the first payload content) // * - Updated time (seconds) // * // * All fields of a file line are separated by a tab (\t) and are present even if no value. @@ -83,11 +83,11 @@ npfl.get('/p01/filelist/:appID/:taskID', async (request: express.Request<{ for (const file of files) { const params = [ file.name, - file.password, // * Unsure if this is really what this is for. Team Kirby Clash Deluxe uses this for passwords though - file.attribute1, - file.attribute2, - file.attribute3, - file.size, + file.attributes.description, + file.attributes.attribute1, + file.attributes.attribute2, + file.attributes.attribute3, + file.payload_contents[0]?.size ?? 0, file.updated / 1000n // * Expects time as seconds, not milliseconds ]; const line = `${params.join('\t')}\r\n`; From 4d266e38a070e5d5805e5a6c658329ab65550982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sat, 1 Nov 2025 13:02:44 +0000 Subject: [PATCH 11/30] fix(database): Search for no countries or languages if not specified This allows us to create files targeted to specific countries and/or languages without any issues such as returning a random language when none is specified. --- src/database.ts | 56 +++++++++++++++++++++++++++++++------------- src/services/npts.ts | 27 +++++++++++++++++---- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/database.ts b/src/database.ts index e7bf865..9cf306e 100644 --- a/src/database.ts +++ b/src/database.ts @@ -74,6 +74,10 @@ export function getCTRTaskFiles(allowDeleted: boolean, bossAppID: string, taskID { supported_countries: country } ] }); + } else { + filter.$and?.push({ + supported_countries: { $eq: [] } + }); } if (language) { @@ -83,10 +87,10 @@ export function getCTRTaskFiles(allowDeleted: boolean, bossAppID: string, taskID { supported_languages: language } ] }); - } - - if (filter.$and?.length === 0) { - delete filter.$and; + } else { + filter.$and?.push({ + supported_languages: { $eq: [] } + }); } return FileCTR.find(filter); @@ -112,6 +116,10 @@ export function getWUPTaskFiles(allowDeleted: boolean, bossAppID: string, taskID { supported_countries: country } ] }); + } else { + filter.$and?.push({ + supported_countries: { $eq: [] } + }); } if (language) { @@ -121,10 +129,10 @@ export function getWUPTaskFiles(allowDeleted: boolean, bossAppID: string, taskID { supported_languages: language } ] }); - } - - if (filter.$and?.length === 0) { - delete filter.$and; + } else { + filter.$and?.push({ + supported_languages: { $eq: [] } + }); } return FileWUP.find(filter); @@ -150,6 +158,10 @@ export function getCTRTaskFilesWithAttributes(allowDeleted: boolean, bossAppID: { supported_countries: country } ] }); + } else { + filter.$and?.push({ + supported_countries: { $eq: [] } + }); } if (language) { @@ -159,6 +171,10 @@ export function getCTRTaskFilesWithAttributes(allowDeleted: boolean, bossAppID: { supported_languages: language } ] }); + } else { + filter.$and?.push({ + supported_languages: { $eq: [] } + }); } if (attribute1) { @@ -198,6 +214,10 @@ export function getCTRTaskFile(bossAppID: string, taskID: string, name: string, { supported_countries: country } ] }); + } else { + filter.$and?.push({ + supported_countries: { $eq: [] } + }); } if (language) { @@ -207,10 +227,10 @@ export function getCTRTaskFile(bossAppID: string, taskID: string, name: string, { supported_languages: language } ] }); - } - - if (filter.$and?.length === 0) { - delete filter.$and; + } else { + filter.$and?.push({ + supported_languages: { $eq: [] } + }); } return FileCTR.findOne(filter); @@ -234,6 +254,10 @@ export function getWUPTaskFile(bossAppID: string, taskID: string, name: string, { supported_countries: country } ] }); + } else { + filter.$and?.push({ + supported_countries: { $eq: [] } + }); } if (language) { @@ -243,10 +267,10 @@ export function getWUPTaskFile(bossAppID: string, taskID: string, name: string, { supported_languages: language } ] }); - } - - if (filter.$and?.length === 0) { - delete filter.$and; + } else { + filter.$and?.push({ + supported_languages: { $eq: [] } + }); } return FileWUP.findOne(filter); diff --git a/src/services/npts.ts b/src/services/npts.ts index 83f5e9e..6e8721f 100644 --- a/src/services/npts.ts +++ b/src/services/npts.ts @@ -24,15 +24,24 @@ function buildFile(task: HydratedTaskDocument, file: HydratedFileWUPDocument): a }; } -npts.get('/p01/tasksheet/:id/:bossAppId/:taskId', async (request, response) => { +npts.get('/p01/tasksheet/:id/:bossAppId/:taskId', async (request: express.Request<{ + id: string; + bossAppId: string; + taskId: string; +}, any, any, { + c?: string; + l?: string; +}>, response) => { const { bossAppId, taskId } = request.params; + const country = request.query.c; + const language = request.query.l; const task = await getTask(bossAppId, taskId); if (!task) { return response.sendStatus(404); } - const files = await getWUPTaskFiles(false, bossAppId, taskId); + const files = await getWUPTaskFiles(false, bossAppId, taskId, country, language); const xmlContent = { TaskSheet: { @@ -49,15 +58,25 @@ npts.get('/p01/tasksheet/:id/:bossAppId/:taskId', async (request, response) => { response.send(xmlbuilder.create(xmlContent, xmlHeadSettings).end({ pretty: true })); }); -npts.get('/p01/tasksheet/:id/:bossAppId/:taskId/:fileName', async (request, response) => { +npts.get('/p01/tasksheet/:id/:bossAppId/:taskId/:fileName', async (request: express.Request<{ + id: string; + bossAppId: string; + taskId: string; + fileName: string; +}, any, any, { + c?: string; + l?: string; +}>, response) => { const { bossAppId, taskId, fileName } = request.params; + const country = request.query.c; + const language = request.query.l; const task = await getTask(bossAppId, taskId); if (!task) { return response.sendStatus(404); } - const file = await getWUPTaskFile(bossAppId, taskId, fileName); + const file = await getWUPTaskFile(bossAppId, taskId, fileName, country, language); if (!file) { return response.sendStatus(404); } From 43f03c83f8f1597ea25ef6f6e43ff49a32813ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sun, 2 Nov 2025 00:05:47 +0000 Subject: [PATCH 12/30] feat(boss/v2): Expand list of known apps Use the JSON list of apps documented on the archival-tools repo as source for these apps. The title IDs were obtained by querying tasksheets of these apps, but not all of them were available so the title ID is set to 0 in those cases. The title name and region were populated though various public sources, but they aren't perfect so some names and/or regions are left unknown. --- package-lock.json | 28 +- .../grpc/boss/v2/list-known-boss-apps.ts | 4217 ++++++++++++++++- 2 files changed, 4223 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2c2b01d..73a38e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -358,6 +358,7 @@ "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.723.0.tgz", "integrity": "sha512-9IH90m4bnHogBctVna2FnXaIGVORncfdxcqeEIovOxjIJJyHDmEAtA7B91dAM4sruddTbVzOYnqfPVst3odCbA==", + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -410,6 +411,7 @@ "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.723.0.tgz", "integrity": "sha512-YyN8x4MI/jMb4LpHsLf+VYqvbColMK8aZeGWVk2fTFsmt8lpTYGaGC1yybSwGX42mZ4W8ucu8SAYSbUraJZEjA==", + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -3389,6 +3391,7 @@ "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.39.1", "@typescript-eslint/types": "8.39.1", @@ -4002,6 +4005,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5045,6 +5049,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -5112,6 +5117,7 @@ "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -5322,6 +5328,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7394,6 +7401,7 @@ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.18.1.tgz", "integrity": "sha512-K0RfrUXXufqNRZZjvAGdyjydB91SnbWxlwFYi5t7zN2DxVWFD3c6puia0/7xfBwZm6RCpYOVdYFlRFpoDWiC+w==", "license": "MIT", + "peer": true, "dependencies": { "bson": "^6.10.4", "kareem": "2.6.3", @@ -9139,6 +9147,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -9473,6 +9482,7 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9569,6 +9579,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -10175,6 +10186,7 @@ "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.723.0.tgz", "integrity": "sha512-9IH90m4bnHogBctVna2FnXaIGVORncfdxcqeEIovOxjIJJyHDmEAtA7B91dAM4sruddTbVzOYnqfPVst3odCbA==", + "peer": true, "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -10221,6 +10233,7 @@ "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.723.0.tgz", "integrity": "sha512-YyN8x4MI/jMb4LpHsLf+VYqvbColMK8aZeGWVk2fTFsmt8lpTYGaGC1yybSwGX42mZ4W8ucu8SAYSbUraJZEjA==", + "peer": true, "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -12277,6 +12290,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.1.tgz", "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", "dev": true, + "peer": true, "requires": { "@typescript-eslint/scope-manager": "8.39.1", "@typescript-eslint/types": "8.39.1", @@ -12617,7 +12631,8 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true + "dev": true, + "peer": true }, "acorn-jsx": { "version": "5.3.2", @@ -13341,6 +13356,7 @@ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "dev": true, + "peer": true, "requires": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", @@ -13391,6 +13407,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.33.0.tgz", "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, + "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -13572,6 +13589,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, + "peer": true, "requires": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -14905,6 +14923,7 @@ "version": "8.18.1", "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.18.1.tgz", "integrity": "sha512-K0RfrUXXufqNRZZjvAGdyjydB91SnbWxlwFYi5t7zN2DxVWFD3c6puia0/7xfBwZm6RCpYOVdYFlRFpoDWiC+w==", + "peer": true, "requires": { "bson": "^6.10.4", "kareem": "2.6.3", @@ -16086,7 +16105,8 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true + "dev": true, + "peer": true } } }, @@ -16306,7 +16326,8 @@ "typescript": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==" + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "peer": true }, "typescript-eslint": { "version": "8.39.1", @@ -16363,6 +16384,7 @@ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", "dev": true, + "peer": true, "requires": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", diff --git a/src/services/grpc/boss/v2/list-known-boss-apps.ts b/src/services/grpc/boss/v2/list-known-boss-apps.ts index 7e7f157..697f0d9 100644 --- a/src/services/grpc/boss/v2/list-known-boss-apps.ts +++ b/src/services/grpc/boss/v2/list-known-boss-apps.ts @@ -15,7 +15,7 @@ export async function listKnownBOSSApps(): Promise { titleId: BigInt(0x0005003010016100), titleRegion: 'UNK', name: 'Unknown', - tasks: ['olvinfo'] + tasks: ['olvinfo', 'oltopic'] }, { bossAppId: '8MNOVprfNVAJjfCM', @@ -106,21 +106,21 @@ export async function listKnownBOSSApps(): Promise { titleId: BigInt(0x0005000010101C00), titleRegion: 'JPN', name: 'NewスーパーマリオブラザーズU (New SUPER MARIO BROS. U)', - tasks: ['news'] + tasks: ['news', 'plyrepo'] }, { bossAppId: 'RaPn5saabzliYrpo', titleId: BigInt(0x0005000010101D00), titleRegion: 'USA', name: 'New SUPER MARIO BROS. U', - tasks: ['news'] + tasks: ['news', 'plyrepo'] }, { bossAppId: '14VFIK3rY2SP0WRE', titleId: BigInt(0x0005000010101E00), titleRegion: 'EUR', name: 'New SUPER MARIO BROS. U', - tasks: ['news'] + tasks: ['news', 'plyrepo'] }, { bossAppId: 'RbEQ44t2AocC4rvu', @@ -141,14 +141,14 @@ export async function listKnownBOSSApps(): Promise { titleId: BigInt(0x0005000010162B00), titleRegion: 'JPN', name: 'スプラトゥーン (Splatoon)', - tasks: ['optdat2', 'schdat2', 'schdata'] + tasks: ['optdat2', 'schdat2', 'schdata', 'optdata'] }, { bossAppId: 'rjVlM7hUXPxmYQJh', titleId: BigInt(0x0005000010176900), titleRegion: 'USA', name: 'Splatoon', - tasks: ['optdat2', 'schdat2', 'schdata', 'optdata2', 'schdata2'] + tasks: ['optdat2', 'schdat2', 'schdata', 'optdata2', 'schdata2', 'test', 'preport', 'otpdata2', 'scddata2', 'otpdat2', 'optdata'] }, { bossAppId: 'zvGSM4kOrXpkKnpT', @@ -162,21 +162,21 @@ export async function listKnownBOSSApps(): Promise { titleId: BigInt(0x000500001012F100), titleRegion: 'JPN', name: 'Wii Sports Club', - tasks: ['sp1_ans'] + tasks: ['sp1_ans', 'sp1_rnk', 'sp1_evt'] }, { bossAppId: 'pO72Hi5uqf5yuNd8', titleId: BigInt(0x0005000010144D00), titleRegion: 'USA', name: 'Wii Sports Club', - tasks: ['sp1_ans'] + tasks: ['sp1_ans', 'sp1_rnk', 'sp1_evt'] }, { bossAppId: '4m8Xme1wKgzwslTJ', titleId: BigInt(0x0005000010144E00), titleRegion: 'EUR', name: 'Wii Sports Club', - tasks: ['sp1_ans'] + tasks: ['sp1_ans', 'sp1_rnk', 'sp1_evt'] }, { bossAppId: 'ESLqtAhxS8KQU4eu', @@ -195,14 +195,14 @@ export async function listKnownBOSSApps(): Promise { { bossAppId: 'IeUc4hQsKKe9rJHB', titleId: BigInt(0x000500001018DD00), - titleRegion: 'EUA', + titleRegion: 'EUR', name: 'Super Mario Maker', tasks: ['CHARA'] }, { bossAppId: '4krJA4Gx3jF5nhQf', - titleId: BigInt(0x000500001012BC00), - titleRegion: 'JPN', + titleId: BigInt(0x000500001012BE00), + titleRegion: 'EUR', name: 'ピクミン3 (PIKMIN 3)', tasks: ['histgrm'] }, @@ -215,8 +215,8 @@ export async function listKnownBOSSApps(): Promise { }, { bossAppId: 'VWqUTspR5YtjDjxa', - titleId: BigInt(0x000500001012BE00), - titleRegion: 'EUR', + titleId: BigInt(0x000500001012BC00), + titleRegion: 'JPN', name: 'PIKMIN 3', tasks: ['histgrm'] }, @@ -232,7 +232,7 @@ export async function listKnownBOSSApps(): Promise { titleId: BigInt(0x0005000010110E00), titleRegion: 'JPN', name: '大乱闘スマッシュブラザーズ for Wii U (Super Smash Bros. for Wii U)', - tasks: ['NEWS', 'amiibo'] + tasks: ['NEWS', 'amiibo', 'friend', 'CONQ'] }, { bossAppId: 'o2Ug1pIp9Uhri6Nh', @@ -316,21 +316,21 @@ export async function listKnownBOSSApps(): Promise { titleId: BigInt(0x00050000101CDB00), titleRegion: 'JPN', name: 'Splatoon Pre-Launch Review', - tasks: ['schdata'] + tasks: ['schdata', 'optdata'] }, { bossAppId: 'sE6KwEpQYyg6tdU7', titleId: BigInt(0x00050000101CDC00), titleRegion: 'USA', name: 'Splatoon Pre-Launch Review', - tasks: ['schdata'] + tasks: ['schdata', 'optdata'] }, { bossAppId: 'pTKZ9q5KrCP3gBag', titleId: BigInt(0x00050000101CDD00), titleRegion: 'EUR', name: 'Splatoon Pre-Launch Review', - tasks: ['schdata'] + tasks: ['schdata', 'optdata'] }, { bossAppId: 'CJT88RO008LAnD51', @@ -378,7 +378,7 @@ export async function listKnownBOSSApps(): Promise { bossAppId: 'Xw6OvZkQofQ3O8Bi', titleId: BigInt(0x0005000010110B00), titleRegion: 'EUR', - name: 'Ninja Gaiden 3: Razor\'s Edge', + name: 'NINJA GAIDEN 3: Razor\'s Edge', tasks: ['DLCINFO'] }, { @@ -401,6 +401,4185 @@ export async function listKnownBOSSApps(): Promise { titleRegion: 'JPN', name: '仮面ライダー バトライド・ウォーⅡ (KAMEN RIDER BATTRIDE WAR Ⅱ)', tasks: ['CHR_GAK', 'CHR_ZNG'] + }, + { + bossAppId: 'P45xuCJjERf6MNWG', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['movie'] + }, + { + bossAppId: 'PQWAfUmDpVo0u9Fi', + titleId: BigInt(0x0005000010111C00), + titleRegion: 'JPN', + name: 'Romance of the Three Kingdoms 12', + tasks: ['Card'] + }, + { + bossAppId: 'EA9wpEnmZmeX70YS', + titleId: BigInt(0x0005000010192200), + titleRegion: 'JPN', + name: 'KAMEN RIDER SUMMON RIDE!', + tasks: ['ADDCHR0'] + }, + { + bossAppId: 'Iq5CNAngvR9auXFO', + titleId: BigInt(0x00050000101BED00), + titleRegion: 'EUR', + name: 'Star Fox Guard', + tasks: ['param'] + }, + { + bossAppId: 'ZtwtVqJkmGE2LloD', + titleId: BigInt(0x0005000010115F00), + titleRegion: 'USA', + name: 'Zen Pinball 2', + tasks: ['PTS'] + }, + { + bossAppId: 'eAzIbHvwKNHwz85M', + titleId: BigInt(0x0005000010116400), + titleRegion: 'JPN', + name: 'niconico', + tasks: ['news'] + }, + { + bossAppId: 'z4d72slRF5GX0cEr', + titleId: BigInt(0x0005000010172000), + titleRegion: 'EUR', + name: 'Pullblox World', + tasks: ['annouce'] + }, + { + bossAppId: '5iKeqk6fQq3wwfgy', + titleId: BigInt(0x000500001010EA00), + titleRegion: 'JPN', + name: 'WARRIORS OROCHI 3 Hyper(JP)', + tasks: ['OR2H000'] + }, + { + bossAppId: 'rvI5oS5jSZ0aLpeo', + titleId: BigInt(0x0005000011000000), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['demo1'] + }, + { + bossAppId: 'R5WU9gZtFShZlf6j', + titleId: BigInt(0x000500001010ED00), + titleRegion: 'EUR', + name: 'MARIO KART 8', + tasks: ['movie', 'Histo'] + }, + { + bossAppId: '78QqMzbyBbwEpzVg', + titleId: BigInt(0x0005000010111700), + titleRegion: 'USA', + name: 'Injustice: Gods Among Us', + tasks: ['Tvars'] + }, + { + bossAppId: 'I8IZTXQyDnUnFo77', + titleId: BigInt(0x0005000010111A00), + titleRegion: 'EUR', + name: 'Injustice: Gods Among Us', + tasks: ['Tvars'] + }, + { + bossAppId: 'XcawL2u1CU624gg3', + titleId: BigInt(0x0005000010185300), + titleRegion: 'JPN', + name: 'PIKMIN 3 (Trial)', + tasks: ['histgrm'] + }, + { + bossAppId: 'uNRNThGetHLXasV9', + titleId: BigInt(0x00050000101BEB00), + titleRegion: 'JPN', + name: 'Star Fox Guard', + tasks: ['param'] + }, + { + bossAppId: 'MBOU6MNVQRdTT1QA', + titleId: BigInt(0x00050000101DCC00), + titleRegion: 'JPN', + name: 'Star Fox Guard Special Demo', + tasks: ['param'] + }, + { + bossAppId: 'zBlJpj2pXcFeJYJI', + titleId: BigInt(0x00050000101DCE00), + titleRegion: 'EUR', + name: 'Star Fox Guard: Special Demo Version', + tasks: ['param'] + }, + { + bossAppId: 'dHWbU7brnq9QKaKA', + titleId: BigInt(0x00050000101DCD00), + titleRegion: 'USA', + name: 'Star Fox Guard Special Demo', + tasks: ['param'] + }, + { + bossAppId: 'gjYbE1NbQerS5v6n', + titleId: BigInt(0x0005000010149000), + titleRegion: 'JPN', + name: 'Romance of the Three Kingdoms 12 with Powerup kit', + tasks: ['Card'] + }, + { + bossAppId: '07SSacDlEHc8z0jg', + titleId: BigInt(0x0004000000155000), + titleRegion: 'JPN', + name: 'ディズニー マジックキャッスル マイ・ハッピー・ライフ2', + tasks: ['FGONLYT', 'MC2NWS'] + }, + { + bossAppId: '0hFlOFo7pNTU2dyE', + titleId: BigInt(0x00040000001A2D00), + titleRegion: 'USA', + name: 'Swapdoodle', + tasks: ['RNG_EC1', 'RNG_LS1', 'RNG_MD1', 'RNG_NT1', 'RNG_NT2', 'RNG_GM1', 'RNG_AP1', 'RNG_DFR', 'RNG_U01', 'RNG_U02', 'RNG_U03', 'RNG_U04', 'RNG_U05', 'RNG_U06', 'RNG_U07', 'RNG_UUS'] + }, + { + bossAppId: '10Og6tqFdXrW1Dra', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: '18ZHPbhuhUMZmLMS', + titleId: BigInt(0x00040000001D6C00), + titleRegion: 'UNK', + name: 'Yo-kai Watch 3', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: '1dNhxKHa1kgzz0gj', + titleId: BigInt(0x0004000000174E00), + titleRegion: 'JPN', + name: 'MEDAROT9 KABUTO Ver.', + tasks: ['MEDA9'] + }, + { + bossAppId: '2eMXT6CAAcQIYWuN', + titleId: BigInt(0x000400000016C700), + titleRegion: 'JPN', + name: 'Yokai Watch Busters Akaneko Dan', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: '2s640xAtrZGOWdq0', + titleId: BigInt(0x0004000000051700), + titleRegion: 'USA', + name: 'Swapnote', + tasks: ['JFR_LS2', 'JFR_NT2', 'JFR_NT1', 'JFR_NT3', 'JFR_AP2', 'JFR_GM2', 'JFR_DNT', 'JFR_DLS', 'JFR_DAP', 'JFR_DGM', 'JFR_DFR', 'JFR_U01', 'JFR_U02', 'JFR_U03', 'JFR_U04', 'JFR_U05', 'JFR_U06', 'JFR_U07', 'JFR_U08', 'JFR_U09', 'JFR_U10'] + }, + { + bossAppId: '2zDwgq1t61PlMaPq', + titleId: BigInt(0x000400000012DE00), + titleRegion: 'UNK', + name: 'ファイアーエムブレム if', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: '3ddVFPLZpzu77yvS', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['daily'] + }, + { + bossAppId: '3EHaOtNKDsD3Ybk8', + titleId: BigInt(0x000400000016C600), + titleRegion: 'JPN', + name: 'Yokai Watch Busters Shiroinu Tai', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: '3isXVXrb2lLqmrW0', + titleId: BigInt(0x00040000000CB900), + titleRegion: 'USA', + name: 'Nintendo 3DS Guide: Louvre (English Version)', + tasks: ['AL8_001'] + }, + { + bossAppId: '3t2IUj3ASUzKKEHK', + titleId: BigInt(0x00040000001B4100), + titleRegion: 'EUR', + name: 'Fire Emblem Echoes: Shadows of Valentia', + tasks: ['TASK00', 'TASK01'] + }, + { + bossAppId: '3vveLadT8H6xKkQH', + titleId: BigInt(0x00040000001A2E00), + titleRegion: 'EUR', + name: 'Swapdoodle', + tasks: ['RNG_EC1', 'RNG_LS1', 'RNG_MD1', 'RNG_NT1', 'RNG_NT2', 'RNG_GM1', 'RNG_AP1', 'RNG_DFR', 'RNG_U01', 'RNG_U02', 'RNG_U03', 'RNG_U04', 'RNG_U05', 'RNG_U06', 'RNG_U07', 'RNG_UUS'] + }, + { + bossAppId: '4LvdQ9tJBCOyHrv4', + titleId: BigInt(0x0004000000030700), + titleRegion: 'EUR', + name: 'MARIO KART 7', + tasks: ['comm', 'ghost', 'ranking'] + }, + { + bossAppId: '4OBVxt1uzhPW4cGR', + titleId: BigInt(0x0004000000118100), + titleRegion: 'JPN', + name: 'DETECTIVE CONAN PHANTOM RHAPSODY', + tasks: ['BKRJ-00', 'BKRJ', 'FGONLYT'] + }, + { + bossAppId: '53R1vYbkfqXlqzns', + titleId: BigInt(0x0004000000030600), + titleRegion: 'JPN', + name: 'マリオカート7', + tasks: ['comm', 'ghost', 'ranking'] + }, + { + bossAppId: '5xq3tXtlGqUd7MbV', + titleId: BigInt(0x00040000000EDF00), + titleRegion: 'USA', + name: 'Super Smash Bros. for Nintendo 3DS', + tasks: ['NEWS', 'amiibo', 'FGONLYT'] + }, + { + bossAppId: '5yLkA3wqcXruNE2N', + titleId: BigInt(0x000400000008C300), + titleRegion: 'USA', + name: 'Tomodachi Life', + tasks: ['tmTaskA', 'tmTaskD'] + }, + { + bossAppId: '6jk78e80CzvtPvim', + titleId: BigInt(0x0004000000095000), + titleRegion: 'JPN', + name: 'Pokemon Mystery Dungeon Magna gate and infinity labyrinth', + tasks: ['PAINF00'] + }, + { + bossAppId: '6SKM87Ll80ONzAvQ', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: '7EnMOQ6WfZSmvQgu', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: '7knOkHlr5db97LJ6', + titleId: BigInt(0x00040000001B4000), + titleRegion: 'USA', + name: 'Fire Emblem Echoes: Shadows of Valentia', + tasks: ['TASK00', 'TASK01'] + }, + { + bossAppId: '7NdLNTN4iH9wEbJQ', + titleId: BigInt(0x00040000000D0900), + titleRegion: 'EUR', + name: 'Pokémon Art Academy', + tasks: ['FGONLYT', 'pnote'] + }, + { + bossAppId: '7vzbLQCS84rtXY0y', + titleId: BigInt(0x0004000000140000), + titleRegion: 'JPN', + name: 'シアトリズム ドラゴンクエスト', + tasks: ['FGONLYT', 'TDQ01'] + }, + { + bossAppId: '8DeC0vBA5VDzPu8x', + titleId: BigInt(0x00040000000A7700), + titleRegion: 'JPN', + name: 'AKB48+Me', + tasks: ['AKBadd'] + }, + { + bossAppId: '8DViaPfyfH1pGO91', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: '8mX4hRmZqkDHn0SE', + titleId: BigInt(0x000400000010BB00), + titleRegion: 'JPN', + name: 'イナズマイレブンGO ギャラクシー スーパーノヴァ', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: '9x4m4dJwyBBlUc3g', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'demotask', 'flist', 'test'] + }, + { + bossAppId: 'ac2P6aIvORxnQJwK', + titleId: BigInt(0x0004000000147100), + titleRegion: 'USA', + name: 'Little Battlers eXperience', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'adQnSUvJlXp5igaT', + titleId: BigInt(0x00040000001D6A00), + titleRegion: 'UNK', + name: 'Yo-kai Watch 3', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'aFMv95FiWHC3k7XV', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'AWKlN4KN0rMA7Gqc', + titleId: BigInt(0x0004000000113400), + titleRegion: 'JPN', + name: 'MEDAROT8 KUWAGATA Ver.1.1', + tasks: ['MEDA8'] + }, + { + bossAppId: 'AZcAJ2Stxa9P8h7a', + titleId: BigInt(0x0004000000072A00), + titleRegion: 'JPN', + name: 'Dynasty Warriors VS', + tasks: ['SMVS_EC', 'SMVS_SC', 'SMVS_PR'] + }, + { + bossAppId: 'B6Iqt2r0EGU549NW', + titleId: BigInt(0x0004000000030800), + titleRegion: 'USA', + name: 'MARIO KART 7', + tasks: ['comm', 'ghost', 'ranking'] + }, + { + bossAppId: 'b8RPtQj41Mw5HjoX', + titleId: BigInt(0x00040000001D6700), + titleRegion: 'USA', + name: 'Yo-kai Watch 3', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'bGoDxPNf97rXq9a5', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['present', 'patch'] + }, + { + bossAppId: 'BgyAdVTkMfLTzM0k', + titleId: BigInt(0x0004000000167600), + titleRegion: 'KOR', + name: 'YO-KAI WATCH', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'bOMC45IF1taPnYx8', + titleId: BigInt(0x0004000000168C00), + titleRegion: 'UNK', + name: 'Little Battlers eXperience', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'ciTu5gGHcW767kOc', + titleId: BigInt(0x0004000000101300), + titleRegion: 'JPN', + name: 'ヒーローバンク', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'cPYaUEX74KmPR0wF', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['present', 'patch'] + }, + { + bossAppId: 'CtfKXACbUPl8s7lk', + titleId: BigInt(0x0004001000021900), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['BGM1', 'BGM2', 'TIGER1'] + }, + { + bossAppId: 'cXo6TOh0AtrCzaCP', + titleId: BigInt(0x0004000000051800), + titleRegion: 'EUR', + name: 'Nintendo Letter Box', + tasks: ['JFR_LS2', 'JFR_AP2', 'JFR_NT1', 'JFR_NT2', 'JFR_NT3', 'JFR_DLS', 'JFR_DNT', 'JFR_DAP', 'JFR_DGM', 'JFR_GM2', 'JFR_DFR', 'JFR_U01', 'JFR_U02', 'JFR_U03', 'JFR_U04', 'JFR_U05', 'JFR_U06', 'JFR_U07', 'JFR_U08', 'JFR_U09', 'JFR_U10'] + }, + { + bossAppId: 'd4495LzgtcxUObka', + titleId: BigInt(0x0004000000065A00), + titleRegion: 'JPN', + name: 'MEDAROT7 KUWAGATA Ver.1.1', + tasks: ['MEDA7'] + }, + { + bossAppId: 'DJBXc6TzoubPYfy6', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['weekly'] + }, + { + bossAppId: 'dpwg7hD86KlFwcbk', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['daily'] + }, + { + bossAppId: 'DUE2YwOq2m9fVsdZ', + titleId: BigInt(0x000400000012A800), + titleRegion: 'JPN', + name: 'hoppechan minnadeodekake! wakuwaku hoppeland!!Ver.1.2', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'DwQNLZT8QlZIxyAJ', + titleId: BigInt(0x0004000000107C00), + titleRegion: 'USA', + name: 'Chibi-Robo! Photo Finder', + tasks: ['mesdat', 'dat'] + }, + { + bossAppId: 'e4rcoYW9QTHc2whz', + titleId: BigInt(0x0004000000155100), + titleRegion: 'JPN', + name: 'Yokai Watch 2 shinuchi', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'E5myNZzoWVHdNUvY', + titleId: BigInt(0x0004000000113300), + titleRegion: 'JPN', + name: 'MEDAROT8 KABUTO Ver.1.1', + tasks: ['MEDA8'] + }, + { + bossAppId: 'eH6oowgSM7n662mw', + titleId: BigInt(0x000400000008C400), + titleRegion: 'EUR', + name: 'Tomodachi Life', + tasks: ['tmTaskA', 'tmTaskD', 'tmTaskU'] + }, + { + bossAppId: 'f5xYhmZvwKo4uv5A', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['data', 'news'] + }, + { + bossAppId: 'fbAzg6nuN4hhQ3pJ', + titleId: BigInt(0x00040000001B2900), + titleRegion: 'EUR', + name: 'YO-KAI WATCH 2: PSYCHIC SPECTERS', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'fFV9HxPJR7NJRTre', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'FNY6HPPbPj2jIErD', + titleId: BigInt(0x0004000000072400), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['Amb_eu', 'Amb'] + }, + { + bossAppId: 'fQZi0N3YlWWq07AZ', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['item'] + }, + { + bossAppId: 'fsYqLFLQngg32A1l', + titleId: BigInt(0x00040000000D5600), + titleRegion: 'USA', + name: 'Nintendo 3DS Guide: Louvre (Version française)', + tasks: ['AL8_001'] + }, + { + bossAppId: 'G06t7Q3mkOy95nBw', + titleId: BigInt(0x00040000001CEB00), + titleRegion: 'USA', + name: 'YO-KAI WATCH BLASTERS RED CAT CORPS', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'G4uDyFA9Yc06kV8m', + titleId: BigInt(0x00040000000B6E00), + titleRegion: 'USA', + name: 'Crashmo', + tasks: ['JAU'] + }, + { + bossAppId: 'g6DKUfhQUHKd5gZz', + titleId: BigInt(0x00040000001CA800), + titleRegion: 'EUR', + name: 'LAYTON\'S MYSTERY JOURNEY™ Katrielle and the Millionaires\'...', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'GjTM1Bphz05wTMtO', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['daily'] + }, + { + bossAppId: 'gOlIVAcjDpj6K1Ut', + titleId: BigInt(0x0004000000166A00), + titleRegion: 'JPN', + name: 'Phoenix Wright: Ace Attorney Spirit of Justice', + tasks: ['GS6'] + }, + { + bossAppId: 'GQkLSOsKwIpvr8Yi', + titleId: BigInt(0x000400000007AE00), + titleRegion: 'USA', + name: 'New Super Mario Bros. 2', + tasks: ['present', 'patch'] + }, + { + bossAppId: 'GQqblssCbq6PuCyJ', + titleId: BigInt(0x0004000000174F00), + titleRegion: 'JPN', + name: 'MEDAROT9 KUWAGATA Ver.', + tasks: ['MEDA9'] + }, + { + bossAppId: 'guBwm9TlQvYvncKn', + titleId: BigInt(0x000400000011C500), + titleRegion: 'JPN', + name: 'Pokémon Alpha Sapphire', + tasks: ['horogra', 'FGONLYT'] + }, + { + bossAppId: 'GxCs83sbgwaoL2js', + titleId: BigInt(0x00040000001AE600), + titleRegion: 'JPN', + name: '100% PASUKARU SENSEI Perfect Paint Bombers', + tasks: ['FGONLYT', 'POSTING'] + }, + { + bossAppId: 'H9GUJGE1xWr9VrgG', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['present', 'patch'] + }, + { + bossAppId: 'hA0Mq0T7KQofzsFF', + titleId: BigInt(0x00040000000CBA00), + titleRegion: 'KOR', + name: 'Nintendo 3DS Guide: Louvre', + tasks: ['AL8_001'] + }, + { + bossAppId: 'i9omdntnCo1GPdHA', + titleId: BigInt(0x00040000001CB400), + titleRegion: 'UNK', + name: 'LAYTON\'S MYSTERY JOURNEY™ Katrielle and the Millionaires\'...', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'iolEjnbGp2W1ghtO', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'iYhju0xUdEBftgzw', + titleId: BigInt(0x0004000000167700), + titleRegion: 'USA', + name: 'YO-KAI WATCH', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'j0ITmVqVgfUxe0O9', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['data', 'FGONLYT', 'news'] + }, + { + bossAppId: 'J3u1c5M8Ff9Y9TyG', + titleId: BigInt(0x00040000000F0500), + titleRegion: 'UNK', + name: '大合奏!バンドブラザーズP しもべツール', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'j5cz6H9mjSt8wvLX', + titleId: BigInt(0x0004000000086300), + titleRegion: 'UNK', + name: 'Animal Crossing New Leaf', + tasks: ['FGONLYT', 'dlvexb', 'news', 'news_p', 'pnews', 'dream', 'dream_p'] + }, + { + bossAppId: 'J6la9Kj8iqTvAPOq', + titleId: BigInt(0x0004000000153600), + titleRegion: 'EUR', + name: 'Nintendo Badge Arcade Ver.1.3.1', + tasks: ['data', 'FGONLYT', 'news'] + }, + { + bossAppId: 'j9wEm4RKrgNuaudD', + titleId: BigInt(0x0004000000176E00), + titleRegion: 'JPN', + name: 'The Legend of Zelda Tri Force Heroes', + tasks: ['Info_00', 'Data', 'Data_00', 'Data_01'] + }, + { + bossAppId: 'JfttU8Wg1iBIbbIs', + titleId: BigInt(0x0004000000053700), + titleRegion: 'JPN', + name: 'エクストルーパーズ', + tasks: ['EXT0100'] + }, + { + bossAppId: 'JIguVGEJJOhAq2te', + titleId: BigInt(0x00040000001A2B00), + titleRegion: 'JPN', + name: 'Fire Emblem Echoes: Shadows of Valentia', + tasks: ['TASK00', 'FGONLYT', 'TASK01'] + }, + { + bossAppId: 'JqhCCnj1t7Zid1SG', + titleId: BigInt(0x000400000010BA00), + titleRegion: 'JPN', + name: 'イナズマイレブンGO ギャラクシー ビッグバン', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'jy95T3iOBKyGbAXl', + titleId: BigInt(0x00040000000CF900), + titleRegion: 'JPN', + name: 'Pokémon Art Academy', + tasks: ['FGONLYT', 'pnote'] + }, + { + bossAppId: 'kb6KM6y6fQx3bOWs', + titleId: BigInt(0x00040000000D0A00), + titleRegion: 'USA', + name: 'Pokémon Art Academy', + tasks: ['FGONLYT', 'pnote'] + }, + { + bossAppId: 'kQGzeGpga1cXwbtf', + titleId: BigInt(0x0004000000065B00), + titleRegion: 'JPN', + name: 'MEDAROT7 KABUTO Ver.1.1', + tasks: ['MEDA7'] + }, + { + bossAppId: 'kSz44ZF73HpIb4O7', + titleId: BigInt(0x000400000008B400), + titleRegion: 'TWN', + name: 'MARIO KART 7', + tasks: ['comm', 'ghost', 'ranking'] + }, + { + bossAppId: 'kTnNBa7mg66E11iV', + titleId: BigInt(0x0004000000030A00), + titleRegion: 'KOR', + name: 'MARIO KART 7', + tasks: ['ghost', 'ranking', 'comm'] + }, + { + bossAppId: 'L1P2RB6s878vioGs', + titleId: BigInt(0x0004000000030100), + titleRegion: 'USA', + name: 'Kid Icarus: Uprising', + tasks: ['SEED'] + }, + { + bossAppId: 'lg9TdjOIyCO7aUR4', + titleId: BigInt(0x00040000000CF400), + titleRegion: 'JPN', + name: 'Yokai Watch', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'lhz3HfuATBPF4EdY', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['ESE_CNF', 'ESE_NWS'] + }, + { + bossAppId: 'Lju5rPwt0QgXXh7Q', + titleId: BigInt(0x00040000000CB700), + titleRegion: 'JPN', + name: 'Nintendo 3DS Guide: Louvre', + tasks: ['AL8_001'] + }, + { + bossAppId: 'llh2zGNci2sULyVX', + titleId: BigInt(0x00040000000DCA00), + titleRegion: 'JPN', + name: 'ダンボール戦機W 超カスタム', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'Lw62Mz00pmT1osSJ', + titleId: BigInt(0x00040000000C3C00), + titleRegion: 'JPN', + name: 'Rodea the Sky Soldier', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'lX2cH6BQVJFyjCfN', + titleId: BigInt(0x00040000001D6B00), + titleRegion: 'UNK', + name: 'Yo-kai Watch 3', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'Lx3bOup9RTIcePKE', + titleId: BigInt(0x00040000000FD500), + titleRegion: 'USA', + name: 'THEATRHYTHM FINAL FANTASY CURTAIN CALL', + tasks: ['DUOINFO'] + }, + { + bossAppId: 'M0EKKJWDaHUDcmRr', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'M7HNbBKOBj4NTTor', + titleId: BigInt(0x0004000000124A00), + titleRegion: 'JPN', + name: 'リアル脱出ゲームxニンテンドー3DS 超破壊計画からの脱出', + tasks: ['FGONLYT', 'info'] + }, + { + bossAppId: 'mb2iVt7j6Wy6yxkb', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['daily'] + }, + { + bossAppId: 'MEXh2UqOKVgJS3h3', + titleId: BigInt(0x0004000000169D00), + titleRegion: 'JPN', + name: 'アイカツ! My No.1 Stage!', + tasks: ['AKT410a'] + }, + { + bossAppId: 'MqPfuz5l3ptcMj23', + titleId: BigInt(0x00040000000D5700), + titleRegion: 'EUR', + name: 'Nintendo 3DS Guide: Louvre (Deutsche Version)', + tasks: ['AL8_001'] + }, + { + bossAppId: 'n1cddgQeTxunOG2K', + titleId: BigInt(0x00040000001B4F00), + titleRegion: 'EUR', + name: 'Miitopia', + tasks: ['Enquete', 'MiiDL'] + }, + { + bossAppId: 'NSWeucPFtbNNCx07', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['data', 'news'] + }, + { + bossAppId: 'NU0xisOCz9UY0IBu', + titleId: BigInt(0x0004000000197200), + titleRegion: 'JPN', + name: 'アイカツスターズ! Myスペシャルアピール', + tasks: ['AKT5PKG'] + }, + { + bossAppId: 'NXIWsA2Gm4EH219s', + titleId: BigInt(0x0004000000183100), + titleRegion: 'KOR', + name: 'Rodea the Sky Soldier', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'OFypMD7hNCrRPthA', + titleId: BigInt(0x0004000000179400), + titleRegion: 'USA', + name: 'Fire Emblem Fates Birthright', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: 'oGD8mLvDr8YY1L9E', + titleId: BigInt(0x00040000001C7900), + titleRegion: 'UNK', + name: 'LAYTON\'S MYSTERY JOURNEY™ Katrielle and the Millionaires\'...', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'OkqvIoTur40udixa', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'Omwq2nEEfqZ9yo9S', + titleId: BigInt(0x00040000001CEC00), + titleRegion: 'EUR', + name: 'YO-KAI WATCH BLASTERS RED CAT CORPS', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'oPuD6PkiPpx1EFiy', + titleId: BigInt(0x00040000001C1800), + titleRegion: 'JPN', + name: 'The SNACK WORLD TREJARERS', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'oUF988iLJo3cWLji', + titleId: BigInt(0x00040000001D6900), + titleRegion: 'UNK', + name: 'Yo-kai Watch 3', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'OvbmGLZ9senvgV3K', + titleId: BigInt(0x0004000000153500), + titleRegion: 'USA', + name: 'Nintendo Badge Arcade Ver.1.3.1', + tasks: ['data', 'FGONLYT', 'news'] + }, + { + bossAppId: 'p1of6zTyKCnqOITZ', + titleId: BigInt(0x000400000009F100), + titleRegion: 'EUR', + name: 'Fire Emblem: Awakening', + tasks: ['Quartz0', 'Quartz1'] + }, + { + bossAppId: 'p47ZQwc3R4qAmAYD', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['allhp', 'FGONLYT'] + }, + { + bossAppId: 'p5HtkudRvY55R212', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'PKeGb0m4S3Gv5jis', + titleId: BigInt(0x0004000000188E00), + titleRegion: 'KOR', + name: 'Fire Emblem Fates', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: 'PpjtFCm4bQskyBUy', + titleId: BigInt(0x00040000000BA900), + titleRegion: 'EUR', + name: 'Pokémon Mystery Dungeon Gates to Infinity', + tasks: ['PAINF00'] + }, + { + bossAppId: 'PV3ljR2w0UwrQfWk', + titleId: BigInt(0x00040000001B2800), + titleRegion: 'UNK', + name: 'YO-KAI WATCH 2: PSYCHIC SPECTERS', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'PzpV1tz303wO66AD', + titleId: BigInt(0x00040000001CEF00), + titleRegion: 'USA', + name: 'YO-KAI WATCH BLASTERS WHITE DOG SQUAD', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'pZWhL0tyf4FMCt8r', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'qAM6zmNEtT45AgUa', + titleId: BigInt(0x0004000000072300), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['Amb_jp', 'Amb'] + }, + { + bossAppId: 'qCcCCCLDwNMea0nq', + titleId: BigInt(0x0004000000176F00), + titleRegion: 'USA', + name: 'The Legend of Zelda Tri Force Heroes', + tasks: ['info_00', 'Data', 'Data_00', 'Data_01'] + }, + { + bossAppId: 'QDlGiLVY6lFsOhV3', + titleId: BigInt(0x0004000000101200), + titleRegion: 'JPN', + name: 'Puyopuyo Tetris', + tasks: ['boss1', 'boss2', 'boss3', 'FGONLYT'] + }, + { + bossAppId: 'qdy7ZP05GyiwgV7L', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'QfJQIU7IDmkaUuLt', + titleId: BigInt(0x0004000000072500), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['Amb_us', 'Amb'] + }, + { + bossAppId: 'qIe6CqkMDj1IaHoD', + titleId: BigInt(0x000400000014E000), + titleRegion: 'JPN', + name: 'アイカツ! 365日のアイドルデイズ', + tasks: ['AKT365I'] + }, + { + bossAppId: 'QOnECUiVKl7wigDk', + titleId: BigInt(0x000400000005C300), + titleRegion: 'JPN', + name: 'スライムもりもりドラゴンクエスト3 大海賊としっぽ団', + tasks: ['Strike1'] + }, + { + bossAppId: 'Qp03w0NGhViaEw4w', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'RA470KStGjDbVn4O', + titleId: BigInt(0x000400000012DC00), + titleRegion: 'UNK', + name: 'ファイアーエムブレム if 白夜王国', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: 'rdWhyQMMUyKavjb4', + titleId: BigInt(0x000400000012DD00), + titleRegion: 'UNK', + name: 'ファイアーエムブレム if 暗夜王国', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: 'rFgmDJuaHQO3TQFc', + titleId: BigInt(0x0004000000178800), + titleRegion: 'JPN', + name: 'Miitopia', + tasks: ['Enquete', 'MiiDL'] + }, + { + bossAppId: 'rLNcsjO2p5oT0mqf', + titleId: BigInt(0x0004000000169500), + titleRegion: 'UNK', + name: 'Rodea the Sky Soldier V1.01', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'RrNiKxhX1QFjyHSS', + titleId: BigInt(0x0004000000179600), + titleRegion: 'USA', + name: 'Fire Emblem Fates Conquest', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: 'rsJRb5kSxvgEazo3', + titleId: BigInt(0x0004001000021800), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['PANEL', 'MIIDATA', 'ETC', 'PANELLM', 'UPDATE', 'LEGEND'] + }, + { + bossAppId: 'ruVXn8rPH6AV83CV', + titleId: BigInt(0x00040000000C0200), + titleRegion: 'JPN', + name: 'レイトン教授と 超文明Aの遺産', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'RXA1qO1PUOBzCrfe', + titleId: BigInt(0x0004000000167800), + titleRegion: 'EUR', + name: 'YO-KAI WATCH', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'S1JafzaIw7C1Iia5', + titleId: BigInt(0x0004000000147200), + titleRegion: 'EUR', + name: 'Little Battlers eXperience', + tasks: ['FGONLYT'] + }, + { + bossAppId: 's4T2rYW8ByXzSwQH', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 's6jspRUJkQbUMWgK', + titleId: BigInt(0x0004000000159500), + titleRegion: 'USA', + name: 'Devil Survivor 2 Record Breaker', + tasks: ['Ds2ocTk'] + }, + { + bossAppId: 'S7tpGELe3d0jT75J', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['data', 'news'] + }, + { + bossAppId: 'sbPtwI3pQEFTPEYu', + titleId: BigInt(0x0004000000168800), + titleRegion: 'UNK', + name: 'Rodea the Sky Soldier', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'SS47bOArh8LhftQC', + titleId: BigInt(0x0004000000078500), + titleRegion: 'JPN', + name: 'ダンボール戦機 爆ブースト', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'svTk7OJgceQciyt9', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['data', 'news'] + }, + { + bossAppId: 'Sz5wLxuBiuxOV6qO', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['data', 'news'] + }, + { + bossAppId: 'T3WjBdLsN0SbsBpB', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'T6hDbtUyZu9yGbiN', + titleId: BigInt(0x000400000017C200), + titleRegion: 'UNK', + name: 'YO-KAI WATCH', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 't6hwMmI9dyfj3vFa', + titleId: BigInt(0x00040000000D5800), + titleRegion: 'EUR', + name: 'Nintendo 3DS Guide: Louvre (Versione italiana)', + tasks: ['AL8_001'] + }, + { + bossAppId: 't7svvLkl31nkcVv9', + titleId: BigInt(0x00040000001B4E00), + titleRegion: 'USA', + name: 'Miitopia', + tasks: ['Enquete', 'MiiDL'] + }, + { + bossAppId: 'TABYOV7qcJ3wjUlk', + titleId: BigInt(0x000400000004B700), + titleRegion: 'JPN', + name: 'G1 Grand Prix Ver.1.1', + tasks: ['g1_plus'] + }, + { + bossAppId: 'TegobOkDc6fN2cLL', + titleId: BigInt(0x0004000000120C00), + titleRegion: 'KOR', + name: 'Tomodachi Life', + tasks: ['tmTaskA'] + }, + { + bossAppId: 'TkTJ7y1N7b4bvs8m', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'TuKTMEFYh0sLS1O3', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['data', 'news'] + }, + { + bossAppId: 'TVNiiigBTi1WzpnF', + titleId: BigInt(0x00040000001C6800), + titleRegion: 'KOR', + name: 'Fire Emblem Echoes: Shadows of Valentia', + tasks: ['TASK00', 'FGONLYT', 'TASK01'] + }, + { + bossAppId: 'U2ZFOVWD6LH9r4JQ', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'uLrjbAsNhsH7Hc1N', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'Umg3IE2TYUrKEV1D', + titleId: BigInt(0x000400000012F800), + titleRegion: 'JPN', + name: 'Yokai Watch 2 honke', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'uuI82221UKkqmtbp', + titleId: BigInt(0x0004003000008F02), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['basho4', 'basho5', 'sysmsg1', 'sysmsg2', 'sysmsg3', 'basho1', 'basho2', 'basho3', 'basho0'] + }, + { + bossAppId: 'UvNyA9yqIVj7LUml', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['data', 'news'] + }, + { + bossAppId: 'uwGWP9VmceGVyTXv', + titleId: BigInt(0x00040000000E9A00), + titleRegion: 'JPN', + name: 'シアトリズム FF カーテンコール', + tasks: ['DUOINFO'] + }, + { + bossAppId: 'V04RWTNqtHWfOaEB', + titleId: BigInt(0x00040000000EA900), + titleRegion: 'USA', + name: 'Disney Magical World', + tasks: ['FGONLYT', 'BNMCASI', 'BNMCASL'] + }, + { + bossAppId: 'V5zESuakCkVaRtYB', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['data', 'news'] + }, + { + bossAppId: 'vCl8UPQ872Q2wzc0', + titleId: BigInt(0x000400000008C500), + titleRegion: 'JPN', + name: 'トモダチコレクション 新生活', + tasks: ['tmTaskA'] + }, + { + bossAppId: 'vl1QWl9Lf3FhiH8r', + titleId: BigInt(0x000400000014AD00), + titleRegion: 'JPN', + name: '大逆転裁判 -成歩堂龍ノ介の冒險-', + tasks: ['DSAIBAN'] + }, + { + bossAppId: 'w4J4AC8GMlfdkX9c', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['daily'] + }, + { + bossAppId: 'w8pqLFi96ZJLL6Co', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'WaOuJFckDnqKr5sK', + titleId: BigInt(0x00040000001D6800), + titleRegion: 'EUR', + name: 'Yo-kai Watch 3', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'wbyZZlvq1j2QNBXj', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['daily'] + }, + { + bossAppId: 'wfHyw6Hj9b1UeCOd', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'WNcE0vrvCCO74nPV', + titleId: BigInt(0x000400000016DE00), + titleRegion: 'USA', + name: 'SmileBASIC Ver.3.6.0', + tasks: ['ptc3nwe'] + }, + { + bossAppId: 'wXBTx8n5TvYbdKqD', + titleId: BigInt(0x000400000012F900), + titleRegion: 'JPN', + name: 'Yokai Watch 2 ganso', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'wZgX791EQIHRicOk', + titleId: BigInt(0x00040000001C1900), + titleRegion: 'JPN', + name: 'LAYTON\'S MYSTERY JOURNEY Katrielle and the Millionaires\'...', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'X0sh6ppe6HabEEcI', + titleId: BigInt(0x0004000000177000), + titleRegion: 'EUR', + name: 'The Legend of Zelda Tri Force Heroes', + tasks: ['Info_00', 'Data', 'Data_00', 'Data_01'] + }, + { + bossAppId: 'X7wyEU7bsKuTwsw0', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'xHdRFJ7H9HIm2Eu5', + titleId: BigInt(0x0004000000030000), + titleRegion: 'JPN', + name: 'Kid Icarus: Uprising', + tasks: ['SEED'] + }, + { + bossAppId: 'XrC4VtwZsJ3ro8sn', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'Y49VUspi8Bd4dqV9', + titleId: BigInt(0x00040000000CF500), + titleRegion: 'JPN', + name: 'DQM2', + tasks: ['FGONLYT', 'WHALE01', 'WHALE02'] + }, + { + bossAppId: 'y67Up0WfC3ljfqic', + titleId: BigInt(0x0004000000030200), + titleRegion: 'EUR', + name: 'Kid Icarus: Uprising', + tasks: ['SEED'] + }, + { + bossAppId: 'YapN7dMun6U6CVPx', + titleId: BigInt(0x000400100002CD00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['thmlist', 'thmtop', 'thmnews', 'thmdtls'] + }, + { + bossAppId: 'YbltplPRvEZ9620A', + titleId: BigInt(0x000400000007AF00), + titleRegion: 'EUR', + name: 'New SUPER MARIO BROS. 2', + tasks: ['present', 'patch'] + }, + { + bossAppId: 'YCHNmojskL3NNzNp', + titleId: BigInt(0x0004000000095C00), + titleRegion: 'JPN', + name: 'Disney Magic Castle My Happy Life', + tasks: ['FGONLYT', 'BNMCASI', 'BNMCASL'] + }, + { + bossAppId: 'yj9NpW9dQjUYhN6i', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'ylHLOTvdmBIaUX4H', + titleId: BigInt(0x0004000000179800), + titleRegion: 'UNK', + name: 'Fire Emblem Fates', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: 'YSMWAkVsyGiCsw36', + titleId: BigInt(0x00040000000F5100), + titleRegion: 'JPN', + name: '三國志', + tasks: ['SGS_APD', 'SGS_INF'] + }, + { + bossAppId: 'ytEcS6DukxLYv42a', + titleId: BigInt(0x0004000000072000), + titleRegion: 'JPN', + name: 'ファイアーエムブレム 覚醒', + tasks: ['Quartz0', 'Quartz1'] + }, + { + bossAppId: 'YzNlkoJpfJCGhJ5J', + titleId: BigInt(0x0004000000166E00), + titleRegion: 'JPN', + name: 'モンハン日記 ぽかぽかアイルー村DX', + tasks: ['AIROUDX'] + }, + { + bossAppId: 'zFRghpEL0RQSrIkZ', + titleId: BigInt(0x0004000000144300), + titleRegion: 'JPN', + name: 'ヒーローバンク2', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'ZtNvntGBgoUcf3hZ', + titleId: BigInt(0x00040000001CF000), + titleRegion: 'EUR', + name: 'YO-KAI WATCH BLASTERS WHITE DOG SQUAD', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'ZweaaGi5WXHt5SO2', + titleId: BigInt(0x00040000000A0500), + titleRegion: 'USA', + name: 'Fire Emblem Awakening', + tasks: ['Quartz0', 'Quartz1'] + }, + { + bossAppId: 'Zy3Cob0dHUBuRjK3', + titleId: BigInt(0x00040000000D5900), + titleRegion: 'USA', + name: 'Nintendo 3DS Guide: Louvre (versión en español)', + tasks: ['AL8_001'] + }, + { + bossAppId: 'zY73NQ2CPef4Rfvu', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'ZzIGzz3gyLOPJUbk', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['daily'] + }, + { + bossAppId: 'RbCdW5sw3xphZ3x7', + titleId: BigInt(0x000400000014F100), + titleRegion: 'USA', + name: 'Animal Crossing: Happy Home Designer', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'kdZ3BEkxoaIt2lLJ', + titleId: BigInt(0x00040000000D4400), + titleRegion: 'JPN', + name: 'Oxford Reading Tree Floppy\'s Phonics vol.3', + tasks: ['ORT03'] + }, + { + bossAppId: '0a2LXft7kMJ39q4g', + titleId: BigInt(0x00040000000D4200), + titleRegion: 'JPN', + name: 'Oxford Reading Tree Floppy\'s Phonics vol.2', + tasks: ['ORT02'] + }, + { + bossAppId: '6cDyiXh2nyoSmHeE', + titleId: BigInt(0x00040000000D4300), + titleRegion: 'JPN', + name: 'Oxford Reading Tree Floppy\'s Phonics vol.1', + tasks: ['ORT01'] + }, + { + bossAppId: 'Tw16Bbw3beUmN6Jd', + titleId: BigInt(0x000400000014F000), + titleRegion: 'JPN', + name: 'Animal Crossing: Happy Home Designer', + tasks: ['FGONLYT'] + }, + { + bossAppId: '7b6ZRfqtEUTwJ6rj', + titleId: BigInt(0x00040000000B1900), + titleRegion: 'EUR', + name: 'Phonics Fun with Biff, Chip and Kipper vol.3', + tasks: ['ORT03'] + }, + { + bossAppId: 'PDhvZpH361L3gmm3', + titleId: BigInt(0x00040000000B1800), + titleRegion: 'EUR', + name: 'Phonics Fun with Biff, Chip and Kipper vol.2', + tasks: ['ORT02'] + }, + { + bossAppId: 'AOdUQg8P4dXbIC4b', + titleId: BigInt(0x000400000014F200), + titleRegion: 'EUR', + name: 'Animal Crossing: Happy Home Designer', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'tqdef27ymLVxfSaD', + titleId: BigInt(0x00040000000B1700), + titleRegion: 'EUR', + name: 'Phonics Fun with Biff, Chip and Kipper vol.1', + tasks: ['ORT01'] + }, + { + bossAppId: 'qEFxWy2HQ5AX7qTX', + titleId: BigInt(0x0004000200178801), + titleRegion: 'UNK', + name: 'Miitopia 体験版', + tasks: ['MiiDL'] + }, + { + bossAppId: 'EnLW1Fe8d0AfR3mE', + titleId: BigInt(0x0004000000055900), + titleRegion: 'EUR', + name: 'Rabbids Rumble', + tasks: ['NEWRABS'] + }, + { + bossAppId: 'lX5BwDIdDsj6RkZg', + titleId: BigInt(0x000400000004C300), + titleRegion: 'USA', + name: 'Imagine® babyz®', + tasks: ['RACHEL'] + }, + { + bossAppId: 'gQ3O9gb9VwvI3is6', + titleId: BigInt(0x0004000000182B00), + titleRegion: 'KOR', + name: 'The Legend of Zelda Tri Force Heroes', + tasks: ['Data', 'Data_00', 'Data_01', 'Info_00'] + }, + { + bossAppId: 'Yh1oGYQ6MHHRacyR', + titleId: BigInt(0x0004000000182C00), + titleRegion: 'TWN', + name: 'The Legend of Zelda Tri Force Heroes', + tasks: ['Data', 'Data_00', 'Data_01', 'Info_00'] + }, + { + bossAppId: 'qn6svwUCotEnBg24', + titleId: BigInt(0x00040000001B1700), + titleRegion: 'JPN', + name: '好きなMiiで見る Miitopia 予告編', + tasks: ['MiiDL', 'Enquete'] + }, + { + bossAppId: 'QlZMDe93rUHQ7svF', + titleId: BigInt(0x000400000004E100), + titleRegion: 'UNK', + name: 'Imagine™ Babies 3D', + tasks: ['RACHEL'] + }, + { + bossAppId: '6WLiMjiIXzGMmZ6n', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['Info', 'Info_00'] + }, + { + bossAppId: 'HpRhWfgZE0SoEiJ6', + titleId: BigInt(0x0004001000020B00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['NZOffPg'] + }, + { + bossAppId: 'BC10cFTXc0NHTohu', + titleId: BigInt(0x00040000001A2C00), + titleRegion: 'JPN', + name: 'Swapdoodle', + tasks: ['RNG_EC1', 'RNG_MD1', 'RNG_LS1', 'RNG_NT1', 'RNG_NT2', 'RNG_GM1', 'RNG_DFR', 'RNG_U01', 'RNG_U02', 'RNG_U03', 'RNG_U04', 'RNG_U05', 'RNG_U06', 'RNG_U07', 'RNG_UUS'] + }, + { + bossAppId: 'QwyHOPV4LsvQ2I3U', + titleId: BigInt(0x00040000000F4E00), + titleRegion: 'JPN', + name: 'NEWラブプラス+', + tasks: ['PTASK01', 'FGONLYT', 'PTASK02'] + }, + { + bossAppId: '7RW9z5Cb71Fpt1OE', + titleId: BigInt(0x0004001000020900), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['BGM1', 'TIGER1', 'BGM2'] + }, + { + bossAppId: 'TLmPhQflK0Yn6BeH', + titleId: BigInt(0x0004000000117200), + titleRegion: 'JPN', + name: 'Petit computer 3 Ver.3.6.3', + tasks: ['ptc3nws'] + }, + { + bossAppId: 'pfQzJEaJOiPlLy3t', + titleId: BigInt(0x0004001000020800), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['MIIDATA', 'ETC', 'PANELLM', 'UPDATE', 'PANEL', 'LEGEND'] + }, + { + bossAppId: '110Rzo2E1vYSfAz6', + titleId: BigInt(0x000400100002CC00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['thmtop', 'thmnews', 'thmdtls', 'thmlist'] + }, + { + bossAppId: 'gWr4JXxb2mKTG3lq', + titleId: BigInt(0x0004003000008202), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['basho1', 'basho2', 'basho3', 'basho4', 'basho5', 'sysmsg1', 'sysmsg2', 'sysmsg3', 'basho0'] + }, + { + bossAppId: 'WyI1CBPmzfm2nR2f', + titleId: BigInt(0x0004000000051600), + titleRegion: 'JPN', + name: 'Swapnote', + tasks: ['JFR_LS2', 'JFR_NT1', 'JFR_NT2', 'JFR_NT3', 'JFR_AP2', 'JFR_GM2', 'JFR_DNT', 'JFR_DLS', 'JFR_DAP', 'JFR_DGM', 'JFR_DFR', 'JFR_U01', 'JFR_U02', 'JFR_U03', 'JFR_U04', 'JFR_U05', 'JFR_U06', 'JFR_U07', 'JFR_U08', 'JFR_U09', 'JFR_U10'] + }, + { + bossAppId: 'h0VRqB2YEgq39zvO', + titleId: BigInt(0x0004000000055D00), + titleRegion: 'JPN', + name: 'Pokémon X', + tasks: ['horogra', 'FGONLYT'] + }, + { + bossAppId: 'H7btVjYWs7p5dGj6', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['BREAK01', 'BREAK02', 'BREAK03', 'FGONLYT'] + }, + { + bossAppId: 'rO34jReRezcPv4HS', + titleId: BigInt(0x0004001000021B00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['NZOffPg'] + }, + { + bossAppId: 'b3Gq6LF6EqE1bvKy', + titleId: BigInt(0x00040000001B5100), + titleRegion: 'JPN', + name: 'Pokémon Ultra Moon', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'OpIF7z4Uzjoww4Jw', + titleId: BigInt(0x00040000000DCD00), + titleRegion: 'USA', + name: 'Mario Golf: World Tour', + tasks: ['spnt_t', 'spnt_x', 'FGONLYT'] + }, + { + bossAppId: 't9PZWHTdBZ57jYL6', + titleId: BigInt(0x00040000000A9000), + titleRegion: 'EUR', + name: 'Nintendo presents: New Style Boutique', + tasks: ['girls', 'GMTM', 'info'] + }, + { + bossAppId: 'vD1TyxppgptrZdfK', + titleId: BigInt(0x0004001000027900), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['BGM1', 'BGM2', 'TIGER1'] + }, + { + bossAppId: 'vgBivYesOH9RS5I8', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'Slv7vHlUOfqrKMpz', + titleId: BigInt(0x0004000000055E00), + titleRegion: 'JPN', + name: 'Pokémon Y', + tasks: ['FGONLYT', 'horogra'] + }, + { + bossAppId: 'EeqptDDf7v2IL7OP', + titleId: BigInt(0x00040000001AB800), + titleRegion: 'USA', + name: 'Team Kirby Clash Deluxe', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'xNwjHvSQy3aGBb4C', + titleId: BigInt(0x0004000000137F00), + titleRegion: 'UNK', + name: 'New SUPER MARIO BROS. 2: Special Edition', + tasks: ['patch', 'present'] + }, + { + bossAppId: 'AH3oZwrEbne6qHCO', + titleId: BigInt(0x0004001000022900), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['BGM1', 'BGM2', 'TIGER1'] + }, + { + bossAppId: 'tjca9oAeXj1R9EfU', + titleId: BigInt(0x0004001000022800), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['MIIDATA', 'ETC', 'PANEL', 'PANELLM', 'UPDATE', 'LEGEND'] + }, + { + bossAppId: 'ZBq1ITue8b9aw64j', + titleId: BigInt(0x00040000000EE000), + titleRegion: 'EUR', + name: 'Super Smash Bros. for Nintendo 3DS', + tasks: ['NEWS', 'amiibo', 'FGONLYT'] + }, + { + bossAppId: 'dMtiFHzm5OOf0y2O', + titleId: BigInt(0x000400100002CE00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['thmtop', 'thmnews', 'thmlist', 'thmdtls'] + }, + { + bossAppId: 'UrXSeurnxhPrq7AS', + titleId: BigInt(0x0004003000009802), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['sysmsg1', 'sysmsg2', 'sysmsg3', 'basho1', 'basho2', 'basho3', 'basho4', 'basho5', 'basho0'] + }, + { + bossAppId: 'Y5G9cKWHtFCre5ni', + titleId: BigInt(0x0004000000086400), + titleRegion: 'UNK', + name: 'Animal Crossing New Leaf', + tasks: ['dlvexb', 'news', 'news_p', 'FGONLYT', 'pnews', 'dream', 'dream_p'] + }, + { + bossAppId: 'oC02RURp92o3o6XQ', + titleId: BigInt(0x00040000001B8D00), + titleRegion: 'EUR', + name: 'Miitopia: Casting Call', + tasks: ['MiiDL', 'Enquete'] + }, + { + bossAppId: '8zLdgUwAeyD4Bn3b', + titleId: BigInt(0x0004000000102F00), + titleRegion: 'JPN', + name: '太鼓の達人 どんとかつの時空大冒険', + tasks: ['DlcInfo', 'FGONLYT'] + }, + { + bossAppId: 'nn2h3ad9wxrX42UF', + titleId: BigInt(0x0004000000141000), + titleRegion: 'JPN', + name: 'Pokémon Shuffle', + tasks: ['pktrpsh'] + }, + { + bossAppId: 'wfGi1AUhRfVvZ2KR', + titleId: BigInt(0x0004001000022B00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['NZOffPg'] + }, + { + bossAppId: '6HThIi5QlwGZNYFs', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: '8QjtffIMWFhiFpTz', + titleId: BigInt(0x0004000000164800), + titleRegion: 'JPN', + name: 'Pokémon Sun', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'gfRN888w01lMrpSr', + titleId: BigInt(0x0004000000190E00), + titleRegion: 'JPN', + name: '太鼓の達人 ドコドン! ミステリーアドベンチャー', + tasks: ['DlcInfo'] + }, + { + bossAppId: 'cRFY0WFHNjPh44If', + titleId: BigInt(0x000400000011C400), + titleRegion: 'JPN', + name: 'Pokémon Omega Ruby', + tasks: ['horogra', 'FGONLYT'] + }, + { + bossAppId: 'wkiOHAEndV3fVHOF', + titleId: BigInt(0x00040000000DCE00), + titleRegion: 'EUR', + name: 'Mario Golf: World Tour', + tasks: ['spnt_t', 'FGONLYT', 'spnt_x'] + }, + { + bossAppId: 'nTik1y6PI2QheWpi', + titleId: BigInt(0x00040000000D4B00), + titleRegion: 'EUR', + name: 'Disney Magical World', + tasks: ['FGONLYT', 'BNMCASI', 'BNMCASL'] + }, + { + bossAppId: 'uAiur5PJTg0lFji6', + titleId: BigInt(0x00040000001C2600), + titleRegion: 'EUR', + name: 'Nintendo Presents New Style Boutique 3', + tasks: ['NEWS', 'POSTER'] + }, + { + bossAppId: 'hatYUcYm85RPNRYv', + titleId: BigInt(0x0004000000032D00), + titleRegion: 'USA', + name: 'SUPER STREET FIGHTER Ⅳ 3D EDITION', + tasks: ['SPA4APP'] + }, + { + bossAppId: 'nT8epEHh2yCVHTZk', + titleId: BigInt(0x0004000000198E00), + titleRegion: 'USA', + name: 'Animal Crossing: New Leaf - Welcome amiibo', + tasks: ['news', 'news_p', 'dlvexb', 'FGONLYT', 'dream', 'dream_p'] + }, + { + bossAppId: 'P2mPjVWZUv2Dw8tw', + titleId: BigInt(0x000400000017EA00), + titleRegion: 'USA', + name: 'HYRULE WARRIORS LEGENDS', + tasks: ['zdltdat', 'FGONLYT'] + }, + { + bossAppId: 'KJshB9eMGJCdvEBK', + titleId: BigInt(0x00040000000BA800), + titleRegion: 'USA', + name: 'Pokémon Mystery Dungeon Gates to Infinity', + tasks: ['PAINF00'] + }, + { + bossAppId: 'tjZ5w8RGlAKd82y0', + titleId: BigInt(0x00040000001B2700), + titleRegion: 'USA', + name: 'YO-KAI WATCH 2: PSYCHIC SPECTERS', + tasks: ['news', 'FGONLYT'] + }, + { + bossAppId: 'GHsikcsO3zjZO8bm', + titleId: BigInt(0x00040000000A9100), + titleRegion: 'USA', + name: 'Style Savvy: Trendsetters', + tasks: ['GMTM', 'info', 'girls'] + }, + { + bossAppId: 'kIHxKNVp3BHrr9bb', + titleId: BigInt(0x0004000000163200), + titleRegion: 'EUR', + name: 'Fullblox', + tasks: ['annouce', 'FGONLYT'] + }, + { + bossAppId: 'qOBtWp4rFtI6PoPl', + titleId: BigInt(0x00040000000B4F00), + titleRegion: 'EUR', + name: 'Fallblox', + tasks: ['JAU'] + }, + { + bossAppId: '3xU3A5ONnnC5ZxG9', + titleId: BigInt(0x0004000000031600), + titleRegion: 'EUR', + name: 'nintendogs + cats', + tasks: ['task_EU'] + }, + { + bossAppId: 'n7KLcJdvAfV8jrr5', + titleId: BigInt(0x00040002001B4F01), + titleRegion: 'UNK', + name: 'Miitopia: Demo Version', + tasks: ['MiiDL', 'Enquete'] + }, + { + bossAppId: 'ZMcgy6xtWsbsQE6P', + titleId: BigInt(0x0004000000198F00), + titleRegion: 'EUR', + name: 'Animal Crossing: New Leaf - Welcome amiibo', + tasks: ['news_p', 'dlvexb', 'news', 'FGONLYT', 'dream', 'dream_p'] + }, + { + bossAppId: 'x7rRVYrCfwjmpOOX', + titleId: BigInt(0x0004000000163100), + titleRegion: 'USA', + name: 'Stretchmo', + tasks: ['annouce', 'FGONLYT'] + }, + { + bossAppId: 'svHxRGPIJ2AWA5QJ', + titleId: BigInt(0x00040002001B4E01), + titleRegion: 'UNK', + name: 'Miitopia Demo', + tasks: ['MiiDL', 'Enquete'] + }, + { + bossAppId: 'vlru9ZBRLJNiPwcm', + titleId: BigInt(0x000400000004AB00), + titleRegion: 'UNK', + name: 'Nintendo Video', + tasks: ['ESP_NWS', 'ESP_CNF'] + }, + { + bossAppId: '6a2i4ewtEkJhoUS0', + titleId: BigInt(0x00040000000D9A00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SN7P'] + }, + { + bossAppId: '9vMstzmE8vG7XDOo', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['EWP_NWS', 'EWP_CNF'] + }, + { + bossAppId: 'VCcHstgngvFjng8c', + titleId: BigInt(0x0004000000032600), + titleRegion: 'UNK', + name: 'Pokédex 3D', + tasks: ['basrao0', 'basrao1'] + }, + { + bossAppId: 'gekBa3SxolnJEyXJ', + titleId: BigInt(0x000400000016A100), + titleRegion: 'EUR', + name: 'Nintendo presents New Style Boutique 2', + tasks: ['NEWS', 'POSTER', 'FGONLYT'] + }, + { + bossAppId: 'AOG9w8nspw1vnerP', + titleId: BigInt(0x0004000000084F00), + titleRegion: 'EUR', + name: 'New Art Academy', + tasks: ['london1'] + }, + { + bossAppId: 'AZdLFt0b2qPaChrb', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['ptc3nws'] + }, + { + bossAppId: 'EPqObwlC8lOg12az', + titleId: BigInt(0x000400000013F800), + titleRegion: 'UNK', + name: 'Rayman and Rabbids - Family Pack', + tasks: ['NEWRABS'] + }, + { + bossAppId: 'ytpowavlB10VzRDI', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['GS5'] + }, + { + bossAppId: 'bX6vTcQgC6ojhnM8', + titleId: BigInt(0x0004000000125D00), + titleRegion: 'JPN', + name: 'Denpa Ningen RPG FREE!', + tasks: ['labos01', 'labos02', 'FGONLYT'] + }, + { + bossAppId: 'sKBxpm1uEGbaKj3Z', + titleId: BigInt(0x0004000000030C00), + titleRegion: 'EUR', + name: 'nintendogs + cats', + tasks: ['task_EU'] + }, + { + bossAppId: 'd2AUo8Ku903Uz7Oy', + titleId: BigInt(0x000400000018FA00), + titleRegion: 'EUR', + name: 'Phoenix Wright: Ace Attorney Spirit of Justice', + tasks: ['GS6'] + }, + { + bossAppId: 'h06QNObaOsEeWbLS', + titleId: BigInt(0x0004000000034D00), + titleRegion: 'USA', + name: 'SAMURAI WARRIORS: Chronicles', + tasks: ['JMCDLC0', 'JMCDLC1', 'JMCDLC2'] + }, + { + bossAppId: 'au22VYFVclQ3t3fK', + titleId: BigInt(0x0004000000082000), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JQBE'] + }, + { + bossAppId: 'nw66VXNswH2IYbJk', + titleId: BigInt(0x0004000000082200), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ5Z'] + }, + { + bossAppId: 'ClDVV1OhC2nPQfiz', + titleId: BigInt(0x0004000000081F00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JQAE'] + }, + { + bossAppId: 'eD9gjUWfENOf7RX4', + titleId: BigInt(0x0004000000033C00), + titleRegion: 'EUR', + name: 'SUPER STREET FIGHTER Ⅳ 3D EDITION', + tasks: ['SPA4APP'] + }, + { + bossAppId: 'Ne7L2H9zREHMz0QT', + titleId: BigInt(0x000400000017A800), + titleRegion: 'UNK', + name: 'Fire Emblem Fates', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: 'asLNEQeEuHnm71Qo', + titleId: BigInt(0x0004000000079000), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ5P'] + }, + { + bossAppId: 'KafSZFWApstWG1oT', + titleId: BigInt(0x0004000000038A00), + titleRegion: 'EUR', + name: 'DEAD OR ALIVE Dimensions', + tasks: ['costume', 'foeevnt', 'info'] + }, + { + bossAppId: 's6jH81JriWS8EFL4', + titleId: BigInt(0x0004000000079300), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJYP'] + }, + { + bossAppId: '7FgFpXT7yPCI7d5K', + titleId: BigInt(0x0004000000079100), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJZP'] + }, + { + bossAppId: 'H0aS5wTwrP8xYZbj', + titleId: BigInt(0x0004000000079400), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJVP'] + }, + { + bossAppId: '2jvsEXOhaXMEJRqU', + titleId: BigInt(0x0004000000079200), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJXP'] + }, + { + bossAppId: '9eqERrZPrfwHsXw8', + titleId: BigInt(0x0004000000078E00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ6P'] + }, + { + bossAppId: 'pvKf3eKlZM86SftD', + titleId: BigInt(0x0004000000078F00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ7P'] + }, + { + bossAppId: 'Q9zLfpAkDHiIAQtD', + titleId: BigInt(0x0004000000080F00), + titleRegion: 'UNK', + name: 'Mario & Sonic - London 2012 Virtual Card Album', + tasks: ['Z_CARD0'] + }, + { + bossAppId: 'KmJKbxqqJ1ISUDgN', + titleId: BigInt(0x0004000000155C00), + titleRegion: 'EUR', + name: 'Gardening mama Forest Friends', + tasks: ['add-on'] + }, + { + bossAppId: 'rNRksUC46LBJUQps', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['weekly'] + }, + { + bossAppId: 'HxHhUeO7QSr7fTf0', + titleId: BigInt(0x0004000000137E00), + titleRegion: 'UNK', + name: 'New Super Mario Bros. 2: Gold Edition', + tasks: ['present', 'patch'] + }, + { + bossAppId: 'p2uEU1eJqc8tFvqS', + titleId: BigInt(0x0004000000034F00), + titleRegion: 'USA', + name: 'DEAD OR ALIVE Dimensions', + tasks: ['costume', 'foeevnt', 'info'] + }, + { + bossAppId: 'MjQnB45RHoQhqIf4', + titleId: BigInt(0x00040000001C2500), + titleRegion: 'USA', + name: 'Style Savvy: Styling Star', + tasks: ['NEWS', 'POSTER'] + }, + { + bossAppId: '0Fkb5zYwkA04nKYK', + titleId: BigInt(0x00040000001B8C00), + titleRegion: 'USA', + name: 'Miitopia: Casting Call', + tasks: ['MiiDL', 'Enquete'] + }, + { + bossAppId: 'CkG3Q4aOnF2ALmis', + titleId: BigInt(0x000400000017EB00), + titleRegion: 'EUR', + name: 'HYRULE WARRIORS LEGENDS', + tasks: ['zdltcm', 'zdltdat', 'FGONLYT'] + }, + { + bossAppId: 'Fk5Tz3LAVAZ2SvqP', + titleId: BigInt(0x0004000000031200), + titleRegion: 'USA', + name: 'nintendogs + cats', + tasks: ['task_US'] + }, + { + bossAppId: 'UKUcmS9e4XbCsq3x', + titleId: BigInt(0x00040000000B8B00), + titleRegion: 'JPN', + name: 'Super Smash Bros. for Nintendo 3DS', + tasks: ['NEWS', 'amiibo', 'FGONLYT'] + }, + { + bossAppId: 'QmLaxX884X14gr28', + titleId: BigInt(0x0004000000030D00), + titleRegion: 'USA', + name: 'nintendogs + cats', + tasks: ['task_US'] + }, + { + bossAppId: 'eVnDsHy7ix4xPKLs', + titleId: BigInt(0x000400000019A900), + titleRegion: 'USA', + name: 'Yo-kai Watch 2 Bony Spirits', + tasks: ['news', 'FGONLYT'] + }, + { + bossAppId: 'fnCAH3KrGIl9dgSd', + titleId: BigInt(0x00040000001B5000), + titleRegion: 'JPN', + name: 'Pokémon Ultra Sun', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'Co6AOQENlqDZDWNw', + titleId: BigInt(0x0004000000106200), + titleRegion: 'EUR', + name: 'Nintendo Pocket Football Club', + tasks: ['hobbit2', 'hobbit1'] + }, + { + bossAppId: 'dJ4hv6uMXYNhbYGJ', + titleId: BigInt(0x000400000018AF00), + titleRegion: 'UNK', + name: 'Disney Magical World 2', + tasks: ['MC2NWS', 'FGONLYT'] + }, + { + bossAppId: 'cd1qe3EXYyRuEdld', + titleId: BigInt(0x0004000000109800), + titleRegion: 'JPN', + name: 'niconico', + tasks: ['news'] + }, + { + bossAppId: 'dpi2G2X44RWYcFKi', + titleId: BigInt(0x0004000000031100), + titleRegion: 'EUR', + name: 'nintendogs + cats', + tasks: ['task_EU'] + }, + { + bossAppId: 'AQzcgnW7ozCHjk0e', + titleId: BigInt(0x000400000019AA00), + titleRegion: 'USA', + name: 'Yo-kai Watch 2 Fleshy Souls', + tasks: ['news', 'FGONLYT'] + }, + { + bossAppId: 'wKrAF9Z9DXZdXZQx', + titleId: BigInt(0x00040000001B9C00), + titleRegion: 'USA', + name: 'Cooking Mama: Sweet Shop', + tasks: ['weekly'] + }, + { + bossAppId: 'ZzWhdXdx7bv5p8bP', + titleId: BigInt(0x0004000000095800), + titleRegion: 'USA', + name: 'Art Academy Lessons for Everyone!', + tasks: ['london1'] + }, + { + bossAppId: 'ME0gcVqjUOI7Xdz4', + titleId: BigInt(0x0004000000075100), + titleRegion: 'USA', + name: 'Heroes of Ruin', + tasks: ['IMAHERO'] + }, + { + bossAppId: 'dAWysFTQbZ8yEBBd', + titleId: BigInt(0x000400000016F200), + titleRegion: 'USA', + name: 'SAMURAI WARRIORS: Chronicles 3', + tasks: ['SC3DLC0', 'FGONLYT'] + }, + { + bossAppId: 'Bj9GROrjies5XMOa', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['GS5'] + }, + { + bossAppId: '65YaZF7skxsw5cdY', + titleId: BigInt(0x000400000018F400), + titleRegion: 'USA', + name: 'Phoenix Wright: Ace Attorney Spirit of Justice', + tasks: ['GS6'] + }, + { + bossAppId: 'InwBEl2EEhnbr5EC', + titleId: BigInt(0x0004000000031700), + titleRegion: 'USA', + name: 'nintendogs + cats', + tasks: ['task_US'] + }, + { + bossAppId: 'gfRXFtm46orJeAoe', + titleId: BigInt(0x0004000000086200), + titleRegion: 'UNK', + name: 'Animal Crossing New Leaf', + tasks: ['news', 'news_p', 'pnews', 'FGONLYT', 'dream', 'dream_p'] + }, + { + bossAppId: 'h9yS1FDz26uNM9lj', + titleId: BigInt(0x00040000000B4A00), + titleRegion: 'JPN', + name: 'Tongariboshi To Maho no Machi', + tasks: ['tg4', 'tg4home', 'FGONLYT'] + }, + { + bossAppId: 'hLU6fGE4vzOsrlmw', + titleId: BigInt(0x0004000000031000), + titleRegion: 'JPN', + name: 'nintendogs + cats', + tasks: ['task_JP'] + }, + { + bossAppId: '5H2IWrmjM8PiN5SV', + titleId: BigInt(0x00040000000B4700), + titleRegion: 'UNK', + name: 'どこでも本屋さん', + tasks: ['diibo00', 'diibo02', 'diibo01', 'FGONLYT'] + }, + { + bossAppId: 'E5GjXFKIix3yGqgK', + titleId: BigInt(0x0004000000099700), + titleRegion: 'JPN', + name: 'RECOCHOKU', + tasks: ['RECOBNT', 'FGONLYT'] + }, + { + bossAppId: 'VEPSPo1c55w9ydWV', + titleId: BigInt(0x0004000000157B00), + titleRegion: 'JPN', + name: '引ク出ス ヒッパランド', + tasks: ['annouce', 'FGONLYT'] + }, + { + bossAppId: 'sA2vpi47OPnR78Ge', + titleId: BigInt(0x0004000000180A00), + titleRegion: 'JPN', + name: 'ChoChariso ver.1.2', + tasks: ['taskImg', 'taskID', 'taskFlg'] + }, + { + bossAppId: 'PAuBgnpL4UGBpboR', + titleId: BigInt(0x0004000000141D00), + titleRegion: 'JPN', + name: '大合奏!バンドブラザーズP デビュー', + tasks: ['allhp'] + }, + { + bossAppId: '76cct9EjYJJ17weU', + titleId: BigInt(0x00040002000C9E01), + titleRegion: 'UNK', + name: '(体験版)実写でちびロボ!', + tasks: ['mesdat', 'dat'] + }, + { + bossAppId: 'AcYFJX9F9mNBA34T', + titleId: BigInt(0x000400000005C800), + titleRegion: 'USA', + name: 'Crosswords Plus', + tasks: ['CW_WEEK', 'CW_RGN'] + }, + { + bossAppId: 'ZppKDH9dFwci1xuM', + titleId: BigInt(0x00040000000F1500), + titleRegion: 'USA', + name: 'JUMP TRIALS SUPREME', + tasks: ['dqu5vs9', 'x7a9u1y'] + }, + { + bossAppId: '7mXz0DXR4b4CdD8r', + titleId: BigInt(0x0004000000175E00), + titleRegion: 'JPN', + name: 'Pokémon Moon', + tasks: ['FGONLYT'] + }, + { + bossAppId: '5XpCMVOjCHJOLeh6', + titleId: BigInt(0x0004000000079700), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ6E'] + }, + { + bossAppId: 'Kgg1X843lmd8Qx4H', + titleId: BigInt(0x0004000000074000), + titleRegion: 'EUR', + name: 'Heroes of Ruin', + tasks: ['IMAHERO'] + }, + { + bossAppId: 'nmU69oGizolOYGSc', + titleId: BigInt(0x00040000000FCA00), + titleRegion: 'EUR', + name: 'THEATRHYTHM FINAL FANTASY CURTAIN CALL', + tasks: ['DUOINFO'] + }, + { + bossAppId: 'OXEubGVtO2scl6qt', + titleId: BigInt(0x0004000000132500), + titleRegion: 'JPN', + name: 'Code Name: S.T.E.A.M.', + tasks: ['NEWS'] + }, + { + bossAppId: 'wujsRH3kkwPXc5p9', + titleId: BigInt(0x0004000000179500), + titleRegion: 'UNK', + name: 'Fire Emblem Fates Birthright', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: 'xxVJpVHJVIX1unyz', + titleId: BigInt(0x000400000018B000), + titleRegion: 'JPN', + name: 'YO-KAI SANGOKUSHI', + tasks: ['yk_news', 'FGONLYT'] + }, + { + bossAppId: '9bUGzV1nUl8wxkPY', + titleId: BigInt(0x0004000000198D00), + titleRegion: 'JPN', + name: 'Animal Crossing: New Leaf - Welcome amiibo', + tasks: ['news', 'news_p', 'FGONLYT', 'dream', 'dream_p'] + }, + { + bossAppId: 'MbNeoMBork0x5U9D', + titleId: BigInt(0x0004000000137D00), + titleRegion: 'UNK', + name: 'New SUPER MARIO BROS. 2: Gold Edition', + tasks: ['present', 'patch'] + }, + { + bossAppId: 'wESLUfmWpm322iLC', + titleId: BigInt(0x00040000000A5300), + titleRegion: 'JPN', + name: 'Mario Golf: World Tour', + tasks: ['spnt_t', 'spnt_x', 'FGONLYT'] + }, + { + bossAppId: 'TISVjpYatgrPk9By', + titleId: BigInt(0x00040000001B9500), + titleRegion: 'EUR', + name: 'Cooking Mama: Sweet Shop', + tasks: ['weekly'] + }, + { + bossAppId: 'JK6188WXQQi46VGk', + titleId: BigInt(0x0004000000192F00), + titleRegion: 'EUR', + name: 'JUMP TRIALS SUPREME', + tasks: ['dqu5vs9', 'x7a9u1y'] + }, + { + bossAppId: 'r8bumkFVkPoNhc9c', + titleId: BigInt(0x00040000000E6700), + titleRegion: 'USA', + name: 'Skater Cat', + tasks: ['boss1', 'FGONLYT'] + }, + { + bossAppId: 'INTHvj3cbPjeXOIp', + titleId: BigInt(0x00040000001B7400), + titleRegion: 'EUR', + name: 'Pic-a-Pix Colour', + tasks: ['NEWPACK'] + }, + { + bossAppId: 'yYddhP0UtHK9WfXf', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['item'] + }, + { + bossAppId: 'oRUi0TW540yJfcFI', + titleId: BigInt(0x0004000000165400), + titleRegion: 'UNK', + name: 'Karaoke JOYSOUND', + tasks: ['KRKJ001'] + }, + { + bossAppId: 'YTCRfaHNkqkyAoR0', + titleId: BigInt(0x0004000000036F00), + titleRegion: 'JPN', + name: '花といきもの立体図鑑(Ver. 1.1)', + tasks: ['sflower', 'sflocal'] + }, + { + bossAppId: 'eOywu80lc2iSQrGQ', + titleId: BigInt(0x0004000000079800), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ7E'] + }, + { + bossAppId: 'Bjlq1Q32KdkSazF0', + titleId: BigInt(0x0004000000179700), + titleRegion: 'UNK', + name: 'Fire Emblem Fates Conquest', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: 'fY56qJDdLSRY1zwh', + titleId: BigInt(0x00040000000F7D00), + titleRegion: 'EUR', + name: 'Inazuma Eleven 3 Lightning Bolt', + tasks: ['news', 'FGONLYT'] + }, + { + bossAppId: 'wkGEp5JKVH1sHK4j', + titleId: BigInt(0x0004000000055400), + titleRegion: 'USA', + name: 'Rabbids Rumble', + tasks: ['NEWRABS'] + }, + { + bossAppId: 'zXfHuj8k9mgzKrpM', + titleId: BigInt(0x0004000000196500), + titleRegion: 'USA', + name: 'Nintendo presents Style Savvy: Fashion Forward', + tasks: ['NEWS', 'POSTER', 'FGONLYT'] + }, + { + bossAppId: 'gaM5Hqp8XiLyp3pU', + titleId: BigInt(0x000400000008CD00), + titleRegion: 'USA', + name: 'Sparkle Snapshots 3D', + tasks: ['NadlTsk'] + }, + { + bossAppId: 'LJ569qrUUTcV5MWa', + titleId: BigInt(0x0004000000079A00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJZE'] + }, + { + bossAppId: 'LvMjVLZITq73RRfN', + titleId: BigInt(0x0004000000079B00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJXE'] + }, + { + bossAppId: '8Urw573qCWPTMHt1', + titleId: BigInt(0x0004000000079D00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJVE'] + }, + { + bossAppId: '7oIHiYcoNKUFPXvo', + titleId: BigInt(0x0004000000079C00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJYE'] + }, + { + bossAppId: 'tGgHjsqB1lxz7laV', + titleId: BigInt(0x000400000018AE00), + titleRegion: 'UNK', + name: 'Disney Magical World 2', + tasks: ['MC2NWS', 'FGONLYT'] + }, + { + bossAppId: 'zd1wgUFX5L74rQjK', + titleId: BigInt(0x00040000000F1F00), + titleRegion: 'UNK', + name: 'Pokemon tretta LAB MAIN SYSTEM', + tasks: ['Notice', 'FGONLYT'] + }, + { + bossAppId: 'qJastY9KRjKlmpx7', + titleId: BigInt(0x000400000009AC00), + titleRegion: 'UNK', + name: 'Déco Photo 3D', + tasks: ['NadlTsk'] + }, + { + bossAppId: 'AUonPa828Z3PtkeG', + titleId: BigInt(0x00040000000ECB00), + titleRegion: 'EUR', + name: 'Skater Cat', + tasks: ['boss1'] + }, + { + bossAppId: '00ydKyJUl4PPMEM8', + titleId: BigInt(0x000400000016E600), + titleRegion: 'EUR', + name: 'Devil Survivor 2: Record Breaker', + tasks: ['Ds2ocTk'] + }, + { + bossAppId: 'Sv1HvEbP3JAcxqvf', + titleId: BigInt(0x00040000000FC800), + titleRegion: 'UNK', + name: 'honto for ニンテンドー3DS', + tasks: ['honto00'] + }, + { + bossAppId: '40Ox7iM6hyihJOiz', + titleId: BigInt(0x000400000016B200), + titleRegion: 'JPN', + name: 'ファイアーエムブレム if', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: 'yMkh08f1lW0DafIi', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JQ2J'] + }, + { + bossAppId: 'Nctmz7ODGZtPf69M', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: 'o8ZLmDtyL1j7SXyQ', + titleId: BigInt(0x0004000000144400), + titleRegion: 'JPN', + name: 'ワンピース 超グランドバトル!X', + tasks: ['compe'] + }, + { + bossAppId: 'snhV1gVHgJLnjhid', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['BREAK01', 'BREAK03', 'BREAK02', 'FGONLYT'] + }, + { + bossAppId: 'x7D5Xg4ezlaVAJyE', + titleId: BigInt(0x00040000000ABD00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNJJ'] + }, + { + bossAppId: 'jPDSZKCbiBNMsEq9', + titleId: BigInt(0x0004000000182F00), + titleRegion: 'JPN', + name: 'PUZZLE & DRAGONS CROSS GOD type', + tasks: ['INFITEM', 'INFEVT', 'INFPUB', 'FGONLYT'] + }, + { + bossAppId: 'im5wnzidQQs5uoKe', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['mesdat', 'dat'] + }, + { + bossAppId: 'eFz4GxIjoCccrVSV', + titleId: BigInt(0x0004000000031500), + titleRegion: 'JPN', + name: 'nintendogs + cats', + tasks: ['task_JP'] + }, + { + bossAppId: 'KWrfnjzo2LKhZy4s', + titleId: BigInt(0x00040000000C3A00), + titleRegion: 'JPN', + name: 'デビルサバイバー2 ブレイクレコード', + tasks: ['Ds2ocTk'] + }, + { + bossAppId: '8qwKR8OSh5GOapqe', + titleId: BigInt(0x000400300000B102), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['basho0', 'basho1', 'basho2', 'basho3', 'basho4', 'basho5', 'sysmsg1', 'sysmsg2', 'sysmsg3'] + }, + { + bossAppId: 'j7tO10mcyMaz5ig6', + titleId: BigInt(0x000400300000A102), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['basho5', 'basho4', 'basho0', 'basho1', 'basho2', 'basho3', 'sysmsg1', 'sysmsg2', 'sysmsg3'] + }, + { + bossAppId: 'kRlrRG3XMEZShz9a', + titleId: BigInt(0x0004001000028900), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['BGM1', 'BGM2', 'TIGER1'] + }, + { + bossAppId: 'VPFqk5XKRlkeei6V', + titleId: BigInt(0x000400300000A902), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['basho0', 'basho1', 'basho2', 'basho3', 'basho4', 'basho5', 'sysmsg1', 'sysmsg2', 'sysmsg3'] + }, + { + bossAppId: 'pgNuDMNihGLmz8EQ', + titleId: BigInt(0x00040000000AAD00), + titleRegion: 'JPN', + name: 'Crashmo', + tasks: ['JAU'] + }, + { + bossAppId: 'HiSDUEw0agNVyQQh', + titleId: BigInt(0x0004000000090700), + titleRegion: 'TWN', + name: 'nintendogs + cats', + tasks: ['task_CH', 'task_TW'] + }, + { + bossAppId: 'dnfsqL9yQI945m6N', + titleId: BigInt(0x0004000000160A00), + titleRegion: 'JPN', + name: 'Greeting cards', + tasks: ['KGCTask'] + }, + { + bossAppId: '0AzfphwTbGD6PV9y', + titleId: BigInt(0x00040000000DC900), + titleRegion: 'JPN', + name: 'ちび☆デビ!2 ~魔法のゆめえほん~', + tasks: ['chibi2'] + }, + { + bossAppId: 'CU4vwXZB68V4M5dd', + titleId: BigInt(0x000400000005D100), + titleRegion: 'JPN', + name: 'GIRLS MODE よくばり宣言! トキメキUP!', + tasks: ['girls', 'info', 'GMTM', 'plus'] + }, + { + bossAppId: 'SAV0iYvYj2VU3WLm', + titleId: BigInt(0x00040000000A3500), + titleRegion: 'UNK', + name: 'Skylanders Giants™', + tasks: ['SLGTASK'] + }, + { + bossAppId: 'HfTkA7wv9Xq7uCba', + titleId: BigInt(0x00040000000A9800), + titleRegion: 'JPN', + name: 'モデル☆おしゃれオーディション プラチナ', + tasks: ['MO3000', 'MO3001'] + }, + { + bossAppId: 'pIjCA0tlmrOQXZSx', + titleId: BigInt(0x0004000000170900), + titleRegion: 'JPN', + name: 'Nobunaga\'s Ambition 2', + tasks: ['NA2F000'] + }, + { + bossAppId: 'RjlyCZV4WuOMLhdI', + titleId: BigInt(0x00040000000E9000), + titleRegion: 'JPN', + name: 'JUMP TRIALS SUPREME', + tasks: ['dqu5vs9', 'x7a9u1y'] + }, + { + bossAppId: 'yivdj3PrGaiLW2q4', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['GS6'] + }, + { + bossAppId: '6KHx0Ly7P5BU2gMs', + titleId: BigInt(0x0004000000039B00), + titleRegion: 'JPN', + name: 'ポケットサッカーリーグ カルチョビット', + tasks: ['hobbit1'] + }, + { + bossAppId: 'qZJMRyERIxHiLC94', + titleId: BigInt(0x0004000000090900), + titleRegion: 'TWN', + name: 'nintendogs + cats', + tasks: ['task_CH', 'task_TW'] + }, + { + bossAppId: 'Vgw1b4CRc6tTJAuW', + titleId: BigInt(0x00040000000F4900), + titleRegion: 'JPN', + name: 'Gardening Mama Mama and Her Forest Friends', + tasks: ['add-on'] + }, + { + bossAppId: '51DuhF1iuLnlykWa', + titleId: BigInt(0x0004000000169C00), + titleRegion: 'JPN', + name: 'ちゃおイラストクラブ', + tasks: ['BMDJFOR'] + }, + { + bossAppId: 'Sx5rjDrrIqdlfck9', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['news'] + }, + { + bossAppId: 'SAV0iYvYj2VU3WLm', + titleId: BigInt(0x00040000000E7F00), + titleRegion: 'UNK', + name: 'Skylanders SWAP Force™', + tasks: ['SLGTASK'] + }, + { + bossAppId: 'SAV0iYvYj2VU3WLm', + titleId: BigInt(0x00040000000E6500), + titleRegion: 'USA', + name: 'Skylanders SWAP Force™', + tasks: ['SLGTASK'] + }, + { + bossAppId: 'hKHhZ3jJeHOdJ1dh', + titleId: BigInt(0x0004000000178000), + titleRegion: 'JPN', + name: 'ドリームガール プルミエ', + tasks: ['MO5000', 'MO5001'] + }, + { + bossAppId: 'SVCHF7ayaPXyV8da', + titleId: BigInt(0x000400000012D700), + titleRegion: 'USA', + name: 'Gardening Mama 2 Forest Friends', + tasks: ['add-on'] + }, + { + bossAppId: 'DRegPN9WZfUgWBa2', + titleId: BigInt(0x0004000000090800), + titleRegion: 'TWN', + name: 'nintendogs + cats', + tasks: ['task_CH', 'task_TW'] + }, + { + bossAppId: 'gSU6Tz36YEQi4zCC', + titleId: BigInt(0x00040000000C9E00), + titleRegion: 'JPN', + name: '実写でちびロボ!', + tasks: ['mesdat', 'dat', 'mesdat2'] + }, + { + bossAppId: 'SXDe1d24jEHW5Ddy', + titleId: BigInt(0x000400000017B700), + titleRegion: 'UNK', + name: 'Fire Emblem if', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: 'd9DVo37kDln5fsfx', + titleId: BigInt(0x00040000000F9C00), + titleRegion: 'JPN', + name: 'A Penguin\'s Troubles +', + tasks: ['PGDL000'] + }, + { + bossAppId: 'gt2GNl074uoLp9VR', + titleId: BigInt(0x00040000000BAA00), + titleRegion: 'JPN', + name: '逆転裁判5', + tasks: ['GS5'] + }, + { + bossAppId: 'VEC7I57wA8AL2D5C', + titleId: BigInt(0x00040000000B6300), + titleRegion: 'UNK', + name: 'Girls\' Fashion Shoot', + tasks: ['MO2000', 'MO2001'] + }, + { + bossAppId: 'nhIB89NAaMLIOJq5', + titleId: BigInt(0x0004000000053600), + titleRegion: 'JPN', + name: 'ペンギンの問題 ザ・ウォーズ 1.1', + tasks: ['penwar0'] + }, + { + bossAppId: 'K3cSIGJ2usdI91Mn', + titleId: BigInt(0x0004000000078B00), + titleRegion: 'JPN', + name: 'PROFESSOR LAYTON VS ACE ATTORNEY', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'jm9bQFGAHJxWREsU', + titleId: BigInt(0x00040000000A4D00), + titleRegion: 'JPN', + name: '戦国無双クロニクル セカンド 更新データ', + tasks: ['SC2DLC0'] + }, + { + bossAppId: 'SAV0iYvYj2VU3WLm', + titleId: BigInt(0x0004000000091D00), + titleRegion: 'USA', + name: 'Skylanders Giants™', + tasks: ['SLGTASK'] + }, + { + bossAppId: 'QehlmytT9n95yB2j', + titleId: BigInt(0x0004000000197F00), + titleRegion: 'JPN', + name: 'アイカツスターズ! ファーストアピール', + tasks: ['AKT5F2P'] + }, + { + bossAppId: 'EGcBTbh5QbT5PjmV', + titleId: BigInt(0x0004000000156300), + titleRegion: 'JPN', + name: 'SHIKAKUMARU', + tasks: ['SAM01'] + }, + { + bossAppId: 'iZWCrW72Sx50ObA4', + titleId: BigInt(0x0004000000112800), + titleRegion: 'JPN', + name: 'マギ 新たなる世界', + tasks: ['mg20bst'] + }, + { + bossAppId: 'r6xdZT62kmgPHbxK', + titleId: BigInt(0x0004000000156A00), + titleRegion: 'JPN', + name: 'SHIKAKUMARU', + tasks: ['SAM02'] + }, + { + bossAppId: '4zQ0tSdYf0MbtMcf', + titleId: BigInt(0x00040000000C9500), + titleRegion: 'JPN', + name: 'peakvox Mew Mew Train', + tasks: ['3MD', '3MD2'] + }, + { + bossAppId: 'UikW5d4DcNOUltN1', + titleId: BigInt(0x0004000000156500), + titleRegion: 'JPN', + name: 'SHIKAKUMARU', + tasks: ['SAM03'] + }, + { + bossAppId: '3B5MnqGceTBxIyHq', + titleId: BigInt(0x00040000000FB200), + titleRegion: 'JPN', + name: 'Nobunaga\'s Ambition', + tasks: ['NA3DFR0'] + }, + { + bossAppId: 'SAV0iYvYj2VU3WLm', + titleId: BigInt(0x00040000000A3600), + titleRegion: 'UNK', + name: 'Skylanders Giants™', + tasks: ['SLGTASK'] + }, + { + bossAppId: 'VEC7I57wA8AL2D5C', + titleId: BigInt(0x00040000000B6F00), + titleRegion: 'UNK', + name: 'Girls\' Fashion Shoot', + tasks: ['MO2000'] + }, + { + bossAppId: 'VEC7I57wA8AL2D5C', + titleId: BigInt(0x0004000000065700), + titleRegion: 'JPN', + name: 'nicola監修 モデル☆おしゃれオーディション2', + tasks: ['MO2000', 'MO2001'] + }, + { + bossAppId: 'RkgNduGP6rRkubPC', + titleId: BigInt(0x0004000000068300), + titleRegion: 'UNK', + name: 'とびだすプリクラ☆ キラデコレボリューション', + tasks: ['NadlTsk'] + }, + { + bossAppId: 'oqxYfft6J9X33Xcm', + titleId: BigInt(0x0004000000170400), + titleRegion: 'JPN', + name: 'nicopuchi', + tasks: ['BNPJFOR'] + }, + { + bossAppId: 'rS9N1JsddhwfKlVp', + titleId: BigInt(0x000400000014DF00), + titleRegion: 'JPN', + name: '戦国無双 Chronicle 3', + tasks: ['SC3DLC0', 'FGONLYT'] + }, + { + bossAppId: 'VEC7I57wA8AL2D5C', + titleId: BigInt(0x00040000000B4800), + titleRegion: 'UNK', + name: 'Girls\' Fashion Shoot', + tasks: ['MO2000'] + }, + { + bossAppId: '8xzpJYrYTQQLhs5U', + titleId: BigInt(0x00040000000FB400), + titleRegion: 'JPN', + name: 'モデル☆おしゃれオーディション ドリームガール', + tasks: ['MO4000', 'MO4001'] + }, + { + bossAppId: 'huaVwH8vmGDlynY5', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['0000001'] + }, + { + bossAppId: 'gQdtAv8S5QqhAMq6', + titleId: BigInt(0x00040000000F8000), + titleRegion: 'UNK', + name: 'Inazuma Eleven 3 Team Ogre Attacks!', + tasks: ['news', 'FGONLYT'] + }, + { + bossAppId: 'TPqWmZW8y6NEWz9N', + titleId: BigInt(0x000400000008B500), + titleRegion: 'UNK', + name: 'MARIO KART 7', + tasks: ['comm', 'ranking', 'ghost'] + }, + { + bossAppId: '4813nyO3ACQf2cwz', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['item'] + }, + { + bossAppId: 'sk1jvwem58tprBkZ', + titleId: BigInt(0x0004000000032000), + titleRegion: 'JPN', + name: 'DEAD OR ALIVE Dimensions', + tasks: ['costume', 'foeevnt', 'info'] + }, + { + bossAppId: 'crLAF7DBkd3coa3v', + titleId: BigInt(0x0004000000030500), + titleRegion: 'JPN', + name: 'SUPER STREET FIGHTER Ⅳ 3D EDITION', + tasks: ['SPA4APP'] + }, + { + bossAppId: '9DLWs8Zl50qCaWxp', + titleId: BigInt(0x0004000000040200), + titleRegion: 'UNK', + name: 'SAMURAI WARRIORS: Chronicles', + tasks: ['JMCDLC0', 'JMCDLC1', 'JMCDLC2'] + }, + { + bossAppId: 'gzPtSkOPonlun2Ug', + titleId: BigInt(0x0004000000173B00), + titleRegion: 'EUR', + name: 'SAMURAI WARRIORS: Chronicles 3', + tasks: ['SC3DLC0', 'FGONLYT'] + }, + { + bossAppId: 'J8jLC3PD2ERSNrmK', + titleId: BigInt(0x000400000008CC00), + titleRegion: 'EUR', + name: 'Sparkle Snapshots 3D', + tasks: ['NadlTsk'] + }, + { + bossAppId: 'HEMBJCoTyLKjm9pU', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['weekly'] + }, + { + bossAppId: 'OTjVevatduCI863l', + titleId: BigInt(0x00040000000F7B00), + titleRegion: 'EUR', + name: 'Inazuma Eleven 3 Bomb Blast', + tasks: ['news', 'FGONLYT'] + }, + { + bossAppId: 'NiAJFnn4fyAAiggq', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['PTASK01', 'ITASK01', 'PTASK02'] + }, + { + bossAppId: 'oHl1Ay3RPWlWngp3', + titleId: BigInt(0x0004000000030B00), + titleRegion: 'JPN', + name: 'nintendogs + cats', + tasks: ['task_JP'] + }, + { + bossAppId: 'V3xe0ZHtBxzJwJtL', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'v5fxzRdEaBW5ujTi', + titleId: BigInt(0x00040000000CC900), + titleRegion: 'KOR', + name: 'Swapnote', + tasks: ['JFR_NT1', 'JFR_NT2', 'JFR_NT3', 'JFR_LS2', 'JFR_AP2', 'JFR_DFR', 'JFR_GM2', 'JFR_U01', 'JFR_U02', 'JFR_U03', 'JFR_U04', 'JFR_U05', 'JFR_U06', 'JFR_U07', 'JFR_UUS'] + }, + { + bossAppId: 'OjI5Ysd6x9mFk22c', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['zdltdat', 'FGONLYT'] + }, + { + bossAppId: '99qH2qwTlJy9XDKs', + titleId: BigInt(0x0004000000030F00), + titleRegion: 'KOR', + name: 'nintendogs + cats', + tasks: ['task_KR'] + }, + { + bossAppId: '7Fqa3EZYYlvKEh5v', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JQ5J'] + }, + { + bossAppId: '4r5wpBYnaf6rldJ3', + titleId: BigInt(0x00040000000F7E00), + titleRegion: 'UNK', + name: 'Inazuma Eleven 3 Lightning Bolt', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: '3ERCXw1170g54UBs', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JC9K'] + }, + { + bossAppId: '9dXYzFjKdKbJMCWe', + titleId: BigInt(0x0004001000027800), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['MIIDATA', 'PANEL', 'PANELLM', 'LEGEND', 'UPDATE'] + }, + { + bossAppId: 'hMwDQ4myueRLe1J9', + titleId: BigInt(0x0004000000092E00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JZ8J'] + }, + { + bossAppId: 'L7j0ivgZfXSskmmY', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JMCDLC0', 'JMCDLC1', 'JMCDLC2'] + }, + { + bossAppId: 'CAsKat7WKddnAZX3', + titleId: BigInt(0x000400000019F600), + titleRegion: 'JPN', + name: 'Girls Mode 4 スター☆スタイリスト', + tasks: ['NEWS', 'POSTER'] + }, + { + bossAppId: 'S8m7Nskf5LDHOAFr', + titleId: BigInt(0x0004000000182E00), + titleRegion: 'JPN', + name: 'PUZZLE & DRAGONS CROSS DRAGON type', + tasks: ['INFITEM', 'INFEVT', 'INFPUB', 'FGONLYT'] + }, + { + bossAppId: 's9byficX77gemj2B', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'hXDTHQ4WSEmdUHmH', + titleId: BigInt(0x000400000012D800), + titleRegion: 'JPN', + name: 'GIRLS MODE 3 キラキラ☆コーデ', + tasks: ['NEWS', 'POSTER', 'FGONLYT'] + }, + { + bossAppId: 'txq0HfojeCsTXwdB', + titleId: BigInt(0x0004001000028800), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['PANELLM', 'MIIDATA', 'PANEL', 'LEGEND'] + }, + { + bossAppId: 'v77n3f1JniCBO6DY', + titleId: BigInt(0x00040000000EC000), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SPAJ'] + }, + { + bossAppId: 'IWu6JntKmBfUFK0D', + titleId: BigInt(0x0004000000105E00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SPFJ'] + }, + { + bossAppId: 'x6bxVnGPunxrUEvR', + titleId: BigInt(0x00040000000B9700), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SN2J'] + }, + { + bossAppId: 'oYzBvnIepXcRAWB8', + titleId: BigInt(0x0004000000155D00), + titleRegion: 'KOR', + name: 'Cooking Mama', + tasks: ['weekly'] + }, + { + bossAppId: 'wSajRGAQdY6dffMd', + titleId: BigInt(0x000400000006C500), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJGJ'] + }, + { + bossAppId: 'riNWIl9p0ZdQORe5', + titleId: BigInt(0x0004000000095700), + titleRegion: 'JPN', + name: 'New Art Academy', + tasks: ['london1'] + }, + { + bossAppId: 'IGctFNjjb8E1TONI', + titleId: BigInt(0x000400000018F100), + titleRegion: 'USA', + name: 'Dragon Quest VIII', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'XT7XAO9p7C9Rfvvx', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JQZJ'] + }, + { + bossAppId: 'LUX1uLHXK82phCiX', + titleId: BigInt(0x00040000001AB900), + titleRegion: 'EUR', + name: 'Team Kirby Clash Deluxe', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'uzKXEvs4hNMA25oG', + titleId: BigInt(0x0004000000137800), + titleRegion: 'EUR', + name: 'Puzzle & Dragons Z + Super Mario Bros. Edition', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'iR1A5X1wiLEkSynO', + titleId: BigInt(0x0004000000167C00), + titleRegion: 'KOR', + name: 'Super Smash Bros. for Nintendo 3DS', + tasks: ['NEWS', 'amiibo', 'FGONLYT'] + }, + { + bossAppId: '24XiNVrAmhfjS82s', + titleId: BigInt(0x0004000000137700), + titleRegion: 'USA', + name: 'Puzzle & Dragons Z + Super Mario Bros. Edition', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'S2OgfKUn0ZVKpO2G', + titleId: BigInt(0x000400000004DC00), + titleRegion: 'UNK', + name: 'Imagine™ Fashion World 3D', + tasks: ['NL00001'] + }, + { + bossAppId: 'drVvdr4bzLr25i9t', + titleId: BigInt(0x00040000001A0000), + titleRegion: 'JPN', + name: 'Team Kirby Clash Deluxe', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'l3gOdKu5CZ45zxTk', + titleId: BigInt(0x000400000009AD00), + titleRegion: 'UNK', + name: 'Foto Glitter 3D', + tasks: ['NadlTsk'] + }, + { + bossAppId: '0AgBH2eLZV5YqTMW', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['0000001'] + }, + { + bossAppId: '4n7UxjbCGnuq5FYy', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['0000001'] + }, + { + bossAppId: '7Cs4OOUfDbCC9su5', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['0000001'] + }, + { + bossAppId: 'Duu3faN13x95aVMY', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['0000001'] + }, + { + bossAppId: 'ERl9vWxc1lKwBCYk', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['0000001'] + }, + { + bossAppId: 'NbmO38IyGwh7nt7t', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['0000001'] + }, + { + bossAppId: 'X00vNlT4qqcyzxlL', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['0000001'] + }, + { + bossAppId: 'XUWPufbAdkuBsj26', + titleId: BigInt(0x0004000000198500), + titleRegion: 'KOR', + name: 'Dragon Quest VIII', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'YqwaX4YDDsNecFub', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['miku_01', 'miku'] + }, + { + bossAppId: 'oX0GI0HwZvOJHMo9', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['0000001'] + }, + { + bossAppId: 'tY7USAZRiWlUgQVe', + titleId: BigInt(0x0004000000047800), + titleRegion: 'USA', + name: 'Imagine™ Fashion Life', + tasks: ['NL00001'] + }, + { + bossAppId: 'wJoxCKvP6AFEHecL', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['0000001'] + }, + { + bossAppId: 'wTy7ND4OqUdfR7nH', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['0000001'] + }, + { + bossAppId: 'xSycjLbQyJJBlc7x', + titleId: BigInt(0x000400000015CD00), + titleRegion: 'JPN', + name: 'Dragon Quest VIII', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'xedkyOsho6iccqUT', + titleId: BigInt(0x000400000018F200), + titleRegion: 'EUR', + name: 'Dragon Quest VIII', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'y1kgSu9oYpgC1oOm', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['0000001'] + }, + { + bossAppId: '2qmhIndG8vluZoEO', + titleId: BigInt(0x000400000008C800), + titleRegion: 'JPN', + name: 'DRAGON QUEST Ⅹ Morph de Battle', + tasks: ['lucky01', 'lucky03', 'lucky02'] + }, + { + bossAppId: 'SDIA7fBPxg6X01AW', + titleId: BigInt(0x0004001000026800), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['PANEL', 'MIIDATA', 'LEGEND', 'PANELLM'] + }, + { + bossAppId: 'oUTbEtsyaJ7Fzx4h', + titleId: BigInt(0x000400000006B600), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJYJ'] + }, + { + bossAppId: 'ZwMc34IYS96zWJRD', + titleId: BigInt(0x00040000000F7F00), + titleRegion: 'EUR', + name: 'Inazuma Eleven 3 Team Ogre Attacks!', + tasks: ['news', 'FGONLYT'] + }, + { + bossAppId: '4mMi9ll6zFrSftt9', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JQ4J'] + }, + { + bossAppId: 'MSN04ONcEZyPL5dW', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SPEJ'] + }, + { + bossAppId: '2hZYkL2D64eCrTzg', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['MESSAGE', 'DL'] + }, + { + bossAppId: 'FMhu53ZtDLnz2ZZ1', + titleId: BigInt(0x0004000000168600), + titleRegion: 'TWN', + name: 'Super Smash Bros. for Nintendo 3DS', + tasks: ['NEWS', 'amiibo', 'FGONLYT'] + }, + { + bossAppId: 'xjMBeunnHUfxuK2d', + titleId: BigInt(0x0004000000151F00), + titleRegion: 'TWN', + name: 'Pokémon Art Academy', + tasks: ['pnote', 'FGONLYT'] + }, + { + bossAppId: 'kuQRrwInvnbJvfa6', + titleId: BigInt(0x0004000000143400), + titleRegion: 'UNK', + name: 'New SUPER MARIO BROS. 2: Gold Edition', + tasks: ['present', 'patch'] + }, + { + bossAppId: 'HWC1oxxoFRhulvSL', + titleId: BigInt(0x00040000000B9600), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNZJ'] + }, + { + bossAppId: 'cxWp2lsjKrG7diwk', + titleId: BigInt(0x00040000000B9200), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNVJ'] + }, + { + bossAppId: 'h8S6cmA4FdBvPYZc', + titleId: BigInt(0x00040000000EC100), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SPBJ'] + }, + { + bossAppId: 'i3iGTnVbl4tKi9KW', + titleId: BigInt(0x00040000000EBF00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SN9J'] + }, + { + bossAppId: 'NxEAqX0zZl4f0Im0', + titleId: BigInt(0x00040000000FA500), + titleRegion: 'JPN', + name: 'クッキングママ5', + tasks: ['weekly'] + }, + { + bossAppId: 'N6VoGN3wJ698hOiI', + titleId: BigInt(0x0004000000106000), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SPHJ'] + }, + { + bossAppId: '9SCQWB4mFxhoXyQK', + titleId: BigInt(0x00040000001AE200), + titleRegion: 'JPN', + name: '大逆転裁判2 ―成歩堂龍ノ介の覺悟―', + tasks: ['SHOLMES'] + }, + { + bossAppId: 'pdR21q4ZRkxaSeVJ', + titleId: BigInt(0x000400000006BC00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJSJ'] + }, + { + bossAppId: '3BraiaIl0mpnuoOA', + titleId: BigInt(0x00040000000AA000), + titleRegion: 'JPN', + name: 'Unknown', + tasks: ['MAJ'] + }, + { + bossAppId: 'YVH4WfoFszQwwG3w', + titleId: BigInt(0x00040000000CCA00), + titleRegion: 'TWN', + name: 'Swapnote', + tasks: ['JFR_LS2', 'JFR_NT1', 'JFR_NT2', 'JFR_NT3', 'JFR_AP2', 'JFR_DFR', 'JFR_GM2', 'JFR_U01', 'JFR_U02', 'JFR_U03', 'JFR_U04', 'JFR_U05', 'JFR_U06', 'JFR_U07', 'JFR_UUS'] + }, + { + bossAppId: 'DciJTpUDHw8APohR', + titleId: BigInt(0x000400000009AB00), + titleRegion: 'UNK', + name: 'Foto-Zauber 3D', + tasks: ['NadlTsk'] + }, + { + bossAppId: 'HLLrgikagMK0pzbs', + titleId: BigInt(0x000400000006B000), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ6J'] + }, + { + bossAppId: 'prddiG9hlRiH8NEC', + titleId: BigInt(0x000400000006AF00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ7J'] + }, + { + bossAppId: 'cmL3N7HUvMJGfGFp', + titleId: BigInt(0x000400000006B100), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ5J'] + }, + { + bossAppId: 'Ryh3zAYKR1zjJWc3', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['zdltdat'] + }, + { + bossAppId: 'HvwgRXicx4vCBFTL', + titleId: BigInt(0x0004000000199000), + titleRegion: 'KOR', + name: 'Animal Crossing: New Leaf - Welcome amiibo', + tasks: ['news', 'news_p', 'dream', 'dream_p', 'FGONLYT'] + }, + { + bossAppId: 'OIC58iiMakdqQSLH', + titleId: BigInt(0x00040000000C4F00), + titleRegion: 'KOR', + name: 'Nintendo presents: New Style Boutique', + tasks: ['GMTM', 'girls', 'info'] + }, + { + bossAppId: 'J0ovIyLP9tYaJqaq', + titleId: BigInt(0x00040000000D1C00), + titleRegion: 'JPN', + name: 'Unknown', + tasks: ['MBE'] + }, + { + bossAppId: 'BAIXhLmD2rGJVAvQ', + titleId: BigInt(0x00040000000AA400), + titleRegion: 'JPN', + name: 'Unknown', + tasks: ['MAL'] + }, + { + bossAppId: 'AOGimz8AhOCZLJks', + titleId: BigInt(0x00040000000D1B00), + titleRegion: 'JPN', + name: 'Unknown', + tasks: ['MBD'] + }, + { + bossAppId: 'MSswwTMHhCzsTg4l', + titleId: BigInt(0x00040000000AA300), + titleRegion: 'JPN', + name: 'Unknown', + tasks: ['MAK'] + }, + { + bossAppId: '83j0DZ0YuEbd6OTR', + titleId: BigInt(0x000400000006B700), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJXJ'] + }, + { + bossAppId: '0kXLgixMNsmA8Ulw', + titleId: BigInt(0x000400000006AE00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ8J'] + }, + { + bossAppId: '34pqkzXxOsXTju6N', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JQBJ'] + }, + { + bossAppId: '3mqMq8p8Vu7clovO', + titleId: BigInt(0x000400000006C800), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJDJ'] + }, + { + bossAppId: '4z69DLEmm8eWmAWh', + titleId: BigInt(0x00040000000AC100), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNNJ'] + }, + { + bossAppId: '7NAqDIHJVMr4Svya', + titleId: BigInt(0x0004000000092F00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JZ7J'] + }, + { + bossAppId: 'A9ikIHjkbhlbkQGZ', + titleId: BigInt(0x000400000006C700), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJEJ'] + }, + { + bossAppId: 'aA9hBmp4EagWTNM5', + titleId: BigInt(0x000400000006AD00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ9J'] + }, + { + bossAppId: 'AY0oNJfCHmoAqyGJ', + titleId: BigInt(0x000400000006C200), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJLJ'] + }, + { + bossAppId: 'BDgoKPBE6iIm6GvU', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JQYJ'] + }, + { + bossAppId: 'BnYCVYAGDPA2ODsF', + titleId: BigInt(0x000400000006BB00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJTJ'] + }, + { + bossAppId: 'bO0kDGfd70dY8jP7', + titleId: BigInt(0x000400000007B500), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JQEJ'] + }, + { + bossAppId: 'bP3RO5aNzfiUhTqG', + titleId: BigInt(0x0004000000110D00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SPKJ'] + }, + { + bossAppId: 'BXzMU4QDn6gJzHL8', + titleId: BigInt(0x000400000006C900), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJCJ'] + }, + { + bossAppId: 'dCdWyWjvMdwC55jp', + titleId: BigInt(0x00040000000B9500), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNYJ'] + }, + { + bossAppId: 'eUDyHqSSYL8Dbnw7', + titleId: BigInt(0x000400000006BE00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJQJ'] + }, + { + bossAppId: 'gH85V9hCAuisHPiI', + titleId: BigInt(0x00040000000A9D00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNSJ'] + }, + { + bossAppId: 'gIKNS5K3QTXk8uZ0', + titleId: BigInt(0x000400000006B300), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ3J'] + }, + { + bossAppId: 'IwPki1shz2Sa5Jtf', + titleId: BigInt(0x000400000006C000), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJNJ'] + }, + { + bossAppId: 'kIk3rjwlpys7cBgb', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'MjC52YHyd1mUxL3K', + titleId: BigInt(0x000400000006B500), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJZJ'] + }, + { + bossAppId: 'Mrv7MaHG7NUKIssp', + titleId: BigInt(0x00040000000ABF00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNLJ'] + }, + { + bossAppId: 'mvFYPcqk08wjN1Lf', + titleId: BigInt(0x0004000000105F00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SPGJ'] + }, + { + bossAppId: 'NSfT7yEow0T98638', + titleId: BigInt(0x000400000007B700), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JQGJ'] + }, + { + bossAppId: 'O5RNJSKQSbaGMWMe', + titleId: BigInt(0x000400000006C300), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJKJ'] + }, + { + bossAppId: 'O7IQrnoLPrUckjY6', + titleId: BigInt(0x000400000006C400), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJHJ'] + }, + { + bossAppId: 'PR5223DvK2yJeLm7', + titleId: BigInt(0x00040000000A0300), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNDJ'] + }, + { + bossAppId: 'QI6XYVa7oCGM7PSb', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SN5J'] + }, + { + bossAppId: 'QIr38311UsvE7kpy', + titleId: BigInt(0x000400000006C600), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJFJ'] + }, + { + bossAppId: 'rbxZVYCrNIIZiV1Y', + titleId: BigInt(0x00040000000B9400), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNXJ'] + }, + { + bossAppId: 'Rk3lD7SViJwXHqxY', + titleId: BigInt(0x00040000000A0100), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNCJ'] + }, + { + bossAppId: 's332O6HVmb1mrZlJ', + titleId: BigInt(0x000400000007B100), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JQAJ'] + }, + { + bossAppId: 'szZ7fUtxGj3C8vAm', + titleId: BigInt(0x000400000006BF00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJPJ'] + }, + { + bossAppId: 'ufWtleWnZVr175yL', + titleId: BigInt(0x000400000006B900), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJVJ'] + }, + { + bossAppId: 'uZp0tj5WSQXC1ajP', + titleId: BigInt(0x00040000000A9C00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNRJ'] + }, + { + bossAppId: 'VaaaQIH2Y4MCHZoE', + titleId: BigInt(0x000400000006B400), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ2J'] + }, + { + bossAppId: 'ValTqsbfLsdprJ9D', + titleId: BigInt(0x00040000000B9300), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNWJ'] + }, + { + bossAppId: 'VJotYqkLqCNb6WEZ', + titleId: BigInt(0x000400000006C100), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJMJ'] + }, + { + bossAppId: 'vWajW15RdM4DoMkY', + titleId: BigInt(0x000400000007B600), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JQFJ'] + }, + { + bossAppId: 'wiDPs6x6gT5GuQ0L', + titleId: BigInt(0x000400000006B800), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJWJ'] + }, + { + bossAppId: 'wIfkwczLlzXY1Ih0', + titleId: BigInt(0x00040000000A0400), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNEJ'] + }, + { + bossAppId: 'WvnKyyWlllNC1P3l', + titleId: BigInt(0x00040000000A9B00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNQJ'] + }, + { + bossAppId: 'x8ak29C2mgHwjQm5', + titleId: BigInt(0x000400000006B200), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJ4J'] + }, + { + bossAppId: 'XAdIxXWPa1PDWMkm', + titleId: BigInt(0x0004000000150A00), + titleRegion: 'UNK', + name: 'Imagine® Collection', + tasks: ['RACHEL'] + }, + { + bossAppId: 'XH9X43PmZ9lGVT7i', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JQ6J'] + }, + { + bossAppId: 'Yhg9sWpNbOT95HTu', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'yMP5bCaKlAgItWqV', + titleId: BigInt(0x00040000000D9200), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SN6J'] + }, + { + bossAppId: 'yoN5MAgfB2yTKrLz', + titleId: BigInt(0x000400000006BA00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JJUJ'] + }, + { + bossAppId: 'zztKIdGngfCS9cS5', + titleId: BigInt(0x00040000000A9A00), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['SNPJ'] + }, + { + bossAppId: 'Bo1owHMnKHjFK2Nk', + titleId: BigInt(0x0004000000143300), + titleRegion: 'UNK', + name: 'New SUPER MARIO BROS. 2: Gold Edition', + tasks: ['patch', 'present'] + }, + { + bossAppId: 'MLJEOnEgGcwd5uaP', + titleId: BigInt(0x00040000000F7C00), + titleRegion: 'UNK', + name: 'Inazuma Eleven 3 Bomb Blast', + tasks: ['news', 'FGONLYT'] + }, + { + bossAppId: '1j4M12R6obSARq8y', + titleId: BigInt(0x0004000000086500), + titleRegion: 'UNK', + name: 'Animal Crossing New Leaf', + tasks: ['news', 'pnews', 'FGONLYT', 'dream', 'dream_p', 'news_p'] + }, + { + bossAppId: '3l15Yk6cOm0AWsSP', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'BUvZ7pfEEhcLt4B2', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['news'] + }, + { + bossAppId: 'Qq4luhjWR94QqvhX', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['daily'] + }, + { + bossAppId: 'f9sKNCJ94RghSSyg', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['GS5'] + }, + { + bossAppId: 'CnnviXAnc0qGGTio', + titleId: BigInt(0x00040000000FEB00), + titleRegion: 'KOR', + name: 'Phoenix Wright: Ace Attorney Dual Destinies', + tasks: ['GS5'] + }, + { + bossAppId: 'fMTQWnFhZQ2BijI5', + titleId: BigInt(0x000400000017B800), + titleRegion: 'TWN', + name: 'Fire Emblem if', + tasks: ['TASK00', 'TASK02'] + }, + { + bossAppId: 'Xv543XtNNYZU2OXz', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'eyCw6Qph1Hz1uQmC', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['JAU'] + }, + { + bossAppId: 'jeUQ3nnY70ddJ87q', + titleId: BigInt(0x000400000009AE00), + titleRegion: 'UNK', + name: 'Cámara Glamour 3D', + tasks: ['NadlTsk'] + }, + { + bossAppId: 'bRWXguqQXXAT99Th', + titleId: BigInt(0x00040000001BC900), + titleRegion: 'TWN', + name: 'Fire Emblem Echoes: Shadows of Valentia', + tasks: ['TASK00', 'FGONLYT', 'TASK01'] + }, + { + bossAppId: '1XCuNDvhVlp5r7mO', + titleId: BigInt(0x00040000000C3900), + titleRegion: 'JPN', + name: 'パズドラZ', + tasks: ['FGONLYT', 'PDZDLDG', 'PDZDLNP', 'PDZDLTI'] + }, + { + bossAppId: '7f094kCjE5HDVGlu', + titleId: BigInt(0x00040000000CBD00), + titleRegion: 'JPN', + name: 'Oshaberi-Chat', + tasks: ['JCTTask'] + }, + { + bossAppId: 'aOJyK9TocYWjxMRR', + titleId: BigInt(0x000400000004A900), + titleRegion: 'UNK', + name: 'Nintendo Video', + tasks: ['ESJ_CNF', 'ESJ_MD1', 'ESJ_MD2', 'ESJ_MD3', 'ESJ_MD4'] + }, + { + bossAppId: 'G0VUurQf4EVYe37v', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['0ER_CNF', '1ER_DBN', '2ER_DCM', '3ER_AX1', '4ER_CX1', '5ER_AX2', '6ER_CX2', '7ER_AX3', '8ER_CX3', '9ER_NTD'] + }, + { + bossAppId: '3PSCke6Q3B84IzWl', + titleId: BigInt(0x0004000000031900), + titleRegion: 'KOR', + name: 'nintendogs + cats', + tasks: ['task_KR'] + }, + { + bossAppId: 'XaphN80NdywuA9Ef', + titleId: BigInt(0x0004000000155E00), + titleRegion: 'KOR', + name: 'Gardening Mama', + tasks: ['add-on'] + }, + { + bossAppId: '0hYP8YjQTWpdlt0v', + titleId: BigInt(0x0004000000031400), + titleRegion: 'KOR', + name: 'nintendogs + cats', + tasks: ['task_KR'] + }, + { + bossAppId: '7HXSa828OHpIlsMh', + titleId: BigInt(0x000400000012C700), + titleRegion: 'KOR', + name: 'Disney Magical World', + tasks: ['BNMCASI', 'BNMCASL', 'FGONLYT'] + }, + { + bossAppId: 'j3JOMyqvsV4ocbUM', + titleId: BigInt(0x0004000000150B00), + titleRegion: 'KOR', + name: 'Pokémon Art Academy', + tasks: ['FGONLYT', 'pnote'] + }, + { + bossAppId: 'l0BkjqC5MI1bEY29', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['Ds2ocTk'] + }, + { + bossAppId: 'sKFrnzQCMkGb60y7', + titleId: BigInt(0x0004000000106300), + titleRegion: 'KOR', + name: 'Mario Golf: World Tour', + tasks: ['FGONLYT', 'spnt_t', 'spnt_x'] + }, + { + bossAppId: '3V3eHMNiNmweRqQO', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'HFnqrJv4fmyO5ItT', + titleId: BigInt(0x0004000000091F00), + titleRegion: 'KOR', + name: 'Model Audition Superstar 2 ', + tasks: ['MO2000', 'MO2001'] + }, + { + bossAppId: 'IyMJMSHvuD2nC8r1', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'JJVi8mFl1LRVk4gS', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'VQU2jX0YCWHynn03', + titleId: BigInt(0x000400000015A700), + titleRegion: 'KOR', + name: 'Puzzle & Dragons Z + Super Mario Bros. Edition', + tasks: ['FGONLYT'] + }, + { + bossAppId: 'cm0cPPTXpEkGEeII', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'y4Q3Z35IQzh6nWds', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['FGONLYT', 'news'] + }, + { + bossAppId: 'aKd4hjqxg87UDbwy', + titleId: BigInt(0x000400000018F900), + titleRegion: 'KOR', + name: 'Phoenix Wright: Ace Attorney Spirit of Justice', + tasks: ['GS6'] + }, + { + bossAppId: '4q11abiKEAOBaBMD', + titleId: BigInt(0x000400000016D100), + titleRegion: 'TWN', + name: 'Puzzle & Dragons Z + Super Mario Bros. Edition', + tasks: ['FGONLYT'] + }, + { + bossAppId: '3yire4GGq6BfeD71', + titleId: BigInt(0x0), + titleRegion: 'UNK', + name: 'Unknown', + tasks: ['news'] } ] }; From 257ef945ec1f475b08797b11b3f6bc6a53f84e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sun, 2 Nov 2025 00:47:22 +0000 Subject: [PATCH 13/30] feat(npts): Implement attributes mode When using this mode, the server only returns the filename, type, size and attributes of a file. --- src/services/npts.ts | 46 ++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/src/services/npts.ts b/src/services/npts.ts index 6e8721f..6332772 100644 --- a/src/services/npts.ts +++ b/src/services/npts.ts @@ -10,18 +10,32 @@ const npts = express.Router(); const xmlHeadSettings = { encoding: 'UTF-8', version: '1.0' }; -function buildFile(task: HydratedTaskDocument, file: HydratedFileWUPDocument): any { - return { - Filename: file.name, - DataId: file.data_id, - Type: file.type, - Url: `https://${config.domains.npdi}/p01/data/1/${task.boss_app_id}/${file.data_id}/${file.hash}`, - Size: file.size, - Notify: { - New: file.notify_on_new.join(','), - LED: file.notify_led - } - }; +function buildFile(task: HydratedTaskDocument, file: HydratedFileWUPDocument, attributesMode: boolean): any { + if (attributesMode) { + return { + Filename: file.name, + Type: file.type, + Size: file.size, + Attributes: { + Attribute1: file.attribute1, + Attribute2: file.attribute2, + Attribute3: file.attribute3, + Description: file.password + } + }; + } else { + return { + Filename: file.name, + DataId: file.data_id, + Type: file.type, + Url: `https://${config.domains.npdi}/p01/data/1/${task.boss_app_id}/${file.data_id}/${file.hash}`, + Size: file.size, + Notify: { + New: file.notify_on_new.join(','), + LED: file.notify_led + } + }; + } } npts.get('/p01/tasksheet/:id/:bossAppId/:taskId', async (request: express.Request<{ @@ -31,10 +45,12 @@ npts.get('/p01/tasksheet/:id/:bossAppId/:taskId', async (request: express.Reques }, any, any, { c?: string; l?: string; + mode?: string; }>, response) => { const { bossAppId, taskId } = request.params; const country = request.query.c; const language = request.query.l; + const mode = request.query.mode; const task = await getTask(bossAppId, taskId); if (!task) { @@ -49,7 +65,7 @@ npts.get('/p01/tasksheet/:id/:bossAppId/:taskId', async (request: express.Reques TaskId: task.id, ServiceStatus: task.status, Files: { - File: files.map(f => buildFile(task, f)) + File: files.map(f => buildFile(task, f, mode === 'attr')) } } }; @@ -66,10 +82,12 @@ npts.get('/p01/tasksheet/:id/:bossAppId/:taskId/:fileName', async (request: expr }, any, any, { c?: string; l?: string; + mode?: string; }>, response) => { const { bossAppId, taskId, fileName } = request.params; const country = request.query.c; const language = request.query.l; + const mode = request.query.mode; const task = await getTask(bossAppId, taskId); if (!task) { @@ -87,7 +105,7 @@ npts.get('/p01/tasksheet/:id/:bossAppId/:taskId/:fileName', async (request: expr TaskId: task.id, ServiceStatus: task.status, Files: { - File: buildFile(task, file) + File: buildFile(task, file, mode === 'attr') } } }; From 99173260f1b6dadee3ea960067822b3e1d8ea6ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sun, 2 Nov 2025 00:54:50 +0000 Subject: [PATCH 14/30] fix(grpc/v2): Fix PIKMIN 3 name on EUR and JPN --- src/services/grpc/boss/v2/list-known-boss-apps.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/grpc/boss/v2/list-known-boss-apps.ts b/src/services/grpc/boss/v2/list-known-boss-apps.ts index 697f0d9..674ad35 100644 --- a/src/services/grpc/boss/v2/list-known-boss-apps.ts +++ b/src/services/grpc/boss/v2/list-known-boss-apps.ts @@ -203,7 +203,7 @@ export async function listKnownBOSSApps(): Promise { bossAppId: '4krJA4Gx3jF5nhQf', titleId: BigInt(0x000500001012BE00), titleRegion: 'EUR', - name: 'ピクミン3 (PIKMIN 3)', + name: 'PIKMIN 3', tasks: ['histgrm'] }, { @@ -217,7 +217,7 @@ export async function listKnownBOSSApps(): Promise { bossAppId: 'VWqUTspR5YtjDjxa', titleId: BigInt(0x000500001012BC00), titleRegion: 'JPN', - name: 'PIKMIN 3', + name: 'ピクミン3 (PIKMIN 3)', tasks: ['histgrm'] }, { From e1bb1b2cd80e08bfe247027a4e3403512f893280 Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Mon, 3 Nov 2025 15:21:36 -0500 Subject: [PATCH 15/30] chore: update @typegoose/auto-increment --- package-lock.json | 42 ++++++++++-------------------------------- package.json | 2 +- src/models/file-wup.ts | 2 +- 3 files changed, 12 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index 73a38e9..7cbeca8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@aws-sdk/client-s3": "^3.723.0", "@pretendonetwork/boss-crypto": "^1.1.3", "@pretendonetwork/grpc": "^2.3.3", - "@typegoose/auto-increment": "^4.13.0", + "@typegoose/auto-increment": "^4.13.1", "commander": "^14.0.0", "cron": "^4.3.3", "dotenv": "^16.4.7", @@ -358,7 +358,6 @@ "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.723.0.tgz", "integrity": "sha512-9IH90m4bnHogBctVna2FnXaIGVORncfdxcqeEIovOxjIJJyHDmEAtA7B91dAM4sruddTbVzOYnqfPVst3odCbA==", - "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -411,7 +410,6 @@ "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.723.0.tgz", "integrity": "sha512-YyN8x4MI/jMb4LpHsLf+VYqvbColMK8aZeGWVk2fTFsmt8lpTYGaGC1yybSwGX42mZ4W8ucu8SAYSbUraJZEjA==", - "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -3163,9 +3161,9 @@ } }, "node_modules/@typegoose/auto-increment": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@typegoose/auto-increment/-/auto-increment-4.13.0.tgz", - "integrity": "sha512-saOwqB66duV+rntkME/027A8opjgzmV3pBY8+zoJ4mGSc3FVGad6CSr56x4oqd15p39XtWH1UNZaS5Bzp6O6Ow==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@typegoose/auto-increment/-/auto-increment-4.13.1.tgz", + "integrity": "sha512-Dv0jlgBo4GkAApZmSxNGhD6eF3LP+1veQGvmNG2/tCNJvCVdTBUlC8ZNdUdTEzQlRRVq5p53qJH18irc5sX2jA==", "license": "MIT", "dependencies": { "loglevel": "^1.9.2", @@ -3391,7 +3389,6 @@ "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.39.1", "@typescript-eslint/types": "8.39.1", @@ -4005,7 +4002,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5049,7 +5045,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -5117,7 +5112,6 @@ "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -5328,7 +5322,6 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7401,7 +7394,6 @@ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.18.1.tgz", "integrity": "sha512-K0RfrUXXufqNRZZjvAGdyjydB91SnbWxlwFYi5t7zN2DxVWFD3c6puia0/7xfBwZm6RCpYOVdYFlRFpoDWiC+w==", "license": "MIT", - "peer": true, "dependencies": { "bson": "^6.10.4", "kareem": "2.6.3", @@ -9147,7 +9139,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -9482,7 +9473,6 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9579,7 +9569,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -10186,7 +10175,6 @@ "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.723.0.tgz", "integrity": "sha512-9IH90m4bnHogBctVna2FnXaIGVORncfdxcqeEIovOxjIJJyHDmEAtA7B91dAM4sruddTbVzOYnqfPVst3odCbA==", - "peer": true, "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -10233,7 +10221,6 @@ "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.723.0.tgz", "integrity": "sha512-YyN8x4MI/jMb4LpHsLf+VYqvbColMK8aZeGWVk2fTFsmt8lpTYGaGC1yybSwGX42mZ4W8ucu8SAYSbUraJZEjA==", - "peer": true, "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -12092,9 +12079,9 @@ } }, "@typegoose/auto-increment": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@typegoose/auto-increment/-/auto-increment-4.13.0.tgz", - "integrity": "sha512-saOwqB66duV+rntkME/027A8opjgzmV3pBY8+zoJ4mGSc3FVGad6CSr56x4oqd15p39XtWH1UNZaS5Bzp6O6Ow==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@typegoose/auto-increment/-/auto-increment-4.13.1.tgz", + "integrity": "sha512-Dv0jlgBo4GkAApZmSxNGhD6eF3LP+1veQGvmNG2/tCNJvCVdTBUlC8ZNdUdTEzQlRRVq5p53qJH18irc5sX2jA==", "requires": { "loglevel": "^1.9.2", "tslib": "^2.8.1" @@ -12290,7 +12277,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.1.tgz", "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", "dev": true, - "peer": true, "requires": { "@typescript-eslint/scope-manager": "8.39.1", "@typescript-eslint/types": "8.39.1", @@ -12631,8 +12617,7 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "peer": true + "dev": true }, "acorn-jsx": { "version": "5.3.2", @@ -13356,7 +13341,6 @@ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "dev": true, - "peer": true, "requires": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", @@ -13407,7 +13391,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.33.0.tgz", "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, - "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -13589,7 +13572,6 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, - "peer": true, "requires": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -14923,7 +14905,6 @@ "version": "8.18.1", "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.18.1.tgz", "integrity": "sha512-K0RfrUXXufqNRZZjvAGdyjydB91SnbWxlwFYi5t7zN2DxVWFD3c6puia0/7xfBwZm6RCpYOVdYFlRFpoDWiC+w==", - "peer": true, "requires": { "bson": "^6.10.4", "kareem": "2.6.3", @@ -16105,8 +16086,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "peer": true + "dev": true } } }, @@ -16326,8 +16306,7 @@ "typescript": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "peer": true + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==" }, "typescript-eslint": { "version": "8.39.1", @@ -16384,7 +16363,6 @@ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", "dev": true, - "peer": true, "requires": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", diff --git a/package.json b/package.json index 2392789..7e0cf81 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@aws-sdk/client-s3": "^3.723.0", "@pretendonetwork/boss-crypto": "^1.1.3", "@pretendonetwork/grpc": "^2.3.3", - "@typegoose/auto-increment": "^4.13.0", + "@typegoose/auto-increment": "^4.13.1", "commander": "^14.0.0", "cron": "^4.3.3", "dotenv": "^16.4.7", diff --git a/src/models/file-wup.ts b/src/models/file-wup.ts index b329a18..d9ed6dd 100644 --- a/src/models/file-wup.ts +++ b/src/models/file-wup.ts @@ -8,7 +8,7 @@ const FileWUPSchema = new mongoose.Schema Date: Mon, 3 Nov 2025 15:31:09 -0500 Subject: [PATCH 16/30] chore: convert FileWUP model to use the same attribute structure as the FileCTR model --- src/models/file-wup.ts | 10 ++++++---- src/services/grpc/boss/v1/list-files.ts | 8 ++++---- .../grpc/boss/v1/update-file-metadata.ts | 8 ++++---- src/services/grpc/boss/v1/upload-file.ts | 8 ++++---- src/services/grpc/boss/v2/list-files-wup.ts | 7 +------ .../grpc/boss/v2/update-file-metadata-wup.ts | 5 +---- src/services/grpc/boss/v2/upload-file-wup.ts | 18 ++++++++++++------ src/services/npts.ts | 8 ++++---- src/types/mongoose/file-wup.ts | 10 ++++++---- 9 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/models/file-wup.ts b/src/models/file-wup.ts index d9ed6dd..1494ba9 100644 --- a/src/models/file-wup.ts +++ b/src/models/file-wup.ts @@ -13,10 +13,12 @@ const FileWUPSchema = new mongoose.Schema Date: Mon, 3 Nov 2025 15:32:31 -0500 Subject: [PATCH 17/30] chore: remove BigInt casts in ListKnownBOSSApps gRPC methods --- .../grpc/boss/v1/list-known-boss-apps.ts | 114 +- .../grpc/boss/v2/list-known-boss-apps.ts | 1308 ++++++++--------- 2 files changed, 711 insertions(+), 711 deletions(-) diff --git a/src/services/grpc/boss/v1/list-known-boss-apps.ts b/src/services/grpc/boss/v1/list-known-boss-apps.ts index ebb3da9..81c5446 100644 --- a/src/services/grpc/boss/v1/list-known-boss-apps.ts +++ b/src/services/grpc/boss/v1/list-known-boss-apps.ts @@ -5,399 +5,399 @@ export async function listKnownBOSSApps(): Promise { apps: [ { bossAppId: 'WJDaV6ePVgrS0TRa', - titleId: BigInt(0x0005003010016000), + titleId: 0x0005003010016000n, titleRegion: 'UNK', name: 'Unknown', tasks: ['olvinfo'] }, { bossAppId: 'VFoY6V7u7UUq1EG5', - titleId: BigInt(0x0005003010016100), + titleId: 0x0005003010016100n, titleRegion: 'UNK', name: 'Unknown', tasks: ['olvinfo'] }, { bossAppId: '8MNOVprfNVAJjfCM', - titleId: BigInt(0x0005003010016200), + titleId: 0x0005003010016200n, titleRegion: 'UNK', name: 'Unknown', tasks: ['olvinfo'] }, { bossAppId: 'v1cqzWykBKUg0rHQ', - titleId: BigInt(0x000500301001900A), + titleId: 0x000500301001900An, titleRegion: 'JPN', name: 'Miiverse Post All', tasks: ['solv'] }, { bossAppId: 'bieC9ACJlisFg5xS', - titleId: BigInt(0x000500301001910A), + titleId: 0x000500301001910An, titleRegion: 'USA', name: 'Miiverse Post All', tasks: ['solv'] }, { bossAppId: 'tOaQcoBLtPTgVN3Y', - titleId: BigInt(0x000500301001920A), + titleId: 0x000500301001920An, titleRegion: 'EUR', name: 'Miiverse Post All', tasks: ['solv'] }, { bossAppId: 'HX8a16MMNn6i1z0Y', - titleId: BigInt(0x000500301001400A), + titleId: 0x000500301001400An, titleRegion: 'JPN', name: 'Nintendo eShop', tasks: ['wood1', 'woodBGM'] }, { bossAppId: '07E3nY6lAwlwrQRo', - titleId: BigInt(0x000500301001410A), + titleId: 0x000500301001410An, titleRegion: 'USA', name: 'Nintendo eShop', tasks: ['wood1', 'woodBGM'] }, { bossAppId: '8UsM86l8xgkjFk8z', - titleId: BigInt(0x000500301001420A), + titleId: 0x000500301001420An, titleRegion: 'EUR', name: 'Nintendo eShop', tasks: ['wood1', 'woodBGM'] }, { bossAppId: 'IXmFUqR2qenXfF61', - titleId: BigInt(0x0005001010066000), + titleId: 0x0005001010066000n, titleRegion: 'ALL', name: 'ECO Process', tasks: ['promo1', 'promo2', 'promo3', 'push'] }, { bossAppId: 'BMQAm5iUVtPsJVsU', - titleId: BigInt(0x000500101004D000), + titleId: 0x000500101004D000n, titleRegion: 'JPN', name: 'Notifications', tasks: ['sysmsg1', 'sysmsg2'] }, { bossAppId: 'LRmanFo4Tx3kEGDp', - titleId: BigInt(0x000500101004D100), + titleId: 0x000500101004D100n, titleRegion: 'USA', name: 'Notifications', tasks: ['sysmsg1', 'sysmsg2'] }, { bossAppId: 'TZr27FE8wzKiEaTO', - titleId: BigInt(0x000500101004D200), + titleId: 0x000500101004D200n, titleRegion: 'EUR', name: 'Notifications', tasks: ['sysmsg1', 'sysmsg2'] }, { bossAppId: 'JnIrm9c4E9JBmxBo', - titleId: BigInt(0x0005000010185200), + titleId: 0x0005000010185200n, titleRegion: 'JPN', name: 'NewスーパーマリオブラザーズU 無料お試し版 (New SUPER MARIO BROS. U (Trial))', tasks: ['news'] }, { bossAppId: 'dadlI27Ww8H2d56x', - titleId: BigInt(0x0005000010101C00), + titleId: 0x0005000010101C00n, titleRegion: 'JPN', name: 'NewスーパーマリオブラザーズU (New SUPER MARIO BROS. U)', tasks: ['news'] }, { bossAppId: 'RaPn5saabzliYrpo', - titleId: BigInt(0x0005000010101D00), + titleId: 0x0005000010101D00n, titleRegion: 'USA', name: 'New SUPER MARIO BROS. U', tasks: ['news'] }, { bossAppId: '14VFIK3rY2SP0WRE', - titleId: BigInt(0x0005000010101E00), + titleId: 0x0005000010101E00n, titleRegion: 'EUR', name: 'New SUPER MARIO BROS. U', tasks: ['news'] }, { bossAppId: 'RbEQ44t2AocC4rvu', - titleId: BigInt(0x000500001014B700), + titleId: 0x000500001014B700n, titleRegion: 'USA', name: 'New SUPER MARIO BROS. U + New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: '287gv3WZdxo1QRhl', - titleId: BigInt(0x000500001014B800), + titleId: 0x000500001014B800n, titleRegion: 'EUR', name: 'New SUPER MARIO BROS. U + New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: 'bb6tOEckvgZ50ciH', - titleId: BigInt(0x0005000010162B00), + titleId: 0x0005000010162B00n, titleRegion: 'JPN', name: 'スプラトゥーン (Splatoon)', tasks: ['optdat2', 'schdat2', 'schdata'] }, { bossAppId: 'rjVlM7hUXPxmYQJh', - titleId: BigInt(0x0005000010176900), + titleId: 0x0005000010176900n, titleRegion: 'USA', name: 'Splatoon', tasks: ['optdat2', 'schdat2', 'schdata', 'optdata2', 'schdata2'] }, { bossAppId: 'zvGSM4kOrXpkKnpT', - titleId: BigInt(0x0005000010176A00), + titleId: 0x0005000010176A00n, titleRegion: 'EUR', name: 'Splatoon', tasks: ['optdat2', 'schdat2', 'schdata', 'optdata'] }, { bossAppId: 'm8KJPtmPweiPuETE', - titleId: BigInt(0x000500001012F100), + titleId: 0x000500001012F100n, titleRegion: 'JPN', name: 'Wii Sports Club', tasks: ['sp1_ans'] }, { bossAppId: 'pO72Hi5uqf5yuNd8', - titleId: BigInt(0x0005000010144D00), + titleId: 0x0005000010144D00n, titleRegion: 'USA', name: 'Wii Sports Club', tasks: ['sp1_ans'] }, { bossAppId: '4m8Xme1wKgzwslTJ', - titleId: BigInt(0x0005000010144E00), + titleId: 0x0005000010144E00n, titleRegion: 'EUR', name: 'Wii Sports Club', tasks: ['sp1_ans'] }, { bossAppId: 'ESLqtAhxS8KQU4eu', - titleId: BigInt(0x000500001018DB00), + titleId: 0x000500001018DB00n, titleRegion: 'JPN', name: 'Super Mario Maker (スーパーマリオメーカー)', tasks: ['CHARA'] }, { bossAppId: 'vGwChBW1ExOoHDsm', - titleId: BigInt(0x000500001018DC00), + titleId: 0x000500001018DC00n, titleRegion: 'USA', name: 'Super Mario Maker', tasks: ['CHARA'] }, { bossAppId: 'IeUc4hQsKKe9rJHB', - titleId: BigInt(0x000500001018DD00), + titleId: 0x000500001018DD00n, titleRegion: 'EUA', name: 'Super Mario Maker', tasks: ['CHARA'] }, { bossAppId: '4krJA4Gx3jF5nhQf', - titleId: BigInt(0x000500001012BC00), + titleId: 0x000500001012BC00n, titleRegion: 'JPN', name: 'ピクミン3 (PIKMIN 3)', tasks: ['histgrm'] }, { bossAppId: '9jRZEoWYLc3OG9a8', - titleId: BigInt(0x000500001012BD00), + titleId: 0x000500001012BD00n, titleRegion: 'USA', name: 'PIKMIN 3', tasks: ['histgrm'] }, { bossAppId: 'VWqUTspR5YtjDjxa', - titleId: BigInt(0x000500001012BE00), + titleId: 0x000500001012BE00n, titleRegion: 'EUR', name: 'PIKMIN 3', tasks: ['histgrm'] }, { bossAppId: 'Ge1KtMu8tYlf4AUM', - titleId: BigInt(0x0005000010192000), + titleId: 0x0005000010192000n, titleRegion: 'JPN', name: '太鼓の達人 特盛り! (Taiko no Tatsujin Tokumori!)', tasks: ['notice1'] }, { bossAppId: 'gycVtTzCouZmukZ6', - titleId: BigInt(0x0005000010110E00), + titleId: 0x0005000010110E00n, titleRegion: 'JPN', name: '大乱闘スマッシュブラザーズ for Wii U (Super Smash Bros. for Wii U)', tasks: ['NEWS', 'amiibo'] }, { bossAppId: 'o2Ug1pIp9Uhri6Nh', - titleId: BigInt(0x0005000010144F00), + titleId: 0x0005000010144F00n, titleRegion: 'USA', name: 'Super Smash Bros. for Wii U', tasks: ['amiibo', 'NEWS', 'friend', 'CONQ'] }, { bossAppId: 'n6rAJ1nnfC1Sgcpl', - titleId: BigInt(0x0005000010145000), + titleId: 0x0005000010145000n, titleRegion: 'EUR', name: 'Super Smash Bros. for Wii U', tasks: ['amiibo', 'NEWS', 'friend', 'CONQ'] }, { bossAppId: 'CHUN6T1m7Xk4EBg4', - titleId: BigInt(0x00050000101DFF00), + titleId: 0x00050000101DFF00n, titleRegion: 'JPN', name: 'プチコンBIG (Petitcom BIG)', tasks: ['ptcbnws'] }, { bossAppId: 'zyXdCW9jGdi9rjaz', - titleId: BigInt(0x0005000010142200), + titleId: 0x0005000010142200n, titleRegion: 'JPN', name: 'NewスーパールイージU (New SUPER LUIGI U)', tasks: ['news'] }, { bossAppId: 'jPHLlJr2fJyTzffp', - titleId: BigInt(0x0005000010142300), + titleId: 0x0005000010142300n, titleRegion: 'USA', name: 'New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: 'YsXB6IRGSI56tPxl', - titleId: BigInt(0x0005000010142400), + titleId: 0x0005000010142400n, titleRegion: 'EUR', name: 'New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: 'Lbqp9Sg1i0xUzFFa', - titleId: BigInt(0x0005000010113800), + titleId: 0x0005000010113800n, titleRegion: 'EUR', name: 'Zen Pinball 2', tasks: ['PTS'] }, { bossAppId: 'DwU7n0FidGrLNiOo', - titleId: BigInt(0x000500001014D900), + titleId: 0x000500001014D900n, titleRegion: 'JPN', name: 'ぷよぷよテトリス (PUYOPUYOTETRIS)', tasks: ['boss1', 'boss2', 'boss3'] }, { bossAppId: 'yIUkFmuGVkGP8pDb', - titleId: BigInt(0x0005000010132200), + titleId: 0x0005000010132200n, titleRegion: 'JPN', name: '太鼓の達人 Wii Uば~じょん! (Taiko no Tatsujin Wii U version!)', tasks: ['notice1'] }, { bossAppId: 'v4WRObSzD7VU3dcJ', - titleId: BigInt(0x00050000101D3000), + titleId: 0x00050000101D3000n, titleRegion: 'JPN', name: '太鼓の達人 あつめて★ともだち大作戦! (Taiko no Tatsujin Atsumete★ TomodachiDaisakusen!)', tasks: ['notice1'] }, { bossAppId: '3zDjXIA57bSceyaw', - titleId: BigInt(0x00050000101BEC00), + titleId: 0x00050000101BEC00n, titleRegion: 'USA', name: 'Star Fox Guard', tasks: ['param'] }, { bossAppId: 'NL38jhExI2CQqhWd', - titleId: BigInt(0x00050000101CDB00), + titleId: 0x00050000101CDB00n, titleRegion: 'JPN', name: 'Splatoon Pre-Launch Review', tasks: ['schdata'] }, { bossAppId: 'sE6KwEpQYyg6tdU7', - titleId: BigInt(0x00050000101CDC00), + titleId: 0x00050000101CDC00n, titleRegion: 'USA', name: 'Splatoon Pre-Launch Review', tasks: ['schdata'] }, { bossAppId: 'pTKZ9q5KrCP3gBag', - titleId: BigInt(0x00050000101CDD00), + titleId: 0x00050000101CDD00n, titleRegion: 'EUR', name: 'Splatoon Pre-Launch Review', tasks: ['schdata'] }, { bossAppId: 'CJT88RO008LAnD51', - titleId: BigInt(0x0005000010170600), + titleId: 0x0005000010170600n, titleRegion: 'JPN', name: '仮面ライダー バトライド・ウォーⅡ プレミアムTV&MOVIEサウンドED. (KAMEN RIDER BATTRIDE WAR Ⅱ PREMIUM TV&MOVIE SOUND ED.)', tasks: ['PE_GAK', 'PE_ZNG'] }, { bossAppId: 'FyyMFzEByuQJc6sJ', - titleId: BigInt(0x0005000010135200), + titleId: 0x0005000010135200n, titleRegion: 'USA', name: 'Star Wars Pinball', tasks: ['PTS'] }, { bossAppId: 'A4yyXWKZZUToFtrt', - titleId: BigInt(0x0005000010132A00), + titleId: 0x0005000010132A00n, titleRegion: 'EUR', name: 'Star Wars Pinball', tasks: ['PTS'] }, { bossAppId: 'HauaFQ1sPsnQ6rBj', - titleId: BigInt(0x0005000010171F00), + titleId: 0x0005000010171F00n, titleRegion: 'USA', name: 'Pushmo World', tasks: ['annouce'] }, { bossAppId: 'qDUeFmk0Az71nHyD', - titleId: BigInt(0x0005000010110900), + titleId: 0x0005000010110900n, titleRegion: 'JPN', name: 'NINJA GAIDEN 3: Razor\'s Edge', tasks: ['DLCINFO'] }, { bossAppId: 'yVsSPM2E0DEOxroT', - titleId: BigInt(0x0005000010110A00), + titleId: 0x0005000010110A00n, titleRegion: 'USA', name: 'NINJA GAIDEN 3: Razor\'s Edge', tasks: ['DLCINFO'] }, { bossAppId: 'Xw6OvZkQofQ3O8Bi', - titleId: BigInt(0x0005000010110B00), + titleId: 0x0005000010110B00n, titleRegion: 'EUR', name: 'Ninja Gaiden 3: Razor\'s Edge', tasks: ['DLCINFO'] }, { bossAppId: 'LUQX5swEjBUPQ8nR', - titleId: BigInt(0x0005000010110200), + titleId: 0x0005000010110200n, titleRegion: 'USA', name: 'WARRIORS OROCHI 3 Hyper(NA)', tasks: ['OR2H000'] }, { bossAppId: 'y4pXrgLe0JGao3No', - titleId: BigInt(0x0005000010112B00), + titleId: 0x0005000010112B00n, titleRegion: 'EUR', name: 'WARRIORS OROCHI 3 Hyper(EU)', tasks: ['OR2H000'] }, { bossAppId: 'j01mRJ9sNe00MWPC', - titleId: BigInt(0x0005000010170700), + titleId: 0x0005000010170700n, titleRegion: 'JPN', name: '仮面ライダー バトライド・ウォーⅡ (KAMEN RIDER BATTRIDE WAR Ⅱ)', tasks: ['CHR_GAK', 'CHR_ZNG'] diff --git a/src/services/grpc/boss/v2/list-known-boss-apps.ts b/src/services/grpc/boss/v2/list-known-boss-apps.ts index 674ad35..ae1d666 100644 --- a/src/services/grpc/boss/v2/list-known-boss-apps.ts +++ b/src/services/grpc/boss/v2/list-known-boss-apps.ts @@ -5,4578 +5,4578 @@ export async function listKnownBOSSApps(): Promise { apps: [ { bossAppId: 'WJDaV6ePVgrS0TRa', - titleId: BigInt(0x0005003010016000), + titleId: 0x0005003010016000n, titleRegion: 'UNK', name: 'Unknown', tasks: ['olvinfo'] }, { bossAppId: 'VFoY6V7u7UUq1EG5', - titleId: BigInt(0x0005003010016100), + titleId: 0x0005003010016100n, titleRegion: 'UNK', name: 'Unknown', tasks: ['olvinfo', 'oltopic'] }, { bossAppId: '8MNOVprfNVAJjfCM', - titleId: BigInt(0x0005003010016200), + titleId: 0x0005003010016200n, titleRegion: 'UNK', name: 'Unknown', tasks: ['olvinfo'] }, { bossAppId: 'v1cqzWykBKUg0rHQ', - titleId: BigInt(0x000500301001900A), + titleId: 0x000500301001900An, titleRegion: 'JPN', name: 'Miiverse Post All', tasks: ['solv'] }, { bossAppId: 'bieC9ACJlisFg5xS', - titleId: BigInt(0x000500301001910A), + titleId: 0x000500301001910An, titleRegion: 'USA', name: 'Miiverse Post All', tasks: ['solv'] }, { bossAppId: 'tOaQcoBLtPTgVN3Y', - titleId: BigInt(0x000500301001920A), + titleId: 0x000500301001920An, titleRegion: 'EUR', name: 'Miiverse Post All', tasks: ['solv'] }, { bossAppId: 'HX8a16MMNn6i1z0Y', - titleId: BigInt(0x000500301001400A), + titleId: 0x000500301001400An, titleRegion: 'JPN', name: 'Nintendo eShop', tasks: ['wood1', 'woodBGM'] }, { bossAppId: '07E3nY6lAwlwrQRo', - titleId: BigInt(0x000500301001410A), + titleId: 0x000500301001410An, titleRegion: 'USA', name: 'Nintendo eShop', tasks: ['wood1', 'woodBGM'] }, { bossAppId: '8UsM86l8xgkjFk8z', - titleId: BigInt(0x000500301001420A), + titleId: 0x000500301001420An, titleRegion: 'EUR', name: 'Nintendo eShop', tasks: ['wood1', 'woodBGM'] }, { bossAppId: 'IXmFUqR2qenXfF61', - titleId: BigInt(0x0005001010066000), + titleId: 0x0005001010066000n, titleRegion: 'ALL', name: 'ECO Process', tasks: ['promo1', 'promo2', 'promo3', 'push'] }, { bossAppId: 'BMQAm5iUVtPsJVsU', - titleId: BigInt(0x000500101004D000), + titleId: 0x000500101004D000n, titleRegion: 'JPN', name: 'Notifications', tasks: ['sysmsg1', 'sysmsg2'] }, { bossAppId: 'LRmanFo4Tx3kEGDp', - titleId: BigInt(0x000500101004D100), + titleId: 0x000500101004D100n, titleRegion: 'USA', name: 'Notifications', tasks: ['sysmsg1', 'sysmsg2'] }, { bossAppId: 'TZr27FE8wzKiEaTO', - titleId: BigInt(0x000500101004D200), + titleId: 0x000500101004D200n, titleRegion: 'EUR', name: 'Notifications', tasks: ['sysmsg1', 'sysmsg2'] }, { bossAppId: 'JnIrm9c4E9JBmxBo', - titleId: BigInt(0x0005000010185200), + titleId: 0x0005000010185200n, titleRegion: 'JPN', name: 'NewスーパーマリオブラザーズU 無料お試し版 (New SUPER MARIO BROS. U (Trial))', tasks: ['news'] }, { bossAppId: 'dadlI27Ww8H2d56x', - titleId: BigInt(0x0005000010101C00), + titleId: 0x0005000010101C00n, titleRegion: 'JPN', name: 'NewスーパーマリオブラザーズU (New SUPER MARIO BROS. U)', tasks: ['news', 'plyrepo'] }, { bossAppId: 'RaPn5saabzliYrpo', - titleId: BigInt(0x0005000010101D00), + titleId: 0x0005000010101D00n, titleRegion: 'USA', name: 'New SUPER MARIO BROS. U', tasks: ['news', 'plyrepo'] }, { bossAppId: '14VFIK3rY2SP0WRE', - titleId: BigInt(0x0005000010101E00), + titleId: 0x0005000010101E00n, titleRegion: 'EUR', name: 'New SUPER MARIO BROS. U', tasks: ['news', 'plyrepo'] }, { bossAppId: 'RbEQ44t2AocC4rvu', - titleId: BigInt(0x000500001014B700), + titleId: 0x000500001014B700n, titleRegion: 'USA', name: 'New SUPER MARIO BROS. U + New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: '287gv3WZdxo1QRhl', - titleId: BigInt(0x000500001014B800), + titleId: 0x000500001014B800n, titleRegion: 'EUR', name: 'New SUPER MARIO BROS. U + New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: 'bb6tOEckvgZ50ciH', - titleId: BigInt(0x0005000010162B00), + titleId: 0x0005000010162B00n, titleRegion: 'JPN', name: 'スプラトゥーン (Splatoon)', tasks: ['optdat2', 'schdat2', 'schdata', 'optdata'] }, { bossAppId: 'rjVlM7hUXPxmYQJh', - titleId: BigInt(0x0005000010176900), + titleId: 0x0005000010176900n, titleRegion: 'USA', name: 'Splatoon', tasks: ['optdat2', 'schdat2', 'schdata', 'optdata2', 'schdata2', 'test', 'preport', 'otpdata2', 'scddata2', 'otpdat2', 'optdata'] }, { bossAppId: 'zvGSM4kOrXpkKnpT', - titleId: BigInt(0x0005000010176A00), + titleId: 0x0005000010176A00n, titleRegion: 'EUR', name: 'Splatoon', tasks: ['optdat2', 'schdat2', 'schdata', 'optdata'] }, { bossAppId: 'm8KJPtmPweiPuETE', - titleId: BigInt(0x000500001012F100), + titleId: 0x000500001012F100n, titleRegion: 'JPN', name: 'Wii Sports Club', tasks: ['sp1_ans', 'sp1_rnk', 'sp1_evt'] }, { bossAppId: 'pO72Hi5uqf5yuNd8', - titleId: BigInt(0x0005000010144D00), + titleId: 0x0005000010144D00n, titleRegion: 'USA', name: 'Wii Sports Club', tasks: ['sp1_ans', 'sp1_rnk', 'sp1_evt'] }, { bossAppId: '4m8Xme1wKgzwslTJ', - titleId: BigInt(0x0005000010144E00), + titleId: 0x0005000010144E00n, titleRegion: 'EUR', name: 'Wii Sports Club', tasks: ['sp1_ans', 'sp1_rnk', 'sp1_evt'] }, { bossAppId: 'ESLqtAhxS8KQU4eu', - titleId: BigInt(0x000500001018DB00), + titleId: 0x000500001018DB00n, titleRegion: 'JPN', name: 'Super Mario Maker (スーパーマリオメーカー)', tasks: ['CHARA'] }, { bossAppId: 'vGwChBW1ExOoHDsm', - titleId: BigInt(0x000500001018DC00), + titleId: 0x000500001018DC00n, titleRegion: 'USA', name: 'Super Mario Maker', tasks: ['CHARA'] }, { bossAppId: 'IeUc4hQsKKe9rJHB', - titleId: BigInt(0x000500001018DD00), + titleId: 0x000500001018DD00n, titleRegion: 'EUR', name: 'Super Mario Maker', tasks: ['CHARA'] }, { bossAppId: '4krJA4Gx3jF5nhQf', - titleId: BigInt(0x000500001012BE00), + titleId: 0x000500001012BE00n, titleRegion: 'EUR', name: 'PIKMIN 3', tasks: ['histgrm'] }, { bossAppId: '9jRZEoWYLc3OG9a8', - titleId: BigInt(0x000500001012BD00), + titleId: 0x000500001012BD00n, titleRegion: 'USA', name: 'PIKMIN 3', tasks: ['histgrm'] }, { bossAppId: 'VWqUTspR5YtjDjxa', - titleId: BigInt(0x000500001012BC00), + titleId: 0x000500001012BC00n, titleRegion: 'JPN', name: 'ピクミン3 (PIKMIN 3)', tasks: ['histgrm'] }, { bossAppId: 'Ge1KtMu8tYlf4AUM', - titleId: BigInt(0x0005000010192000), + titleId: 0x0005000010192000n, titleRegion: 'JPN', name: '太鼓の達人 特盛り! (Taiko no Tatsujin Tokumori!)', tasks: ['notice1'] }, { bossAppId: 'gycVtTzCouZmukZ6', - titleId: BigInt(0x0005000010110E00), + titleId: 0x0005000010110E00n, titleRegion: 'JPN', name: '大乱闘スマッシュブラザーズ for Wii U (Super Smash Bros. for Wii U)', tasks: ['NEWS', 'amiibo', 'friend', 'CONQ'] }, { bossAppId: 'o2Ug1pIp9Uhri6Nh', - titleId: BigInt(0x0005000010144F00), + titleId: 0x0005000010144F00n, titleRegion: 'USA', name: 'Super Smash Bros. for Wii U', tasks: ['amiibo', 'NEWS', 'friend', 'CONQ'] }, { bossAppId: 'n6rAJ1nnfC1Sgcpl', - titleId: BigInt(0x0005000010145000), + titleId: 0x0005000010145000n, titleRegion: 'EUR', name: 'Super Smash Bros. for Wii U', tasks: ['amiibo', 'NEWS', 'friend', 'CONQ'] }, { bossAppId: 'CHUN6T1m7Xk4EBg4', - titleId: BigInt(0x00050000101DFF00), + titleId: 0x00050000101DFF00n, titleRegion: 'JPN', name: 'プチコンBIG (Petitcom BIG)', tasks: ['ptcbnws'] }, { bossAppId: 'zyXdCW9jGdi9rjaz', - titleId: BigInt(0x0005000010142200), + titleId: 0x0005000010142200n, titleRegion: 'JPN', name: 'NewスーパールイージU (New SUPER LUIGI U)', tasks: ['news'] }, { bossAppId: 'jPHLlJr2fJyTzffp', - titleId: BigInt(0x0005000010142300), + titleId: 0x0005000010142300n, titleRegion: 'USA', name: 'New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: 'YsXB6IRGSI56tPxl', - titleId: BigInt(0x0005000010142400), + titleId: 0x0005000010142400n, titleRegion: 'EUR', name: 'New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: 'Lbqp9Sg1i0xUzFFa', - titleId: BigInt(0x0005000010113800), + titleId: 0x0005000010113800n, titleRegion: 'EUR', name: 'Zen Pinball 2', tasks: ['PTS'] }, { bossAppId: 'DwU7n0FidGrLNiOo', - titleId: BigInt(0x000500001014D900), + titleId: 0x000500001014D900n, titleRegion: 'JPN', name: 'ぷよぷよテトリス (PUYOPUYOTETRIS)', tasks: ['boss1', 'boss2', 'boss3'] }, { bossAppId: 'yIUkFmuGVkGP8pDb', - titleId: BigInt(0x0005000010132200), + titleId: 0x0005000010132200n, titleRegion: 'JPN', name: '太鼓の達人 Wii Uば~じょん! (Taiko no Tatsujin Wii U version!)', tasks: ['notice1'] }, { bossAppId: 'v4WRObSzD7VU3dcJ', - titleId: BigInt(0x00050000101D3000), + titleId: 0x00050000101D3000n, titleRegion: 'JPN', name: '太鼓の達人 あつめて★ともだち大作戦! (Taiko no Tatsujin Atsumete★ TomodachiDaisakusen!)', tasks: ['notice1'] }, { bossAppId: '3zDjXIA57bSceyaw', - titleId: BigInt(0x00050000101BEC00), + titleId: 0x00050000101BEC00n, titleRegion: 'USA', name: 'Star Fox Guard', tasks: ['param'] }, { bossAppId: 'NL38jhExI2CQqhWd', - titleId: BigInt(0x00050000101CDB00), + titleId: 0x00050000101CDB00n, titleRegion: 'JPN', name: 'Splatoon Pre-Launch Review', tasks: ['schdata', 'optdata'] }, { bossAppId: 'sE6KwEpQYyg6tdU7', - titleId: BigInt(0x00050000101CDC00), + titleId: 0x00050000101CDC00n, titleRegion: 'USA', name: 'Splatoon Pre-Launch Review', tasks: ['schdata', 'optdata'] }, { bossAppId: 'pTKZ9q5KrCP3gBag', - titleId: BigInt(0x00050000101CDD00), + titleId: 0x00050000101CDD00n, titleRegion: 'EUR', name: 'Splatoon Pre-Launch Review', tasks: ['schdata', 'optdata'] }, { bossAppId: 'CJT88RO008LAnD51', - titleId: BigInt(0x0005000010170600), + titleId: 0x0005000010170600n, titleRegion: 'JPN', name: '仮面ライダー バトライド・ウォーⅡ プレミアムTV&MOVIEサウンドED. (KAMEN RIDER BATTRIDE WAR Ⅱ PREMIUM TV&MOVIE SOUND ED.)', tasks: ['PE_GAK', 'PE_ZNG'] }, { bossAppId: 'FyyMFzEByuQJc6sJ', - titleId: BigInt(0x0005000010135200), + titleId: 0x0005000010135200n, titleRegion: 'USA', name: 'Star Wars Pinball', tasks: ['PTS'] }, { bossAppId: 'A4yyXWKZZUToFtrt', - titleId: BigInt(0x0005000010132A00), + titleId: 0x0005000010132A00n, titleRegion: 'EUR', name: 'Star Wars Pinball', tasks: ['PTS'] }, { bossAppId: 'HauaFQ1sPsnQ6rBj', - titleId: BigInt(0x0005000010171F00), + titleId: 0x0005000010171F00n, titleRegion: 'USA', name: 'Pushmo World', tasks: ['annouce'] }, { bossAppId: 'qDUeFmk0Az71nHyD', - titleId: BigInt(0x0005000010110900), + titleId: 0x0005000010110900n, titleRegion: 'JPN', name: 'NINJA GAIDEN 3: Razor\'s Edge', tasks: ['DLCINFO'] }, { bossAppId: 'yVsSPM2E0DEOxroT', - titleId: BigInt(0x0005000010110A00), + titleId: 0x0005000010110A00n, titleRegion: 'USA', name: 'NINJA GAIDEN 3: Razor\'s Edge', tasks: ['DLCINFO'] }, { bossAppId: 'Xw6OvZkQofQ3O8Bi', - titleId: BigInt(0x0005000010110B00), + titleId: 0x0005000010110B00n, titleRegion: 'EUR', name: 'NINJA GAIDEN 3: Razor\'s Edge', tasks: ['DLCINFO'] }, { bossAppId: 'LUQX5swEjBUPQ8nR', - titleId: BigInt(0x0005000010110200), + titleId: 0x0005000010110200n, titleRegion: 'USA', name: 'WARRIORS OROCHI 3 Hyper(NA)', tasks: ['OR2H000'] }, { bossAppId: 'y4pXrgLe0JGao3No', - titleId: BigInt(0x0005000010112B00), + titleId: 0x0005000010112B00n, titleRegion: 'EUR', name: 'WARRIORS OROCHI 3 Hyper(EU)', tasks: ['OR2H000'] }, { bossAppId: 'j01mRJ9sNe00MWPC', - titleId: BigInt(0x0005000010170700), + titleId: 0x0005000010170700n, titleRegion: 'JPN', name: '仮面ライダー バトライド・ウォーⅡ (KAMEN RIDER BATTRIDE WAR Ⅱ)', tasks: ['CHR_GAK', 'CHR_ZNG'] }, { bossAppId: 'P45xuCJjERf6MNWG', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['movie'] }, { bossAppId: 'PQWAfUmDpVo0u9Fi', - titleId: BigInt(0x0005000010111C00), + titleId: 0x0005000010111C00n, titleRegion: 'JPN', name: 'Romance of the Three Kingdoms 12', tasks: ['Card'] }, { bossAppId: 'EA9wpEnmZmeX70YS', - titleId: BigInt(0x0005000010192200), + titleId: 0x0005000010192200n, titleRegion: 'JPN', name: 'KAMEN RIDER SUMMON RIDE!', tasks: ['ADDCHR0'] }, { bossAppId: 'Iq5CNAngvR9auXFO', - titleId: BigInt(0x00050000101BED00), + titleId: 0x00050000101BED00n, titleRegion: 'EUR', name: 'Star Fox Guard', tasks: ['param'] }, { bossAppId: 'ZtwtVqJkmGE2LloD', - titleId: BigInt(0x0005000010115F00), + titleId: 0x0005000010115F00n, titleRegion: 'USA', name: 'Zen Pinball 2', tasks: ['PTS'] }, { bossAppId: 'eAzIbHvwKNHwz85M', - titleId: BigInt(0x0005000010116400), + titleId: 0x0005000010116400n, titleRegion: 'JPN', name: 'niconico', tasks: ['news'] }, { bossAppId: 'z4d72slRF5GX0cEr', - titleId: BigInt(0x0005000010172000), + titleId: 0x0005000010172000n, titleRegion: 'EUR', name: 'Pullblox World', tasks: ['annouce'] }, { bossAppId: '5iKeqk6fQq3wwfgy', - titleId: BigInt(0x000500001010EA00), + titleId: 0x000500001010EA00n, titleRegion: 'JPN', name: 'WARRIORS OROCHI 3 Hyper(JP)', tasks: ['OR2H000'] }, { bossAppId: 'rvI5oS5jSZ0aLpeo', - titleId: BigInt(0x0005000011000000), + titleId: 0x0005000011000000n, titleRegion: 'UNK', name: 'Unknown', tasks: ['demo1'] }, { bossAppId: 'R5WU9gZtFShZlf6j', - titleId: BigInt(0x000500001010ED00), + titleId: 0x000500001010ED00n, titleRegion: 'EUR', name: 'MARIO KART 8', tasks: ['movie', 'Histo'] }, { bossAppId: '78QqMzbyBbwEpzVg', - titleId: BigInt(0x0005000010111700), + titleId: 0x0005000010111700n, titleRegion: 'USA', name: 'Injustice: Gods Among Us', tasks: ['Tvars'] }, { bossAppId: 'I8IZTXQyDnUnFo77', - titleId: BigInt(0x0005000010111A00), + titleId: 0x0005000010111A00n, titleRegion: 'EUR', name: 'Injustice: Gods Among Us', tasks: ['Tvars'] }, { bossAppId: 'XcawL2u1CU624gg3', - titleId: BigInt(0x0005000010185300), + titleId: 0x0005000010185300n, titleRegion: 'JPN', name: 'PIKMIN 3 (Trial)', tasks: ['histgrm'] }, { bossAppId: 'uNRNThGetHLXasV9', - titleId: BigInt(0x00050000101BEB00), + titleId: 0x00050000101BEB00n, titleRegion: 'JPN', name: 'Star Fox Guard', tasks: ['param'] }, { bossAppId: 'MBOU6MNVQRdTT1QA', - titleId: BigInt(0x00050000101DCC00), + titleId: 0x00050000101DCC00n, titleRegion: 'JPN', name: 'Star Fox Guard Special Demo', tasks: ['param'] }, { bossAppId: 'zBlJpj2pXcFeJYJI', - titleId: BigInt(0x00050000101DCE00), + titleId: 0x00050000101DCE00n, titleRegion: 'EUR', name: 'Star Fox Guard: Special Demo Version', tasks: ['param'] }, { bossAppId: 'dHWbU7brnq9QKaKA', - titleId: BigInt(0x00050000101DCD00), + titleId: 0x00050000101DCD00n, titleRegion: 'USA', name: 'Star Fox Guard Special Demo', tasks: ['param'] }, { bossAppId: 'gjYbE1NbQerS5v6n', - titleId: BigInt(0x0005000010149000), + titleId: 0x0005000010149000n, titleRegion: 'JPN', name: 'Romance of the Three Kingdoms 12 with Powerup kit', tasks: ['Card'] }, { bossAppId: '07SSacDlEHc8z0jg', - titleId: BigInt(0x0004000000155000), + titleId: 0x0004000000155000n, titleRegion: 'JPN', name: 'ディズニー マジックキャッスル マイ・ハッピー・ライフ2', tasks: ['FGONLYT', 'MC2NWS'] }, { bossAppId: '0hFlOFo7pNTU2dyE', - titleId: BigInt(0x00040000001A2D00), + titleId: 0x00040000001A2D00n, titleRegion: 'USA', name: 'Swapdoodle', tasks: ['RNG_EC1', 'RNG_LS1', 'RNG_MD1', 'RNG_NT1', 'RNG_NT2', 'RNG_GM1', 'RNG_AP1', 'RNG_DFR', 'RNG_U01', 'RNG_U02', 'RNG_U03', 'RNG_U04', 'RNG_U05', 'RNG_U06', 'RNG_U07', 'RNG_UUS'] }, { bossAppId: '10Og6tqFdXrW1Dra', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: '18ZHPbhuhUMZmLMS', - titleId: BigInt(0x00040000001D6C00), + titleId: 0x00040000001D6C00n, titleRegion: 'UNK', name: 'Yo-kai Watch 3', tasks: ['FGONLYT', 'news'] }, { bossAppId: '1dNhxKHa1kgzz0gj', - titleId: BigInt(0x0004000000174E00), + titleId: 0x0004000000174E00n, titleRegion: 'JPN', name: 'MEDAROT9 KABUTO Ver.', tasks: ['MEDA9'] }, { bossAppId: '2eMXT6CAAcQIYWuN', - titleId: BigInt(0x000400000016C700), + titleId: 0x000400000016C700n, titleRegion: 'JPN', name: 'Yokai Watch Busters Akaneko Dan', tasks: ['FGONLYT', 'news'] }, { bossAppId: '2s640xAtrZGOWdq0', - titleId: BigInt(0x0004000000051700), + titleId: 0x0004000000051700n, titleRegion: 'USA', name: 'Swapnote', tasks: ['JFR_LS2', 'JFR_NT2', 'JFR_NT1', 'JFR_NT3', 'JFR_AP2', 'JFR_GM2', 'JFR_DNT', 'JFR_DLS', 'JFR_DAP', 'JFR_DGM', 'JFR_DFR', 'JFR_U01', 'JFR_U02', 'JFR_U03', 'JFR_U04', 'JFR_U05', 'JFR_U06', 'JFR_U07', 'JFR_U08', 'JFR_U09', 'JFR_U10'] }, { bossAppId: '2zDwgq1t61PlMaPq', - titleId: BigInt(0x000400000012DE00), + titleId: 0x000400000012DE00n, titleRegion: 'UNK', name: 'ファイアーエムブレム if', tasks: ['TASK00', 'TASK02'] }, { bossAppId: '3ddVFPLZpzu77yvS', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['daily'] }, { bossAppId: '3EHaOtNKDsD3Ybk8', - titleId: BigInt(0x000400000016C600), + titleId: 0x000400000016C600n, titleRegion: 'JPN', name: 'Yokai Watch Busters Shiroinu Tai', tasks: ['FGONLYT', 'news'] }, { bossAppId: '3isXVXrb2lLqmrW0', - titleId: BigInt(0x00040000000CB900), + titleId: 0x00040000000CB900n, titleRegion: 'USA', name: 'Nintendo 3DS Guide: Louvre (English Version)', tasks: ['AL8_001'] }, { bossAppId: '3t2IUj3ASUzKKEHK', - titleId: BigInt(0x00040000001B4100), + titleId: 0x00040000001B4100n, titleRegion: 'EUR', name: 'Fire Emblem Echoes: Shadows of Valentia', tasks: ['TASK00', 'TASK01'] }, { bossAppId: '3vveLadT8H6xKkQH', - titleId: BigInt(0x00040000001A2E00), + titleId: 0x00040000001A2E00n, titleRegion: 'EUR', name: 'Swapdoodle', tasks: ['RNG_EC1', 'RNG_LS1', 'RNG_MD1', 'RNG_NT1', 'RNG_NT2', 'RNG_GM1', 'RNG_AP1', 'RNG_DFR', 'RNG_U01', 'RNG_U02', 'RNG_U03', 'RNG_U04', 'RNG_U05', 'RNG_U06', 'RNG_U07', 'RNG_UUS'] }, { bossAppId: '4LvdQ9tJBCOyHrv4', - titleId: BigInt(0x0004000000030700), + titleId: 0x0004000000030700n, titleRegion: 'EUR', name: 'MARIO KART 7', tasks: ['comm', 'ghost', 'ranking'] }, { bossAppId: '4OBVxt1uzhPW4cGR', - titleId: BigInt(0x0004000000118100), + titleId: 0x0004000000118100n, titleRegion: 'JPN', name: 'DETECTIVE CONAN PHANTOM RHAPSODY', tasks: ['BKRJ-00', 'BKRJ', 'FGONLYT'] }, { bossAppId: '53R1vYbkfqXlqzns', - titleId: BigInt(0x0004000000030600), + titleId: 0x0004000000030600n, titleRegion: 'JPN', name: 'マリオカート7', tasks: ['comm', 'ghost', 'ranking'] }, { bossAppId: '5xq3tXtlGqUd7MbV', - titleId: BigInt(0x00040000000EDF00), + titleId: 0x00040000000EDF00n, titleRegion: 'USA', name: 'Super Smash Bros. for Nintendo 3DS', tasks: ['NEWS', 'amiibo', 'FGONLYT'] }, { bossAppId: '5yLkA3wqcXruNE2N', - titleId: BigInt(0x000400000008C300), + titleId: 0x000400000008C300n, titleRegion: 'USA', name: 'Tomodachi Life', tasks: ['tmTaskA', 'tmTaskD'] }, { bossAppId: '6jk78e80CzvtPvim', - titleId: BigInt(0x0004000000095000), + titleId: 0x0004000000095000n, titleRegion: 'JPN', name: 'Pokemon Mystery Dungeon Magna gate and infinity labyrinth', tasks: ['PAINF00'] }, { bossAppId: '6SKM87Ll80ONzAvQ', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: '7EnMOQ6WfZSmvQgu', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: '7knOkHlr5db97LJ6', - titleId: BigInt(0x00040000001B4000), + titleId: 0x00040000001B4000n, titleRegion: 'USA', name: 'Fire Emblem Echoes: Shadows of Valentia', tasks: ['TASK00', 'TASK01'] }, { bossAppId: '7NdLNTN4iH9wEbJQ', - titleId: BigInt(0x00040000000D0900), + titleId: 0x00040000000D0900n, titleRegion: 'EUR', name: 'Pokémon Art Academy', tasks: ['FGONLYT', 'pnote'] }, { bossAppId: '7vzbLQCS84rtXY0y', - titleId: BigInt(0x0004000000140000), + titleId: 0x0004000000140000n, titleRegion: 'JPN', name: 'シアトリズム ドラゴンクエスト', tasks: ['FGONLYT', 'TDQ01'] }, { bossAppId: '8DeC0vBA5VDzPu8x', - titleId: BigInt(0x00040000000A7700), + titleId: 0x00040000000A7700n, titleRegion: 'JPN', name: 'AKB48+Me', tasks: ['AKBadd'] }, { bossAppId: '8DViaPfyfH1pGO91', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: '8mX4hRmZqkDHn0SE', - titleId: BigInt(0x000400000010BB00), + titleId: 0x000400000010BB00n, titleRegion: 'JPN', name: 'イナズマイレブンGO ギャラクシー スーパーノヴァ', tasks: ['FGONLYT', 'news'] }, { bossAppId: '9x4m4dJwyBBlUc3g', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'demotask', 'flist', 'test'] }, { bossAppId: 'ac2P6aIvORxnQJwK', - titleId: BigInt(0x0004000000147100), + titleId: 0x0004000000147100n, titleRegion: 'USA', name: 'Little Battlers eXperience', tasks: ['FGONLYT'] }, { bossAppId: 'adQnSUvJlXp5igaT', - titleId: BigInt(0x00040000001D6A00), + titleId: 0x00040000001D6A00n, titleRegion: 'UNK', name: 'Yo-kai Watch 3', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'aFMv95FiWHC3k7XV', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'AWKlN4KN0rMA7Gqc', - titleId: BigInt(0x0004000000113400), + titleId: 0x0004000000113400n, titleRegion: 'JPN', name: 'MEDAROT8 KUWAGATA Ver.1.1', tasks: ['MEDA8'] }, { bossAppId: 'AZcAJ2Stxa9P8h7a', - titleId: BigInt(0x0004000000072A00), + titleId: 0x0004000000072A00n, titleRegion: 'JPN', name: 'Dynasty Warriors VS', tasks: ['SMVS_EC', 'SMVS_SC', 'SMVS_PR'] }, { bossAppId: 'B6Iqt2r0EGU549NW', - titleId: BigInt(0x0004000000030800), + titleId: 0x0004000000030800n, titleRegion: 'USA', name: 'MARIO KART 7', tasks: ['comm', 'ghost', 'ranking'] }, { bossAppId: 'b8RPtQj41Mw5HjoX', - titleId: BigInt(0x00040000001D6700), + titleId: 0x00040000001D6700n, titleRegion: 'USA', name: 'Yo-kai Watch 3', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'bGoDxPNf97rXq9a5', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['present', 'patch'] }, { bossAppId: 'BgyAdVTkMfLTzM0k', - titleId: BigInt(0x0004000000167600), + titleId: 0x0004000000167600n, titleRegion: 'KOR', name: 'YO-KAI WATCH', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'bOMC45IF1taPnYx8', - titleId: BigInt(0x0004000000168C00), + titleId: 0x0004000000168C00n, titleRegion: 'UNK', name: 'Little Battlers eXperience', tasks: ['FGONLYT'] }, { bossAppId: 'ciTu5gGHcW767kOc', - titleId: BigInt(0x0004000000101300), + titleId: 0x0004000000101300n, titleRegion: 'JPN', name: 'ヒーローバンク', tasks: ['FGONLYT'] }, { bossAppId: 'cPYaUEX74KmPR0wF', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['present', 'patch'] }, { bossAppId: 'CtfKXACbUPl8s7lk', - titleId: BigInt(0x0004001000021900), + titleId: 0x0004001000021900n, titleRegion: 'UNK', name: 'Unknown', tasks: ['BGM1', 'BGM2', 'TIGER1'] }, { bossAppId: 'cXo6TOh0AtrCzaCP', - titleId: BigInt(0x0004000000051800), + titleId: 0x0004000000051800n, titleRegion: 'EUR', name: 'Nintendo Letter Box', tasks: ['JFR_LS2', 'JFR_AP2', 'JFR_NT1', 'JFR_NT2', 'JFR_NT3', 'JFR_DLS', 'JFR_DNT', 'JFR_DAP', 'JFR_DGM', 'JFR_GM2', 'JFR_DFR', 'JFR_U01', 'JFR_U02', 'JFR_U03', 'JFR_U04', 'JFR_U05', 'JFR_U06', 'JFR_U07', 'JFR_U08', 'JFR_U09', 'JFR_U10'] }, { bossAppId: 'd4495LzgtcxUObka', - titleId: BigInt(0x0004000000065A00), + titleId: 0x0004000000065A00n, titleRegion: 'JPN', name: 'MEDAROT7 KUWAGATA Ver.1.1', tasks: ['MEDA7'] }, { bossAppId: 'DJBXc6TzoubPYfy6', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['weekly'] }, { bossAppId: 'dpwg7hD86KlFwcbk', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['daily'] }, { bossAppId: 'DUE2YwOq2m9fVsdZ', - titleId: BigInt(0x000400000012A800), + titleId: 0x000400000012A800n, titleRegion: 'JPN', name: 'hoppechan minnadeodekake! wakuwaku hoppeland!!Ver.1.2', tasks: ['FGONLYT'] }, { bossAppId: 'DwQNLZT8QlZIxyAJ', - titleId: BigInt(0x0004000000107C00), + titleId: 0x0004000000107C00n, titleRegion: 'USA', name: 'Chibi-Robo! Photo Finder', tasks: ['mesdat', 'dat'] }, { bossAppId: 'e4rcoYW9QTHc2whz', - titleId: BigInt(0x0004000000155100), + titleId: 0x0004000000155100n, titleRegion: 'JPN', name: 'Yokai Watch 2 shinuchi', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'E5myNZzoWVHdNUvY', - titleId: BigInt(0x0004000000113300), + titleId: 0x0004000000113300n, titleRegion: 'JPN', name: 'MEDAROT8 KABUTO Ver.1.1', tasks: ['MEDA8'] }, { bossAppId: 'eH6oowgSM7n662mw', - titleId: BigInt(0x000400000008C400), + titleId: 0x000400000008C400n, titleRegion: 'EUR', name: 'Tomodachi Life', tasks: ['tmTaskA', 'tmTaskD', 'tmTaskU'] }, { bossAppId: 'f5xYhmZvwKo4uv5A', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['data', 'news'] }, { bossAppId: 'fbAzg6nuN4hhQ3pJ', - titleId: BigInt(0x00040000001B2900), + titleId: 0x00040000001B2900n, titleRegion: 'EUR', name: 'YO-KAI WATCH 2: PSYCHIC SPECTERS', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'fFV9HxPJR7NJRTre', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'FNY6HPPbPj2jIErD', - titleId: BigInt(0x0004000000072400), + titleId: 0x0004000000072400n, titleRegion: 'UNK', name: 'Unknown', tasks: ['Amb_eu', 'Amb'] }, { bossAppId: 'fQZi0N3YlWWq07AZ', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['item'] }, { bossAppId: 'fsYqLFLQngg32A1l', - titleId: BigInt(0x00040000000D5600), + titleId: 0x00040000000D5600n, titleRegion: 'USA', name: 'Nintendo 3DS Guide: Louvre (Version française)', tasks: ['AL8_001'] }, { bossAppId: 'G06t7Q3mkOy95nBw', - titleId: BigInt(0x00040000001CEB00), + titleId: 0x00040000001CEB00n, titleRegion: 'USA', name: 'YO-KAI WATCH BLASTERS RED CAT CORPS', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'G4uDyFA9Yc06kV8m', - titleId: BigInt(0x00040000000B6E00), + titleId: 0x00040000000B6E00n, titleRegion: 'USA', name: 'Crashmo', tasks: ['JAU'] }, { bossAppId: 'g6DKUfhQUHKd5gZz', - titleId: BigInt(0x00040000001CA800), + titleId: 0x00040000001CA800n, titleRegion: 'EUR', name: 'LAYTON\'S MYSTERY JOURNEY™ Katrielle and the Millionaires\'...', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'GjTM1Bphz05wTMtO', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['daily'] }, { bossAppId: 'gOlIVAcjDpj6K1Ut', - titleId: BigInt(0x0004000000166A00), + titleId: 0x0004000000166A00n, titleRegion: 'JPN', name: 'Phoenix Wright: Ace Attorney Spirit of Justice', tasks: ['GS6'] }, { bossAppId: 'GQkLSOsKwIpvr8Yi', - titleId: BigInt(0x000400000007AE00), + titleId: 0x000400000007AE00n, titleRegion: 'USA', name: 'New Super Mario Bros. 2', tasks: ['present', 'patch'] }, { bossAppId: 'GQqblssCbq6PuCyJ', - titleId: BigInt(0x0004000000174F00), + titleId: 0x0004000000174F00n, titleRegion: 'JPN', name: 'MEDAROT9 KUWAGATA Ver.', tasks: ['MEDA9'] }, { bossAppId: 'guBwm9TlQvYvncKn', - titleId: BigInt(0x000400000011C500), + titleId: 0x000400000011C500n, titleRegion: 'JPN', name: 'Pokémon Alpha Sapphire', tasks: ['horogra', 'FGONLYT'] }, { bossAppId: 'GxCs83sbgwaoL2js', - titleId: BigInt(0x00040000001AE600), + titleId: 0x00040000001AE600n, titleRegion: 'JPN', name: '100% PASUKARU SENSEI Perfect Paint Bombers', tasks: ['FGONLYT', 'POSTING'] }, { bossAppId: 'H9GUJGE1xWr9VrgG', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['present', 'patch'] }, { bossAppId: 'hA0Mq0T7KQofzsFF', - titleId: BigInt(0x00040000000CBA00), + titleId: 0x00040000000CBA00n, titleRegion: 'KOR', name: 'Nintendo 3DS Guide: Louvre', tasks: ['AL8_001'] }, { bossAppId: 'i9omdntnCo1GPdHA', - titleId: BigInt(0x00040000001CB400), + titleId: 0x00040000001CB400n, titleRegion: 'UNK', name: 'LAYTON\'S MYSTERY JOURNEY™ Katrielle and the Millionaires\'...', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'iolEjnbGp2W1ghtO', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'iYhju0xUdEBftgzw', - titleId: BigInt(0x0004000000167700), + titleId: 0x0004000000167700n, titleRegion: 'USA', name: 'YO-KAI WATCH', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'j0ITmVqVgfUxe0O9', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['data', 'FGONLYT', 'news'] }, { bossAppId: 'J3u1c5M8Ff9Y9TyG', - titleId: BigInt(0x00040000000F0500), + titleId: 0x00040000000F0500n, titleRegion: 'UNK', name: '大合奏!バンドブラザーズP しもべツール', tasks: ['FGONLYT'] }, { bossAppId: 'j5cz6H9mjSt8wvLX', - titleId: BigInt(0x0004000000086300), + titleId: 0x0004000000086300n, titleRegion: 'UNK', name: 'Animal Crossing New Leaf', tasks: ['FGONLYT', 'dlvexb', 'news', 'news_p', 'pnews', 'dream', 'dream_p'] }, { bossAppId: 'J6la9Kj8iqTvAPOq', - titleId: BigInt(0x0004000000153600), + titleId: 0x0004000000153600n, titleRegion: 'EUR', name: 'Nintendo Badge Arcade Ver.1.3.1', tasks: ['data', 'FGONLYT', 'news'] }, { bossAppId: 'j9wEm4RKrgNuaudD', - titleId: BigInt(0x0004000000176E00), + titleId: 0x0004000000176E00n, titleRegion: 'JPN', name: 'The Legend of Zelda Tri Force Heroes', tasks: ['Info_00', 'Data', 'Data_00', 'Data_01'] }, { bossAppId: 'JfttU8Wg1iBIbbIs', - titleId: BigInt(0x0004000000053700), + titleId: 0x0004000000053700n, titleRegion: 'JPN', name: 'エクストルーパーズ', tasks: ['EXT0100'] }, { bossAppId: 'JIguVGEJJOhAq2te', - titleId: BigInt(0x00040000001A2B00), + titleId: 0x00040000001A2B00n, titleRegion: 'JPN', name: 'Fire Emblem Echoes: Shadows of Valentia', tasks: ['TASK00', 'FGONLYT', 'TASK01'] }, { bossAppId: 'JqhCCnj1t7Zid1SG', - titleId: BigInt(0x000400000010BA00), + titleId: 0x000400000010BA00n, titleRegion: 'JPN', name: 'イナズマイレブンGO ギャラクシー ビッグバン', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'jy95T3iOBKyGbAXl', - titleId: BigInt(0x00040000000CF900), + titleId: 0x00040000000CF900n, titleRegion: 'JPN', name: 'Pokémon Art Academy', tasks: ['FGONLYT', 'pnote'] }, { bossAppId: 'kb6KM6y6fQx3bOWs', - titleId: BigInt(0x00040000000D0A00), + titleId: 0x00040000000D0A00n, titleRegion: 'USA', name: 'Pokémon Art Academy', tasks: ['FGONLYT', 'pnote'] }, { bossAppId: 'kQGzeGpga1cXwbtf', - titleId: BigInt(0x0004000000065B00), + titleId: 0x0004000000065B00n, titleRegion: 'JPN', name: 'MEDAROT7 KABUTO Ver.1.1', tasks: ['MEDA7'] }, { bossAppId: 'kSz44ZF73HpIb4O7', - titleId: BigInt(0x000400000008B400), + titleId: 0x000400000008B400n, titleRegion: 'TWN', name: 'MARIO KART 7', tasks: ['comm', 'ghost', 'ranking'] }, { bossAppId: 'kTnNBa7mg66E11iV', - titleId: BigInt(0x0004000000030A00), + titleId: 0x0004000000030A00n, titleRegion: 'KOR', name: 'MARIO KART 7', tasks: ['ghost', 'ranking', 'comm'] }, { bossAppId: 'L1P2RB6s878vioGs', - titleId: BigInt(0x0004000000030100), + titleId: 0x0004000000030100n, titleRegion: 'USA', name: 'Kid Icarus: Uprising', tasks: ['SEED'] }, { bossAppId: 'lg9TdjOIyCO7aUR4', - titleId: BigInt(0x00040000000CF400), + titleId: 0x00040000000CF400n, titleRegion: 'JPN', name: 'Yokai Watch', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'lhz3HfuATBPF4EdY', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['ESE_CNF', 'ESE_NWS'] }, { bossAppId: 'Lju5rPwt0QgXXh7Q', - titleId: BigInt(0x00040000000CB700), + titleId: 0x00040000000CB700n, titleRegion: 'JPN', name: 'Nintendo 3DS Guide: Louvre', tasks: ['AL8_001'] }, { bossAppId: 'llh2zGNci2sULyVX', - titleId: BigInt(0x00040000000DCA00), + titleId: 0x00040000000DCA00n, titleRegion: 'JPN', name: 'ダンボール戦機W 超カスタム', tasks: ['FGONLYT'] }, { bossAppId: 'Lw62Mz00pmT1osSJ', - titleId: BigInt(0x00040000000C3C00), + titleId: 0x00040000000C3C00n, titleRegion: 'JPN', name: 'Rodea the Sky Soldier', tasks: ['FGONLYT'] }, { bossAppId: 'lX2cH6BQVJFyjCfN', - titleId: BigInt(0x00040000001D6B00), + titleId: 0x00040000001D6B00n, titleRegion: 'UNK', name: 'Yo-kai Watch 3', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'Lx3bOup9RTIcePKE', - titleId: BigInt(0x00040000000FD500), + titleId: 0x00040000000FD500n, titleRegion: 'USA', name: 'THEATRHYTHM FINAL FANTASY CURTAIN CALL', tasks: ['DUOINFO'] }, { bossAppId: 'M0EKKJWDaHUDcmRr', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'M7HNbBKOBj4NTTor', - titleId: BigInt(0x0004000000124A00), + titleId: 0x0004000000124A00n, titleRegion: 'JPN', name: 'リアル脱出ゲームxニンテンドー3DS 超破壊計画からの脱出', tasks: ['FGONLYT', 'info'] }, { bossAppId: 'mb2iVt7j6Wy6yxkb', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['daily'] }, { bossAppId: 'MEXh2UqOKVgJS3h3', - titleId: BigInt(0x0004000000169D00), + titleId: 0x0004000000169D00n, titleRegion: 'JPN', name: 'アイカツ! My No.1 Stage!', tasks: ['AKT410a'] }, { bossAppId: 'MqPfuz5l3ptcMj23', - titleId: BigInt(0x00040000000D5700), + titleId: 0x00040000000D5700n, titleRegion: 'EUR', name: 'Nintendo 3DS Guide: Louvre (Deutsche Version)', tasks: ['AL8_001'] }, { bossAppId: 'n1cddgQeTxunOG2K', - titleId: BigInt(0x00040000001B4F00), + titleId: 0x00040000001B4F00n, titleRegion: 'EUR', name: 'Miitopia', tasks: ['Enquete', 'MiiDL'] }, { bossAppId: 'NSWeucPFtbNNCx07', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['data', 'news'] }, { bossAppId: 'NU0xisOCz9UY0IBu', - titleId: BigInt(0x0004000000197200), + titleId: 0x0004000000197200n, titleRegion: 'JPN', name: 'アイカツスターズ! Myスペシャルアピール', tasks: ['AKT5PKG'] }, { bossAppId: 'NXIWsA2Gm4EH219s', - titleId: BigInt(0x0004000000183100), + titleId: 0x0004000000183100n, titleRegion: 'KOR', name: 'Rodea the Sky Soldier', tasks: ['FGONLYT'] }, { bossAppId: 'OFypMD7hNCrRPthA', - titleId: BigInt(0x0004000000179400), + titleId: 0x0004000000179400n, titleRegion: 'USA', name: 'Fire Emblem Fates Birthright', tasks: ['TASK00', 'TASK02'] }, { bossAppId: 'oGD8mLvDr8YY1L9E', - titleId: BigInt(0x00040000001C7900), + titleId: 0x00040000001C7900n, titleRegion: 'UNK', name: 'LAYTON\'S MYSTERY JOURNEY™ Katrielle and the Millionaires\'...', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'OkqvIoTur40udixa', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'Omwq2nEEfqZ9yo9S', - titleId: BigInt(0x00040000001CEC00), + titleId: 0x00040000001CEC00n, titleRegion: 'EUR', name: 'YO-KAI WATCH BLASTERS RED CAT CORPS', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'oPuD6PkiPpx1EFiy', - titleId: BigInt(0x00040000001C1800), + titleId: 0x00040000001C1800n, titleRegion: 'JPN', name: 'The SNACK WORLD TREJARERS', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'oUF988iLJo3cWLji', - titleId: BigInt(0x00040000001D6900), + titleId: 0x00040000001D6900n, titleRegion: 'UNK', name: 'Yo-kai Watch 3', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'OvbmGLZ9senvgV3K', - titleId: BigInt(0x0004000000153500), + titleId: 0x0004000000153500n, titleRegion: 'USA', name: 'Nintendo Badge Arcade Ver.1.3.1', tasks: ['data', 'FGONLYT', 'news'] }, { bossAppId: 'p1of6zTyKCnqOITZ', - titleId: BigInt(0x000400000009F100), + titleId: 0x000400000009F100n, titleRegion: 'EUR', name: 'Fire Emblem: Awakening', tasks: ['Quartz0', 'Quartz1'] }, { bossAppId: 'p47ZQwc3R4qAmAYD', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['allhp', 'FGONLYT'] }, { bossAppId: 'p5HtkudRvY55R212', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'PKeGb0m4S3Gv5jis', - titleId: BigInt(0x0004000000188E00), + titleId: 0x0004000000188E00n, titleRegion: 'KOR', name: 'Fire Emblem Fates', tasks: ['TASK00', 'TASK02'] }, { bossAppId: 'PpjtFCm4bQskyBUy', - titleId: BigInt(0x00040000000BA900), + titleId: 0x00040000000BA900n, titleRegion: 'EUR', name: 'Pokémon Mystery Dungeon Gates to Infinity', tasks: ['PAINF00'] }, { bossAppId: 'PV3ljR2w0UwrQfWk', - titleId: BigInt(0x00040000001B2800), + titleId: 0x00040000001B2800n, titleRegion: 'UNK', name: 'YO-KAI WATCH 2: PSYCHIC SPECTERS', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'PzpV1tz303wO66AD', - titleId: BigInt(0x00040000001CEF00), + titleId: 0x00040000001CEF00n, titleRegion: 'USA', name: 'YO-KAI WATCH BLASTERS WHITE DOG SQUAD', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'pZWhL0tyf4FMCt8r', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'qAM6zmNEtT45AgUa', - titleId: BigInt(0x0004000000072300), + titleId: 0x0004000000072300n, titleRegion: 'UNK', name: 'Unknown', tasks: ['Amb_jp', 'Amb'] }, { bossAppId: 'qCcCCCLDwNMea0nq', - titleId: BigInt(0x0004000000176F00), + titleId: 0x0004000000176F00n, titleRegion: 'USA', name: 'The Legend of Zelda Tri Force Heroes', tasks: ['info_00', 'Data', 'Data_00', 'Data_01'] }, { bossAppId: 'QDlGiLVY6lFsOhV3', - titleId: BigInt(0x0004000000101200), + titleId: 0x0004000000101200n, titleRegion: 'JPN', name: 'Puyopuyo Tetris', tasks: ['boss1', 'boss2', 'boss3', 'FGONLYT'] }, { bossAppId: 'qdy7ZP05GyiwgV7L', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'QfJQIU7IDmkaUuLt', - titleId: BigInt(0x0004000000072500), + titleId: 0x0004000000072500n, titleRegion: 'UNK', name: 'Unknown', tasks: ['Amb_us', 'Amb'] }, { bossAppId: 'qIe6CqkMDj1IaHoD', - titleId: BigInt(0x000400000014E000), + titleId: 0x000400000014E000n, titleRegion: 'JPN', name: 'アイカツ! 365日のアイドルデイズ', tasks: ['AKT365I'] }, { bossAppId: 'QOnECUiVKl7wigDk', - titleId: BigInt(0x000400000005C300), + titleId: 0x000400000005C300n, titleRegion: 'JPN', name: 'スライムもりもりドラゴンクエスト3 大海賊としっぽ団', tasks: ['Strike1'] }, { bossAppId: 'Qp03w0NGhViaEw4w', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'RA470KStGjDbVn4O', - titleId: BigInt(0x000400000012DC00), + titleId: 0x000400000012DC00n, titleRegion: 'UNK', name: 'ファイアーエムブレム if 白夜王国', tasks: ['TASK00', 'TASK02'] }, { bossAppId: 'rdWhyQMMUyKavjb4', - titleId: BigInt(0x000400000012DD00), + titleId: 0x000400000012DD00n, titleRegion: 'UNK', name: 'ファイアーエムブレム if 暗夜王国', tasks: ['TASK00', 'TASK02'] }, { bossAppId: 'rFgmDJuaHQO3TQFc', - titleId: BigInt(0x0004000000178800), + titleId: 0x0004000000178800n, titleRegion: 'JPN', name: 'Miitopia', tasks: ['Enquete', 'MiiDL'] }, { bossAppId: 'rLNcsjO2p5oT0mqf', - titleId: BigInt(0x0004000000169500), + titleId: 0x0004000000169500n, titleRegion: 'UNK', name: 'Rodea the Sky Soldier V1.01', tasks: ['FGONLYT'] }, { bossAppId: 'RrNiKxhX1QFjyHSS', - titleId: BigInt(0x0004000000179600), + titleId: 0x0004000000179600n, titleRegion: 'USA', name: 'Fire Emblem Fates Conquest', tasks: ['TASK00', 'TASK02'] }, { bossAppId: 'rsJRb5kSxvgEazo3', - titleId: BigInt(0x0004001000021800), + titleId: 0x0004001000021800n, titleRegion: 'UNK', name: 'Unknown', tasks: ['PANEL', 'MIIDATA', 'ETC', 'PANELLM', 'UPDATE', 'LEGEND'] }, { bossAppId: 'ruVXn8rPH6AV83CV', - titleId: BigInt(0x00040000000C0200), + titleId: 0x00040000000C0200n, titleRegion: 'JPN', name: 'レイトン教授と 超文明Aの遺産', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'RXA1qO1PUOBzCrfe', - titleId: BigInt(0x0004000000167800), + titleId: 0x0004000000167800n, titleRegion: 'EUR', name: 'YO-KAI WATCH', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'S1JafzaIw7C1Iia5', - titleId: BigInt(0x0004000000147200), + titleId: 0x0004000000147200n, titleRegion: 'EUR', name: 'Little Battlers eXperience', tasks: ['FGONLYT'] }, { bossAppId: 's4T2rYW8ByXzSwQH', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 's6jspRUJkQbUMWgK', - titleId: BigInt(0x0004000000159500), + titleId: 0x0004000000159500n, titleRegion: 'USA', name: 'Devil Survivor 2 Record Breaker', tasks: ['Ds2ocTk'] }, { bossAppId: 'S7tpGELe3d0jT75J', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['data', 'news'] }, { bossAppId: 'sbPtwI3pQEFTPEYu', - titleId: BigInt(0x0004000000168800), + titleId: 0x0004000000168800n, titleRegion: 'UNK', name: 'Rodea the Sky Soldier', tasks: ['FGONLYT'] }, { bossAppId: 'SS47bOArh8LhftQC', - titleId: BigInt(0x0004000000078500), + titleId: 0x0004000000078500n, titleRegion: 'JPN', name: 'ダンボール戦機 爆ブースト', tasks: ['FGONLYT'] }, { bossAppId: 'svTk7OJgceQciyt9', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['data', 'news'] }, { bossAppId: 'Sz5wLxuBiuxOV6qO', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['data', 'news'] }, { bossAppId: 'T3WjBdLsN0SbsBpB', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'T6hDbtUyZu9yGbiN', - titleId: BigInt(0x000400000017C200), + titleId: 0x000400000017C200n, titleRegion: 'UNK', name: 'YO-KAI WATCH', tasks: ['FGONLYT', 'news'] }, { bossAppId: 't6hwMmI9dyfj3vFa', - titleId: BigInt(0x00040000000D5800), + titleId: 0x00040000000D5800n, titleRegion: 'EUR', name: 'Nintendo 3DS Guide: Louvre (Versione italiana)', tasks: ['AL8_001'] }, { bossAppId: 't7svvLkl31nkcVv9', - titleId: BigInt(0x00040000001B4E00), + titleId: 0x00040000001B4E00n, titleRegion: 'USA', name: 'Miitopia', tasks: ['Enquete', 'MiiDL'] }, { bossAppId: 'TABYOV7qcJ3wjUlk', - titleId: BigInt(0x000400000004B700), + titleId: 0x000400000004B700n, titleRegion: 'JPN', name: 'G1 Grand Prix Ver.1.1', tasks: ['g1_plus'] }, { bossAppId: 'TegobOkDc6fN2cLL', - titleId: BigInt(0x0004000000120C00), + titleId: 0x0004000000120C00n, titleRegion: 'KOR', name: 'Tomodachi Life', tasks: ['tmTaskA'] }, { bossAppId: 'TkTJ7y1N7b4bvs8m', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'TuKTMEFYh0sLS1O3', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['data', 'news'] }, { bossAppId: 'TVNiiigBTi1WzpnF', - titleId: BigInt(0x00040000001C6800), + titleId: 0x00040000001C6800n, titleRegion: 'KOR', name: 'Fire Emblem Echoes: Shadows of Valentia', tasks: ['TASK00', 'FGONLYT', 'TASK01'] }, { bossAppId: 'U2ZFOVWD6LH9r4JQ', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'uLrjbAsNhsH7Hc1N', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'Umg3IE2TYUrKEV1D', - titleId: BigInt(0x000400000012F800), + titleId: 0x000400000012F800n, titleRegion: 'JPN', name: 'Yokai Watch 2 honke', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'uuI82221UKkqmtbp', - titleId: BigInt(0x0004003000008F02), + titleId: 0x0004003000008F02n, titleRegion: 'UNK', name: 'Unknown', tasks: ['basho4', 'basho5', 'sysmsg1', 'sysmsg2', 'sysmsg3', 'basho1', 'basho2', 'basho3', 'basho0'] }, { bossAppId: 'UvNyA9yqIVj7LUml', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['data', 'news'] }, { bossAppId: 'uwGWP9VmceGVyTXv', - titleId: BigInt(0x00040000000E9A00), + titleId: 0x00040000000E9A00n, titleRegion: 'JPN', name: 'シアトリズム FF カーテンコール', tasks: ['DUOINFO'] }, { bossAppId: 'V04RWTNqtHWfOaEB', - titleId: BigInt(0x00040000000EA900), + titleId: 0x00040000000EA900n, titleRegion: 'USA', name: 'Disney Magical World', tasks: ['FGONLYT', 'BNMCASI', 'BNMCASL'] }, { bossAppId: 'V5zESuakCkVaRtYB', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['data', 'news'] }, { bossAppId: 'vCl8UPQ872Q2wzc0', - titleId: BigInt(0x000400000008C500), + titleId: 0x000400000008C500n, titleRegion: 'JPN', name: 'トモダチコレクション 新生活', tasks: ['tmTaskA'] }, { bossAppId: 'vl1QWl9Lf3FhiH8r', - titleId: BigInt(0x000400000014AD00), + titleId: 0x000400000014AD00n, titleRegion: 'JPN', name: '大逆転裁判 -成歩堂龍ノ介の冒險-', tasks: ['DSAIBAN'] }, { bossAppId: 'w4J4AC8GMlfdkX9c', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['daily'] }, { bossAppId: 'w8pqLFi96ZJLL6Co', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'WaOuJFckDnqKr5sK', - titleId: BigInt(0x00040000001D6800), + titleId: 0x00040000001D6800n, titleRegion: 'EUR', name: 'Yo-kai Watch 3', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'wbyZZlvq1j2QNBXj', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['daily'] }, { bossAppId: 'wfHyw6Hj9b1UeCOd', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'WNcE0vrvCCO74nPV', - titleId: BigInt(0x000400000016DE00), + titleId: 0x000400000016DE00n, titleRegion: 'USA', name: 'SmileBASIC Ver.3.6.0', tasks: ['ptc3nwe'] }, { bossAppId: 'wXBTx8n5TvYbdKqD', - titleId: BigInt(0x000400000012F900), + titleId: 0x000400000012F900n, titleRegion: 'JPN', name: 'Yokai Watch 2 ganso', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'wZgX791EQIHRicOk', - titleId: BigInt(0x00040000001C1900), + titleId: 0x00040000001C1900n, titleRegion: 'JPN', name: 'LAYTON\'S MYSTERY JOURNEY Katrielle and the Millionaires\'...', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'X0sh6ppe6HabEEcI', - titleId: BigInt(0x0004000000177000), + titleId: 0x0004000000177000n, titleRegion: 'EUR', name: 'The Legend of Zelda Tri Force Heroes', tasks: ['Info_00', 'Data', 'Data_00', 'Data_01'] }, { bossAppId: 'X7wyEU7bsKuTwsw0', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'xHdRFJ7H9HIm2Eu5', - titleId: BigInt(0x0004000000030000), + titleId: 0x0004000000030000n, titleRegion: 'JPN', name: 'Kid Icarus: Uprising', tasks: ['SEED'] }, { bossAppId: 'XrC4VtwZsJ3ro8sn', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'Y49VUspi8Bd4dqV9', - titleId: BigInt(0x00040000000CF500), + titleId: 0x00040000000CF500n, titleRegion: 'JPN', name: 'DQM2', tasks: ['FGONLYT', 'WHALE01', 'WHALE02'] }, { bossAppId: 'y67Up0WfC3ljfqic', - titleId: BigInt(0x0004000000030200), + titleId: 0x0004000000030200n, titleRegion: 'EUR', name: 'Kid Icarus: Uprising', tasks: ['SEED'] }, { bossAppId: 'YapN7dMun6U6CVPx', - titleId: BigInt(0x000400100002CD00), + titleId: 0x000400100002CD00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['thmlist', 'thmtop', 'thmnews', 'thmdtls'] }, { bossAppId: 'YbltplPRvEZ9620A', - titleId: BigInt(0x000400000007AF00), + titleId: 0x000400000007AF00n, titleRegion: 'EUR', name: 'New SUPER MARIO BROS. 2', tasks: ['present', 'patch'] }, { bossAppId: 'YCHNmojskL3NNzNp', - titleId: BigInt(0x0004000000095C00), + titleId: 0x0004000000095C00n, titleRegion: 'JPN', name: 'Disney Magic Castle My Happy Life', tasks: ['FGONLYT', 'BNMCASI', 'BNMCASL'] }, { bossAppId: 'yj9NpW9dQjUYhN6i', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'ylHLOTvdmBIaUX4H', - titleId: BigInt(0x0004000000179800), + titleId: 0x0004000000179800n, titleRegion: 'UNK', name: 'Fire Emblem Fates', tasks: ['TASK00', 'TASK02'] }, { bossAppId: 'YSMWAkVsyGiCsw36', - titleId: BigInt(0x00040000000F5100), + titleId: 0x00040000000F5100n, titleRegion: 'JPN', name: '三國志', tasks: ['SGS_APD', 'SGS_INF'] }, { bossAppId: 'ytEcS6DukxLYv42a', - titleId: BigInt(0x0004000000072000), + titleId: 0x0004000000072000n, titleRegion: 'JPN', name: 'ファイアーエムブレム 覚醒', tasks: ['Quartz0', 'Quartz1'] }, { bossAppId: 'YzNlkoJpfJCGhJ5J', - titleId: BigInt(0x0004000000166E00), + titleId: 0x0004000000166E00n, titleRegion: 'JPN', name: 'モンハン日記 ぽかぽかアイルー村DX', tasks: ['AIROUDX'] }, { bossAppId: 'zFRghpEL0RQSrIkZ', - titleId: BigInt(0x0004000000144300), + titleId: 0x0004000000144300n, titleRegion: 'JPN', name: 'ヒーローバンク2', tasks: ['FGONLYT'] }, { bossAppId: 'ZtNvntGBgoUcf3hZ', - titleId: BigInt(0x00040000001CF000), + titleId: 0x00040000001CF000n, titleRegion: 'EUR', name: 'YO-KAI WATCH BLASTERS WHITE DOG SQUAD', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'ZweaaGi5WXHt5SO2', - titleId: BigInt(0x00040000000A0500), + titleId: 0x00040000000A0500n, titleRegion: 'USA', name: 'Fire Emblem Awakening', tasks: ['Quartz0', 'Quartz1'] }, { bossAppId: 'Zy3Cob0dHUBuRjK3', - titleId: BigInt(0x00040000000D5900), + titleId: 0x00040000000D5900n, titleRegion: 'USA', name: 'Nintendo 3DS Guide: Louvre (versión en español)', tasks: ['AL8_001'] }, { bossAppId: 'zY73NQ2CPef4Rfvu', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'ZzIGzz3gyLOPJUbk', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['daily'] }, { bossAppId: 'RbCdW5sw3xphZ3x7', - titleId: BigInt(0x000400000014F100), + titleId: 0x000400000014F100n, titleRegion: 'USA', name: 'Animal Crossing: Happy Home Designer', tasks: ['FGONLYT'] }, { bossAppId: 'kdZ3BEkxoaIt2lLJ', - titleId: BigInt(0x00040000000D4400), + titleId: 0x00040000000D4400n, titleRegion: 'JPN', name: 'Oxford Reading Tree Floppy\'s Phonics vol.3', tasks: ['ORT03'] }, { bossAppId: '0a2LXft7kMJ39q4g', - titleId: BigInt(0x00040000000D4200), + titleId: 0x00040000000D4200n, titleRegion: 'JPN', name: 'Oxford Reading Tree Floppy\'s Phonics vol.2', tasks: ['ORT02'] }, { bossAppId: '6cDyiXh2nyoSmHeE', - titleId: BigInt(0x00040000000D4300), + titleId: 0x00040000000D4300n, titleRegion: 'JPN', name: 'Oxford Reading Tree Floppy\'s Phonics vol.1', tasks: ['ORT01'] }, { bossAppId: 'Tw16Bbw3beUmN6Jd', - titleId: BigInt(0x000400000014F000), + titleId: 0x000400000014F000n, titleRegion: 'JPN', name: 'Animal Crossing: Happy Home Designer', tasks: ['FGONLYT'] }, { bossAppId: '7b6ZRfqtEUTwJ6rj', - titleId: BigInt(0x00040000000B1900), + titleId: 0x00040000000B1900n, titleRegion: 'EUR', name: 'Phonics Fun with Biff, Chip and Kipper vol.3', tasks: ['ORT03'] }, { bossAppId: 'PDhvZpH361L3gmm3', - titleId: BigInt(0x00040000000B1800), + titleId: 0x00040000000B1800n, titleRegion: 'EUR', name: 'Phonics Fun with Biff, Chip and Kipper vol.2', tasks: ['ORT02'] }, { bossAppId: 'AOdUQg8P4dXbIC4b', - titleId: BigInt(0x000400000014F200), + titleId: 0x000400000014F200n, titleRegion: 'EUR', name: 'Animal Crossing: Happy Home Designer', tasks: ['FGONLYT'] }, { bossAppId: 'tqdef27ymLVxfSaD', - titleId: BigInt(0x00040000000B1700), + titleId: 0x00040000000B1700n, titleRegion: 'EUR', name: 'Phonics Fun with Biff, Chip and Kipper vol.1', tasks: ['ORT01'] }, { bossAppId: 'qEFxWy2HQ5AX7qTX', - titleId: BigInt(0x0004000200178801), + titleId: 0x0004000200178801n, titleRegion: 'UNK', name: 'Miitopia 体験版', tasks: ['MiiDL'] }, { bossAppId: 'EnLW1Fe8d0AfR3mE', - titleId: BigInt(0x0004000000055900), + titleId: 0x0004000000055900n, titleRegion: 'EUR', name: 'Rabbids Rumble', tasks: ['NEWRABS'] }, { bossAppId: 'lX5BwDIdDsj6RkZg', - titleId: BigInt(0x000400000004C300), + titleId: 0x000400000004C300n, titleRegion: 'USA', name: 'Imagine® babyz®', tasks: ['RACHEL'] }, { bossAppId: 'gQ3O9gb9VwvI3is6', - titleId: BigInt(0x0004000000182B00), + titleId: 0x0004000000182B00n, titleRegion: 'KOR', name: 'The Legend of Zelda Tri Force Heroes', tasks: ['Data', 'Data_00', 'Data_01', 'Info_00'] }, { bossAppId: 'Yh1oGYQ6MHHRacyR', - titleId: BigInt(0x0004000000182C00), + titleId: 0x0004000000182C00n, titleRegion: 'TWN', name: 'The Legend of Zelda Tri Force Heroes', tasks: ['Data', 'Data_00', 'Data_01', 'Info_00'] }, { bossAppId: 'qn6svwUCotEnBg24', - titleId: BigInt(0x00040000001B1700), + titleId: 0x00040000001B1700n, titleRegion: 'JPN', name: '好きなMiiで見る Miitopia 予告編', tasks: ['MiiDL', 'Enquete'] }, { bossAppId: 'QlZMDe93rUHQ7svF', - titleId: BigInt(0x000400000004E100), + titleId: 0x000400000004E100n, titleRegion: 'UNK', name: 'Imagine™ Babies 3D', tasks: ['RACHEL'] }, { bossAppId: '6WLiMjiIXzGMmZ6n', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['Info', 'Info_00'] }, { bossAppId: 'HpRhWfgZE0SoEiJ6', - titleId: BigInt(0x0004001000020B00), + titleId: 0x0004001000020B00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['NZOffPg'] }, { bossAppId: 'BC10cFTXc0NHTohu', - titleId: BigInt(0x00040000001A2C00), + titleId: 0x00040000001A2C00n, titleRegion: 'JPN', name: 'Swapdoodle', tasks: ['RNG_EC1', 'RNG_MD1', 'RNG_LS1', 'RNG_NT1', 'RNG_NT2', 'RNG_GM1', 'RNG_DFR', 'RNG_U01', 'RNG_U02', 'RNG_U03', 'RNG_U04', 'RNG_U05', 'RNG_U06', 'RNG_U07', 'RNG_UUS'] }, { bossAppId: 'QwyHOPV4LsvQ2I3U', - titleId: BigInt(0x00040000000F4E00), + titleId: 0x00040000000F4E00n, titleRegion: 'JPN', name: 'NEWラブプラス+', tasks: ['PTASK01', 'FGONLYT', 'PTASK02'] }, { bossAppId: '7RW9z5Cb71Fpt1OE', - titleId: BigInt(0x0004001000020900), + titleId: 0x0004001000020900n, titleRegion: 'UNK', name: 'Unknown', tasks: ['BGM1', 'TIGER1', 'BGM2'] }, { bossAppId: 'TLmPhQflK0Yn6BeH', - titleId: BigInt(0x0004000000117200), + titleId: 0x0004000000117200n, titleRegion: 'JPN', name: 'Petit computer 3 Ver.3.6.3', tasks: ['ptc3nws'] }, { bossAppId: 'pfQzJEaJOiPlLy3t', - titleId: BigInt(0x0004001000020800), + titleId: 0x0004001000020800n, titleRegion: 'UNK', name: 'Unknown', tasks: ['MIIDATA', 'ETC', 'PANELLM', 'UPDATE', 'PANEL', 'LEGEND'] }, { bossAppId: '110Rzo2E1vYSfAz6', - titleId: BigInt(0x000400100002CC00), + titleId: 0x000400100002CC00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['thmtop', 'thmnews', 'thmdtls', 'thmlist'] }, { bossAppId: 'gWr4JXxb2mKTG3lq', - titleId: BigInt(0x0004003000008202), + titleId: 0x0004003000008202n, titleRegion: 'UNK', name: 'Unknown', tasks: ['basho1', 'basho2', 'basho3', 'basho4', 'basho5', 'sysmsg1', 'sysmsg2', 'sysmsg3', 'basho0'] }, { bossAppId: 'WyI1CBPmzfm2nR2f', - titleId: BigInt(0x0004000000051600), + titleId: 0x0004000000051600n, titleRegion: 'JPN', name: 'Swapnote', tasks: ['JFR_LS2', 'JFR_NT1', 'JFR_NT2', 'JFR_NT3', 'JFR_AP2', 'JFR_GM2', 'JFR_DNT', 'JFR_DLS', 'JFR_DAP', 'JFR_DGM', 'JFR_DFR', 'JFR_U01', 'JFR_U02', 'JFR_U03', 'JFR_U04', 'JFR_U05', 'JFR_U06', 'JFR_U07', 'JFR_U08', 'JFR_U09', 'JFR_U10'] }, { bossAppId: 'h0VRqB2YEgq39zvO', - titleId: BigInt(0x0004000000055D00), + titleId: 0x0004000000055D00n, titleRegion: 'JPN', name: 'Pokémon X', tasks: ['horogra', 'FGONLYT'] }, { bossAppId: 'H7btVjYWs7p5dGj6', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['BREAK01', 'BREAK02', 'BREAK03', 'FGONLYT'] }, { bossAppId: 'rO34jReRezcPv4HS', - titleId: BigInt(0x0004001000021B00), + titleId: 0x0004001000021B00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['NZOffPg'] }, { bossAppId: 'b3Gq6LF6EqE1bvKy', - titleId: BigInt(0x00040000001B5100), + titleId: 0x00040000001B5100n, titleRegion: 'JPN', name: 'Pokémon Ultra Moon', tasks: ['FGONLYT'] }, { bossAppId: 'OpIF7z4Uzjoww4Jw', - titleId: BigInt(0x00040000000DCD00), + titleId: 0x00040000000DCD00n, titleRegion: 'USA', name: 'Mario Golf: World Tour', tasks: ['spnt_t', 'spnt_x', 'FGONLYT'] }, { bossAppId: 't9PZWHTdBZ57jYL6', - titleId: BigInt(0x00040000000A9000), + titleId: 0x00040000000A9000n, titleRegion: 'EUR', name: 'Nintendo presents: New Style Boutique', tasks: ['girls', 'GMTM', 'info'] }, { bossAppId: 'vD1TyxppgptrZdfK', - titleId: BigInt(0x0004001000027900), + titleId: 0x0004001000027900n, titleRegion: 'UNK', name: 'Unknown', tasks: ['BGM1', 'BGM2', 'TIGER1'] }, { bossAppId: 'vgBivYesOH9RS5I8', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'Slv7vHlUOfqrKMpz', - titleId: BigInt(0x0004000000055E00), + titleId: 0x0004000000055E00n, titleRegion: 'JPN', name: 'Pokémon Y', tasks: ['FGONLYT', 'horogra'] }, { bossAppId: 'EeqptDDf7v2IL7OP', - titleId: BigInt(0x00040000001AB800), + titleId: 0x00040000001AB800n, titleRegion: 'USA', name: 'Team Kirby Clash Deluxe', tasks: ['FGONLYT'] }, { bossAppId: 'xNwjHvSQy3aGBb4C', - titleId: BigInt(0x0004000000137F00), + titleId: 0x0004000000137F00n, titleRegion: 'UNK', name: 'New SUPER MARIO BROS. 2: Special Edition', tasks: ['patch', 'present'] }, { bossAppId: 'AH3oZwrEbne6qHCO', - titleId: BigInt(0x0004001000022900), + titleId: 0x0004001000022900n, titleRegion: 'UNK', name: 'Unknown', tasks: ['BGM1', 'BGM2', 'TIGER1'] }, { bossAppId: 'tjca9oAeXj1R9EfU', - titleId: BigInt(0x0004001000022800), + titleId: 0x0004001000022800n, titleRegion: 'UNK', name: 'Unknown', tasks: ['MIIDATA', 'ETC', 'PANEL', 'PANELLM', 'UPDATE', 'LEGEND'] }, { bossAppId: 'ZBq1ITue8b9aw64j', - titleId: BigInt(0x00040000000EE000), + titleId: 0x00040000000EE000n, titleRegion: 'EUR', name: 'Super Smash Bros. for Nintendo 3DS', tasks: ['NEWS', 'amiibo', 'FGONLYT'] }, { bossAppId: 'dMtiFHzm5OOf0y2O', - titleId: BigInt(0x000400100002CE00), + titleId: 0x000400100002CE00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['thmtop', 'thmnews', 'thmlist', 'thmdtls'] }, { bossAppId: 'UrXSeurnxhPrq7AS', - titleId: BigInt(0x0004003000009802), + titleId: 0x0004003000009802n, titleRegion: 'UNK', name: 'Unknown', tasks: ['sysmsg1', 'sysmsg2', 'sysmsg3', 'basho1', 'basho2', 'basho3', 'basho4', 'basho5', 'basho0'] }, { bossAppId: 'Y5G9cKWHtFCre5ni', - titleId: BigInt(0x0004000000086400), + titleId: 0x0004000000086400n, titleRegion: 'UNK', name: 'Animal Crossing New Leaf', tasks: ['dlvexb', 'news', 'news_p', 'FGONLYT', 'pnews', 'dream', 'dream_p'] }, { bossAppId: 'oC02RURp92o3o6XQ', - titleId: BigInt(0x00040000001B8D00), + titleId: 0x00040000001B8D00n, titleRegion: 'EUR', name: 'Miitopia: Casting Call', tasks: ['MiiDL', 'Enquete'] }, { bossAppId: '8zLdgUwAeyD4Bn3b', - titleId: BigInt(0x0004000000102F00), + titleId: 0x0004000000102F00n, titleRegion: 'JPN', name: '太鼓の達人 どんとかつの時空大冒険', tasks: ['DlcInfo', 'FGONLYT'] }, { bossAppId: 'nn2h3ad9wxrX42UF', - titleId: BigInt(0x0004000000141000), + titleId: 0x0004000000141000n, titleRegion: 'JPN', name: 'Pokémon Shuffle', tasks: ['pktrpsh'] }, { bossAppId: 'wfGi1AUhRfVvZ2KR', - titleId: BigInt(0x0004001000022B00), + titleId: 0x0004001000022B00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['NZOffPg'] }, { bossAppId: '6HThIi5QlwGZNYFs', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: '8QjtffIMWFhiFpTz', - titleId: BigInt(0x0004000000164800), + titleId: 0x0004000000164800n, titleRegion: 'JPN', name: 'Pokémon Sun', tasks: ['FGONLYT'] }, { bossAppId: 'gfRN888w01lMrpSr', - titleId: BigInt(0x0004000000190E00), + titleId: 0x0004000000190E00n, titleRegion: 'JPN', name: '太鼓の達人 ドコドン! ミステリーアドベンチャー', tasks: ['DlcInfo'] }, { bossAppId: 'cRFY0WFHNjPh44If', - titleId: BigInt(0x000400000011C400), + titleId: 0x000400000011C400n, titleRegion: 'JPN', name: 'Pokémon Omega Ruby', tasks: ['horogra', 'FGONLYT'] }, { bossAppId: 'wkiOHAEndV3fVHOF', - titleId: BigInt(0x00040000000DCE00), + titleId: 0x00040000000DCE00n, titleRegion: 'EUR', name: 'Mario Golf: World Tour', tasks: ['spnt_t', 'FGONLYT', 'spnt_x'] }, { bossAppId: 'nTik1y6PI2QheWpi', - titleId: BigInt(0x00040000000D4B00), + titleId: 0x00040000000D4B00n, titleRegion: 'EUR', name: 'Disney Magical World', tasks: ['FGONLYT', 'BNMCASI', 'BNMCASL'] }, { bossAppId: 'uAiur5PJTg0lFji6', - titleId: BigInt(0x00040000001C2600), + titleId: 0x00040000001C2600n, titleRegion: 'EUR', name: 'Nintendo Presents New Style Boutique 3', tasks: ['NEWS', 'POSTER'] }, { bossAppId: 'hatYUcYm85RPNRYv', - titleId: BigInt(0x0004000000032D00), + titleId: 0x0004000000032D00n, titleRegion: 'USA', name: 'SUPER STREET FIGHTER Ⅳ 3D EDITION', tasks: ['SPA4APP'] }, { bossAppId: 'nT8epEHh2yCVHTZk', - titleId: BigInt(0x0004000000198E00), + titleId: 0x0004000000198E00n, titleRegion: 'USA', name: 'Animal Crossing: New Leaf - Welcome amiibo', tasks: ['news', 'news_p', 'dlvexb', 'FGONLYT', 'dream', 'dream_p'] }, { bossAppId: 'P2mPjVWZUv2Dw8tw', - titleId: BigInt(0x000400000017EA00), + titleId: 0x000400000017EA00n, titleRegion: 'USA', name: 'HYRULE WARRIORS LEGENDS', tasks: ['zdltdat', 'FGONLYT'] }, { bossAppId: 'KJshB9eMGJCdvEBK', - titleId: BigInt(0x00040000000BA800), + titleId: 0x00040000000BA800n, titleRegion: 'USA', name: 'Pokémon Mystery Dungeon Gates to Infinity', tasks: ['PAINF00'] }, { bossAppId: 'tjZ5w8RGlAKd82y0', - titleId: BigInt(0x00040000001B2700), + titleId: 0x00040000001B2700n, titleRegion: 'USA', name: 'YO-KAI WATCH 2: PSYCHIC SPECTERS', tasks: ['news', 'FGONLYT'] }, { bossAppId: 'GHsikcsO3zjZO8bm', - titleId: BigInt(0x00040000000A9100), + titleId: 0x00040000000A9100n, titleRegion: 'USA', name: 'Style Savvy: Trendsetters', tasks: ['GMTM', 'info', 'girls'] }, { bossAppId: 'kIHxKNVp3BHrr9bb', - titleId: BigInt(0x0004000000163200), + titleId: 0x0004000000163200n, titleRegion: 'EUR', name: 'Fullblox', tasks: ['annouce', 'FGONLYT'] }, { bossAppId: 'qOBtWp4rFtI6PoPl', - titleId: BigInt(0x00040000000B4F00), + titleId: 0x00040000000B4F00n, titleRegion: 'EUR', name: 'Fallblox', tasks: ['JAU'] }, { bossAppId: '3xU3A5ONnnC5ZxG9', - titleId: BigInt(0x0004000000031600), + titleId: 0x0004000000031600n, titleRegion: 'EUR', name: 'nintendogs + cats', tasks: ['task_EU'] }, { bossAppId: 'n7KLcJdvAfV8jrr5', - titleId: BigInt(0x00040002001B4F01), + titleId: 0x00040002001B4F01n, titleRegion: 'UNK', name: 'Miitopia: Demo Version', tasks: ['MiiDL', 'Enquete'] }, { bossAppId: 'ZMcgy6xtWsbsQE6P', - titleId: BigInt(0x0004000000198F00), + titleId: 0x0004000000198F00n, titleRegion: 'EUR', name: 'Animal Crossing: New Leaf - Welcome amiibo', tasks: ['news_p', 'dlvexb', 'news', 'FGONLYT', 'dream', 'dream_p'] }, { bossAppId: 'x7rRVYrCfwjmpOOX', - titleId: BigInt(0x0004000000163100), + titleId: 0x0004000000163100n, titleRegion: 'USA', name: 'Stretchmo', tasks: ['annouce', 'FGONLYT'] }, { bossAppId: 'svHxRGPIJ2AWA5QJ', - titleId: BigInt(0x00040002001B4E01), + titleId: 0x00040002001B4E01n, titleRegion: 'UNK', name: 'Miitopia Demo', tasks: ['MiiDL', 'Enquete'] }, { bossAppId: 'vlru9ZBRLJNiPwcm', - titleId: BigInt(0x000400000004AB00), + titleId: 0x000400000004AB00n, titleRegion: 'UNK', name: 'Nintendo Video', tasks: ['ESP_NWS', 'ESP_CNF'] }, { bossAppId: '6a2i4ewtEkJhoUS0', - titleId: BigInt(0x00040000000D9A00), + titleId: 0x00040000000D9A00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SN7P'] }, { bossAppId: '9vMstzmE8vG7XDOo', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['EWP_NWS', 'EWP_CNF'] }, { bossAppId: 'VCcHstgngvFjng8c', - titleId: BigInt(0x0004000000032600), + titleId: 0x0004000000032600n, titleRegion: 'UNK', name: 'Pokédex 3D', tasks: ['basrao0', 'basrao1'] }, { bossAppId: 'gekBa3SxolnJEyXJ', - titleId: BigInt(0x000400000016A100), + titleId: 0x000400000016A100n, titleRegion: 'EUR', name: 'Nintendo presents New Style Boutique 2', tasks: ['NEWS', 'POSTER', 'FGONLYT'] }, { bossAppId: 'AOG9w8nspw1vnerP', - titleId: BigInt(0x0004000000084F00), + titleId: 0x0004000000084F00n, titleRegion: 'EUR', name: 'New Art Academy', tasks: ['london1'] }, { bossAppId: 'AZdLFt0b2qPaChrb', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['ptc3nws'] }, { bossAppId: 'EPqObwlC8lOg12az', - titleId: BigInt(0x000400000013F800), + titleId: 0x000400000013F800n, titleRegion: 'UNK', name: 'Rayman and Rabbids - Family Pack', tasks: ['NEWRABS'] }, { bossAppId: 'ytpowavlB10VzRDI', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['GS5'] }, { bossAppId: 'bX6vTcQgC6ojhnM8', - titleId: BigInt(0x0004000000125D00), + titleId: 0x0004000000125D00n, titleRegion: 'JPN', name: 'Denpa Ningen RPG FREE!', tasks: ['labos01', 'labos02', 'FGONLYT'] }, { bossAppId: 'sKBxpm1uEGbaKj3Z', - titleId: BigInt(0x0004000000030C00), + titleId: 0x0004000000030C00n, titleRegion: 'EUR', name: 'nintendogs + cats', tasks: ['task_EU'] }, { bossAppId: 'd2AUo8Ku903Uz7Oy', - titleId: BigInt(0x000400000018FA00), + titleId: 0x000400000018FA00n, titleRegion: 'EUR', name: 'Phoenix Wright: Ace Attorney Spirit of Justice', tasks: ['GS6'] }, { bossAppId: 'h06QNObaOsEeWbLS', - titleId: BigInt(0x0004000000034D00), + titleId: 0x0004000000034D00n, titleRegion: 'USA', name: 'SAMURAI WARRIORS: Chronicles', tasks: ['JMCDLC0', 'JMCDLC1', 'JMCDLC2'] }, { bossAppId: 'au22VYFVclQ3t3fK', - titleId: BigInt(0x0004000000082000), + titleId: 0x0004000000082000n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JQBE'] }, { bossAppId: 'nw66VXNswH2IYbJk', - titleId: BigInt(0x0004000000082200), + titleId: 0x0004000000082200n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ5Z'] }, { bossAppId: 'ClDVV1OhC2nPQfiz', - titleId: BigInt(0x0004000000081F00), + titleId: 0x0004000000081F00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JQAE'] }, { bossAppId: 'eD9gjUWfENOf7RX4', - titleId: BigInt(0x0004000000033C00), + titleId: 0x0004000000033C00n, titleRegion: 'EUR', name: 'SUPER STREET FIGHTER Ⅳ 3D EDITION', tasks: ['SPA4APP'] }, { bossAppId: 'Ne7L2H9zREHMz0QT', - titleId: BigInt(0x000400000017A800), + titleId: 0x000400000017A800n, titleRegion: 'UNK', name: 'Fire Emblem Fates', tasks: ['TASK00', 'TASK02'] }, { bossAppId: 'asLNEQeEuHnm71Qo', - titleId: BigInt(0x0004000000079000), + titleId: 0x0004000000079000n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ5P'] }, { bossAppId: 'KafSZFWApstWG1oT', - titleId: BigInt(0x0004000000038A00), + titleId: 0x0004000000038A00n, titleRegion: 'EUR', name: 'DEAD OR ALIVE Dimensions', tasks: ['costume', 'foeevnt', 'info'] }, { bossAppId: 's6jH81JriWS8EFL4', - titleId: BigInt(0x0004000000079300), + titleId: 0x0004000000079300n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJYP'] }, { bossAppId: '7FgFpXT7yPCI7d5K', - titleId: BigInt(0x0004000000079100), + titleId: 0x0004000000079100n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJZP'] }, { bossAppId: 'H0aS5wTwrP8xYZbj', - titleId: BigInt(0x0004000000079400), + titleId: 0x0004000000079400n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJVP'] }, { bossAppId: '2jvsEXOhaXMEJRqU', - titleId: BigInt(0x0004000000079200), + titleId: 0x0004000000079200n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJXP'] }, { bossAppId: '9eqERrZPrfwHsXw8', - titleId: BigInt(0x0004000000078E00), + titleId: 0x0004000000078E00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ6P'] }, { bossAppId: 'pvKf3eKlZM86SftD', - titleId: BigInt(0x0004000000078F00), + titleId: 0x0004000000078F00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ7P'] }, { bossAppId: 'Q9zLfpAkDHiIAQtD', - titleId: BigInt(0x0004000000080F00), + titleId: 0x0004000000080F00n, titleRegion: 'UNK', name: 'Mario & Sonic - London 2012 Virtual Card Album', tasks: ['Z_CARD0'] }, { bossAppId: 'KmJKbxqqJ1ISUDgN', - titleId: BigInt(0x0004000000155C00), + titleId: 0x0004000000155C00n, titleRegion: 'EUR', name: 'Gardening mama Forest Friends', tasks: ['add-on'] }, { bossAppId: 'rNRksUC46LBJUQps', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['weekly'] }, { bossAppId: 'HxHhUeO7QSr7fTf0', - titleId: BigInt(0x0004000000137E00), + titleId: 0x0004000000137E00n, titleRegion: 'UNK', name: 'New Super Mario Bros. 2: Gold Edition', tasks: ['present', 'patch'] }, { bossAppId: 'p2uEU1eJqc8tFvqS', - titleId: BigInt(0x0004000000034F00), + titleId: 0x0004000000034F00n, titleRegion: 'USA', name: 'DEAD OR ALIVE Dimensions', tasks: ['costume', 'foeevnt', 'info'] }, { bossAppId: 'MjQnB45RHoQhqIf4', - titleId: BigInt(0x00040000001C2500), + titleId: 0x00040000001C2500n, titleRegion: 'USA', name: 'Style Savvy: Styling Star', tasks: ['NEWS', 'POSTER'] }, { bossAppId: '0Fkb5zYwkA04nKYK', - titleId: BigInt(0x00040000001B8C00), + titleId: 0x00040000001B8C00n, titleRegion: 'USA', name: 'Miitopia: Casting Call', tasks: ['MiiDL', 'Enquete'] }, { bossAppId: 'CkG3Q4aOnF2ALmis', - titleId: BigInt(0x000400000017EB00), + titleId: 0x000400000017EB00n, titleRegion: 'EUR', name: 'HYRULE WARRIORS LEGENDS', tasks: ['zdltcm', 'zdltdat', 'FGONLYT'] }, { bossAppId: 'Fk5Tz3LAVAZ2SvqP', - titleId: BigInt(0x0004000000031200), + titleId: 0x0004000000031200n, titleRegion: 'USA', name: 'nintendogs + cats', tasks: ['task_US'] }, { bossAppId: 'UKUcmS9e4XbCsq3x', - titleId: BigInt(0x00040000000B8B00), + titleId: 0x00040000000B8B00n, titleRegion: 'JPN', name: 'Super Smash Bros. for Nintendo 3DS', tasks: ['NEWS', 'amiibo', 'FGONLYT'] }, { bossAppId: 'QmLaxX884X14gr28', - titleId: BigInt(0x0004000000030D00), + titleId: 0x0004000000030D00n, titleRegion: 'USA', name: 'nintendogs + cats', tasks: ['task_US'] }, { bossAppId: 'eVnDsHy7ix4xPKLs', - titleId: BigInt(0x000400000019A900), + titleId: 0x000400000019A900n, titleRegion: 'USA', name: 'Yo-kai Watch 2 Bony Spirits', tasks: ['news', 'FGONLYT'] }, { bossAppId: 'fnCAH3KrGIl9dgSd', - titleId: BigInt(0x00040000001B5000), + titleId: 0x00040000001B5000n, titleRegion: 'JPN', name: 'Pokémon Ultra Sun', tasks: ['FGONLYT'] }, { bossAppId: 'Co6AOQENlqDZDWNw', - titleId: BigInt(0x0004000000106200), + titleId: 0x0004000000106200n, titleRegion: 'EUR', name: 'Nintendo Pocket Football Club', tasks: ['hobbit2', 'hobbit1'] }, { bossAppId: 'dJ4hv6uMXYNhbYGJ', - titleId: BigInt(0x000400000018AF00), + titleId: 0x000400000018AF00n, titleRegion: 'UNK', name: 'Disney Magical World 2', tasks: ['MC2NWS', 'FGONLYT'] }, { bossAppId: 'cd1qe3EXYyRuEdld', - titleId: BigInt(0x0004000000109800), + titleId: 0x0004000000109800n, titleRegion: 'JPN', name: 'niconico', tasks: ['news'] }, { bossAppId: 'dpi2G2X44RWYcFKi', - titleId: BigInt(0x0004000000031100), + titleId: 0x0004000000031100n, titleRegion: 'EUR', name: 'nintendogs + cats', tasks: ['task_EU'] }, { bossAppId: 'AQzcgnW7ozCHjk0e', - titleId: BigInt(0x000400000019AA00), + titleId: 0x000400000019AA00n, titleRegion: 'USA', name: 'Yo-kai Watch 2 Fleshy Souls', tasks: ['news', 'FGONLYT'] }, { bossAppId: 'wKrAF9Z9DXZdXZQx', - titleId: BigInt(0x00040000001B9C00), + titleId: 0x00040000001B9C00n, titleRegion: 'USA', name: 'Cooking Mama: Sweet Shop', tasks: ['weekly'] }, { bossAppId: 'ZzWhdXdx7bv5p8bP', - titleId: BigInt(0x0004000000095800), + titleId: 0x0004000000095800n, titleRegion: 'USA', name: 'Art Academy Lessons for Everyone!', tasks: ['london1'] }, { bossAppId: 'ME0gcVqjUOI7Xdz4', - titleId: BigInt(0x0004000000075100), + titleId: 0x0004000000075100n, titleRegion: 'USA', name: 'Heroes of Ruin', tasks: ['IMAHERO'] }, { bossAppId: 'dAWysFTQbZ8yEBBd', - titleId: BigInt(0x000400000016F200), + titleId: 0x000400000016F200n, titleRegion: 'USA', name: 'SAMURAI WARRIORS: Chronicles 3', tasks: ['SC3DLC0', 'FGONLYT'] }, { bossAppId: 'Bj9GROrjies5XMOa', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['GS5'] }, { bossAppId: '65YaZF7skxsw5cdY', - titleId: BigInt(0x000400000018F400), + titleId: 0x000400000018F400n, titleRegion: 'USA', name: 'Phoenix Wright: Ace Attorney Spirit of Justice', tasks: ['GS6'] }, { bossAppId: 'InwBEl2EEhnbr5EC', - titleId: BigInt(0x0004000000031700), + titleId: 0x0004000000031700n, titleRegion: 'USA', name: 'nintendogs + cats', tasks: ['task_US'] }, { bossAppId: 'gfRXFtm46orJeAoe', - titleId: BigInt(0x0004000000086200), + titleId: 0x0004000000086200n, titleRegion: 'UNK', name: 'Animal Crossing New Leaf', tasks: ['news', 'news_p', 'pnews', 'FGONLYT', 'dream', 'dream_p'] }, { bossAppId: 'h9yS1FDz26uNM9lj', - titleId: BigInt(0x00040000000B4A00), + titleId: 0x00040000000B4A00n, titleRegion: 'JPN', name: 'Tongariboshi To Maho no Machi', tasks: ['tg4', 'tg4home', 'FGONLYT'] }, { bossAppId: 'hLU6fGE4vzOsrlmw', - titleId: BigInt(0x0004000000031000), + titleId: 0x0004000000031000n, titleRegion: 'JPN', name: 'nintendogs + cats', tasks: ['task_JP'] }, { bossAppId: '5H2IWrmjM8PiN5SV', - titleId: BigInt(0x00040000000B4700), + titleId: 0x00040000000B4700n, titleRegion: 'UNK', name: 'どこでも本屋さん', tasks: ['diibo00', 'diibo02', 'diibo01', 'FGONLYT'] }, { bossAppId: 'E5GjXFKIix3yGqgK', - titleId: BigInt(0x0004000000099700), + titleId: 0x0004000000099700n, titleRegion: 'JPN', name: 'RECOCHOKU', tasks: ['RECOBNT', 'FGONLYT'] }, { bossAppId: 'VEPSPo1c55w9ydWV', - titleId: BigInt(0x0004000000157B00), + titleId: 0x0004000000157B00n, titleRegion: 'JPN', name: '引ク出ス ヒッパランド', tasks: ['annouce', 'FGONLYT'] }, { bossAppId: 'sA2vpi47OPnR78Ge', - titleId: BigInt(0x0004000000180A00), + titleId: 0x0004000000180A00n, titleRegion: 'JPN', name: 'ChoChariso ver.1.2', tasks: ['taskImg', 'taskID', 'taskFlg'] }, { bossAppId: 'PAuBgnpL4UGBpboR', - titleId: BigInt(0x0004000000141D00), + titleId: 0x0004000000141D00n, titleRegion: 'JPN', name: '大合奏!バンドブラザーズP デビュー', tasks: ['allhp'] }, { bossAppId: '76cct9EjYJJ17weU', - titleId: BigInt(0x00040002000C9E01), + titleId: 0x00040002000C9E01n, titleRegion: 'UNK', name: '(体験版)実写でちびロボ!', tasks: ['mesdat', 'dat'] }, { bossAppId: 'AcYFJX9F9mNBA34T', - titleId: BigInt(0x000400000005C800), + titleId: 0x000400000005C800n, titleRegion: 'USA', name: 'Crosswords Plus', tasks: ['CW_WEEK', 'CW_RGN'] }, { bossAppId: 'ZppKDH9dFwci1xuM', - titleId: BigInt(0x00040000000F1500), + titleId: 0x00040000000F1500n, titleRegion: 'USA', name: 'JUMP TRIALS SUPREME', tasks: ['dqu5vs9', 'x7a9u1y'] }, { bossAppId: '7mXz0DXR4b4CdD8r', - titleId: BigInt(0x0004000000175E00), + titleId: 0x0004000000175E00n, titleRegion: 'JPN', name: 'Pokémon Moon', tasks: ['FGONLYT'] }, { bossAppId: '5XpCMVOjCHJOLeh6', - titleId: BigInt(0x0004000000079700), + titleId: 0x0004000000079700n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ6E'] }, { bossAppId: 'Kgg1X843lmd8Qx4H', - titleId: BigInt(0x0004000000074000), + titleId: 0x0004000000074000n, titleRegion: 'EUR', name: 'Heroes of Ruin', tasks: ['IMAHERO'] }, { bossAppId: 'nmU69oGizolOYGSc', - titleId: BigInt(0x00040000000FCA00), + titleId: 0x00040000000FCA00n, titleRegion: 'EUR', name: 'THEATRHYTHM FINAL FANTASY CURTAIN CALL', tasks: ['DUOINFO'] }, { bossAppId: 'OXEubGVtO2scl6qt', - titleId: BigInt(0x0004000000132500), + titleId: 0x0004000000132500n, titleRegion: 'JPN', name: 'Code Name: S.T.E.A.M.', tasks: ['NEWS'] }, { bossAppId: 'wujsRH3kkwPXc5p9', - titleId: BigInt(0x0004000000179500), + titleId: 0x0004000000179500n, titleRegion: 'UNK', name: 'Fire Emblem Fates Birthright', tasks: ['TASK00', 'TASK02'] }, { bossAppId: 'xxVJpVHJVIX1unyz', - titleId: BigInt(0x000400000018B000), + titleId: 0x000400000018B000n, titleRegion: 'JPN', name: 'YO-KAI SANGOKUSHI', tasks: ['yk_news', 'FGONLYT'] }, { bossAppId: '9bUGzV1nUl8wxkPY', - titleId: BigInt(0x0004000000198D00), + titleId: 0x0004000000198D00n, titleRegion: 'JPN', name: 'Animal Crossing: New Leaf - Welcome amiibo', tasks: ['news', 'news_p', 'FGONLYT', 'dream', 'dream_p'] }, { bossAppId: 'MbNeoMBork0x5U9D', - titleId: BigInt(0x0004000000137D00), + titleId: 0x0004000000137D00n, titleRegion: 'UNK', name: 'New SUPER MARIO BROS. 2: Gold Edition', tasks: ['present', 'patch'] }, { bossAppId: 'wESLUfmWpm322iLC', - titleId: BigInt(0x00040000000A5300), + titleId: 0x00040000000A5300n, titleRegion: 'JPN', name: 'Mario Golf: World Tour', tasks: ['spnt_t', 'spnt_x', 'FGONLYT'] }, { bossAppId: 'TISVjpYatgrPk9By', - titleId: BigInt(0x00040000001B9500), + titleId: 0x00040000001B9500n, titleRegion: 'EUR', name: 'Cooking Mama: Sweet Shop', tasks: ['weekly'] }, { bossAppId: 'JK6188WXQQi46VGk', - titleId: BigInt(0x0004000000192F00), + titleId: 0x0004000000192F00n, titleRegion: 'EUR', name: 'JUMP TRIALS SUPREME', tasks: ['dqu5vs9', 'x7a9u1y'] }, { bossAppId: 'r8bumkFVkPoNhc9c', - titleId: BigInt(0x00040000000E6700), + titleId: 0x00040000000E6700n, titleRegion: 'USA', name: 'Skater Cat', tasks: ['boss1', 'FGONLYT'] }, { bossAppId: 'INTHvj3cbPjeXOIp', - titleId: BigInt(0x00040000001B7400), + titleId: 0x00040000001B7400n, titleRegion: 'EUR', name: 'Pic-a-Pix Colour', tasks: ['NEWPACK'] }, { bossAppId: 'yYddhP0UtHK9WfXf', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['item'] }, { bossAppId: 'oRUi0TW540yJfcFI', - titleId: BigInt(0x0004000000165400), + titleId: 0x0004000000165400n, titleRegion: 'UNK', name: 'Karaoke JOYSOUND', tasks: ['KRKJ001'] }, { bossAppId: 'YTCRfaHNkqkyAoR0', - titleId: BigInt(0x0004000000036F00), + titleId: 0x0004000000036F00n, titleRegion: 'JPN', name: '花といきもの立体図鑑(Ver. 1.1)', tasks: ['sflower', 'sflocal'] }, { bossAppId: 'eOywu80lc2iSQrGQ', - titleId: BigInt(0x0004000000079800), + titleId: 0x0004000000079800n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ7E'] }, { bossAppId: 'Bjlq1Q32KdkSazF0', - titleId: BigInt(0x0004000000179700), + titleId: 0x0004000000179700n, titleRegion: 'UNK', name: 'Fire Emblem Fates Conquest', tasks: ['TASK00', 'TASK02'] }, { bossAppId: 'fY56qJDdLSRY1zwh', - titleId: BigInt(0x00040000000F7D00), + titleId: 0x00040000000F7D00n, titleRegion: 'EUR', name: 'Inazuma Eleven 3 Lightning Bolt', tasks: ['news', 'FGONLYT'] }, { bossAppId: 'wkGEp5JKVH1sHK4j', - titleId: BigInt(0x0004000000055400), + titleId: 0x0004000000055400n, titleRegion: 'USA', name: 'Rabbids Rumble', tasks: ['NEWRABS'] }, { bossAppId: 'zXfHuj8k9mgzKrpM', - titleId: BigInt(0x0004000000196500), + titleId: 0x0004000000196500n, titleRegion: 'USA', name: 'Nintendo presents Style Savvy: Fashion Forward', tasks: ['NEWS', 'POSTER', 'FGONLYT'] }, { bossAppId: 'gaM5Hqp8XiLyp3pU', - titleId: BigInt(0x000400000008CD00), + titleId: 0x000400000008CD00n, titleRegion: 'USA', name: 'Sparkle Snapshots 3D', tasks: ['NadlTsk'] }, { bossAppId: 'LJ569qrUUTcV5MWa', - titleId: BigInt(0x0004000000079A00), + titleId: 0x0004000000079A00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJZE'] }, { bossAppId: 'LvMjVLZITq73RRfN', - titleId: BigInt(0x0004000000079B00), + titleId: 0x0004000000079B00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJXE'] }, { bossAppId: '8Urw573qCWPTMHt1', - titleId: BigInt(0x0004000000079D00), + titleId: 0x0004000000079D00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJVE'] }, { bossAppId: '7oIHiYcoNKUFPXvo', - titleId: BigInt(0x0004000000079C00), + titleId: 0x0004000000079C00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJYE'] }, { bossAppId: 'tGgHjsqB1lxz7laV', - titleId: BigInt(0x000400000018AE00), + titleId: 0x000400000018AE00n, titleRegion: 'UNK', name: 'Disney Magical World 2', tasks: ['MC2NWS', 'FGONLYT'] }, { bossAppId: 'zd1wgUFX5L74rQjK', - titleId: BigInt(0x00040000000F1F00), + titleId: 0x00040000000F1F00n, titleRegion: 'UNK', name: 'Pokemon tretta LAB MAIN SYSTEM', tasks: ['Notice', 'FGONLYT'] }, { bossAppId: 'qJastY9KRjKlmpx7', - titleId: BigInt(0x000400000009AC00), + titleId: 0x000400000009AC00n, titleRegion: 'UNK', name: 'Déco Photo 3D', tasks: ['NadlTsk'] }, { bossAppId: 'AUonPa828Z3PtkeG', - titleId: BigInt(0x00040000000ECB00), + titleId: 0x00040000000ECB00n, titleRegion: 'EUR', name: 'Skater Cat', tasks: ['boss1'] }, { bossAppId: '00ydKyJUl4PPMEM8', - titleId: BigInt(0x000400000016E600), + titleId: 0x000400000016E600n, titleRegion: 'EUR', name: 'Devil Survivor 2: Record Breaker', tasks: ['Ds2ocTk'] }, { bossAppId: 'Sv1HvEbP3JAcxqvf', - titleId: BigInt(0x00040000000FC800), + titleId: 0x00040000000FC800n, titleRegion: 'UNK', name: 'honto for ニンテンドー3DS', tasks: ['honto00'] }, { bossAppId: '40Ox7iM6hyihJOiz', - titleId: BigInt(0x000400000016B200), + titleId: 0x000400000016B200n, titleRegion: 'JPN', name: 'ファイアーエムブレム if', tasks: ['TASK00', 'TASK02'] }, { bossAppId: 'yMkh08f1lW0DafIi', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JQ2J'] }, { bossAppId: 'Nctmz7ODGZtPf69M', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['TASK00', 'TASK02'] }, { bossAppId: 'o8ZLmDtyL1j7SXyQ', - titleId: BigInt(0x0004000000144400), + titleId: 0x0004000000144400n, titleRegion: 'JPN', name: 'ワンピース 超グランドバトル!X', tasks: ['compe'] }, { bossAppId: 'snhV1gVHgJLnjhid', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['BREAK01', 'BREAK03', 'BREAK02', 'FGONLYT'] }, { bossAppId: 'x7D5Xg4ezlaVAJyE', - titleId: BigInt(0x00040000000ABD00), + titleId: 0x00040000000ABD00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNJJ'] }, { bossAppId: 'jPDSZKCbiBNMsEq9', - titleId: BigInt(0x0004000000182F00), + titleId: 0x0004000000182F00n, titleRegion: 'JPN', name: 'PUZZLE & DRAGONS CROSS GOD type', tasks: ['INFITEM', 'INFEVT', 'INFPUB', 'FGONLYT'] }, { bossAppId: 'im5wnzidQQs5uoKe', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['mesdat', 'dat'] }, { bossAppId: 'eFz4GxIjoCccrVSV', - titleId: BigInt(0x0004000000031500), + titleId: 0x0004000000031500n, titleRegion: 'JPN', name: 'nintendogs + cats', tasks: ['task_JP'] }, { bossAppId: 'KWrfnjzo2LKhZy4s', - titleId: BigInt(0x00040000000C3A00), + titleId: 0x00040000000C3A00n, titleRegion: 'JPN', name: 'デビルサバイバー2 ブレイクレコード', tasks: ['Ds2ocTk'] }, { bossAppId: '8qwKR8OSh5GOapqe', - titleId: BigInt(0x000400300000B102), + titleId: 0x000400300000B102n, titleRegion: 'UNK', name: 'Unknown', tasks: ['basho0', 'basho1', 'basho2', 'basho3', 'basho4', 'basho5', 'sysmsg1', 'sysmsg2', 'sysmsg3'] }, { bossAppId: 'j7tO10mcyMaz5ig6', - titleId: BigInt(0x000400300000A102), + titleId: 0x000400300000A102n, titleRegion: 'UNK', name: 'Unknown', tasks: ['basho5', 'basho4', 'basho0', 'basho1', 'basho2', 'basho3', 'sysmsg1', 'sysmsg2', 'sysmsg3'] }, { bossAppId: 'kRlrRG3XMEZShz9a', - titleId: BigInt(0x0004001000028900), + titleId: 0x0004001000028900n, titleRegion: 'UNK', name: 'Unknown', tasks: ['BGM1', 'BGM2', 'TIGER1'] }, { bossAppId: 'VPFqk5XKRlkeei6V', - titleId: BigInt(0x000400300000A902), + titleId: 0x000400300000A902n, titleRegion: 'UNK', name: 'Unknown', tasks: ['basho0', 'basho1', 'basho2', 'basho3', 'basho4', 'basho5', 'sysmsg1', 'sysmsg2', 'sysmsg3'] }, { bossAppId: 'pgNuDMNihGLmz8EQ', - titleId: BigInt(0x00040000000AAD00), + titleId: 0x00040000000AAD00n, titleRegion: 'JPN', name: 'Crashmo', tasks: ['JAU'] }, { bossAppId: 'HiSDUEw0agNVyQQh', - titleId: BigInt(0x0004000000090700), + titleId: 0x0004000000090700n, titleRegion: 'TWN', name: 'nintendogs + cats', tasks: ['task_CH', 'task_TW'] }, { bossAppId: 'dnfsqL9yQI945m6N', - titleId: BigInt(0x0004000000160A00), + titleId: 0x0004000000160A00n, titleRegion: 'JPN', name: 'Greeting cards', tasks: ['KGCTask'] }, { bossAppId: '0AzfphwTbGD6PV9y', - titleId: BigInt(0x00040000000DC900), + titleId: 0x00040000000DC900n, titleRegion: 'JPN', name: 'ちび☆デビ!2 ~魔法のゆめえほん~', tasks: ['chibi2'] }, { bossAppId: 'CU4vwXZB68V4M5dd', - titleId: BigInt(0x000400000005D100), + titleId: 0x000400000005D100n, titleRegion: 'JPN', name: 'GIRLS MODE よくばり宣言! トキメキUP!', tasks: ['girls', 'info', 'GMTM', 'plus'] }, { bossAppId: 'SAV0iYvYj2VU3WLm', - titleId: BigInt(0x00040000000A3500), + titleId: 0x00040000000A3500n, titleRegion: 'UNK', name: 'Skylanders Giants™', tasks: ['SLGTASK'] }, { bossAppId: 'HfTkA7wv9Xq7uCba', - titleId: BigInt(0x00040000000A9800), + titleId: 0x00040000000A9800n, titleRegion: 'JPN', name: 'モデル☆おしゃれオーディション プラチナ', tasks: ['MO3000', 'MO3001'] }, { bossAppId: 'pIjCA0tlmrOQXZSx', - titleId: BigInt(0x0004000000170900), + titleId: 0x0004000000170900n, titleRegion: 'JPN', name: 'Nobunaga\'s Ambition 2', tasks: ['NA2F000'] }, { bossAppId: 'RjlyCZV4WuOMLhdI', - titleId: BigInt(0x00040000000E9000), + titleId: 0x00040000000E9000n, titleRegion: 'JPN', name: 'JUMP TRIALS SUPREME', tasks: ['dqu5vs9', 'x7a9u1y'] }, { bossAppId: 'yivdj3PrGaiLW2q4', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['GS6'] }, { bossAppId: '6KHx0Ly7P5BU2gMs', - titleId: BigInt(0x0004000000039B00), + titleId: 0x0004000000039B00n, titleRegion: 'JPN', name: 'ポケットサッカーリーグ カルチョビット', tasks: ['hobbit1'] }, { bossAppId: 'qZJMRyERIxHiLC94', - titleId: BigInt(0x0004000000090900), + titleId: 0x0004000000090900n, titleRegion: 'TWN', name: 'nintendogs + cats', tasks: ['task_CH', 'task_TW'] }, { bossAppId: 'Vgw1b4CRc6tTJAuW', - titleId: BigInt(0x00040000000F4900), + titleId: 0x00040000000F4900n, titleRegion: 'JPN', name: 'Gardening Mama Mama and Her Forest Friends', tasks: ['add-on'] }, { bossAppId: '51DuhF1iuLnlykWa', - titleId: BigInt(0x0004000000169C00), + titleId: 0x0004000000169C00n, titleRegion: 'JPN', name: 'ちゃおイラストクラブ', tasks: ['BMDJFOR'] }, { bossAppId: 'Sx5rjDrrIqdlfck9', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['news'] }, { bossAppId: 'SAV0iYvYj2VU3WLm', - titleId: BigInt(0x00040000000E7F00), + titleId: 0x00040000000E7F00n, titleRegion: 'UNK', name: 'Skylanders SWAP Force™', tasks: ['SLGTASK'] }, { bossAppId: 'SAV0iYvYj2VU3WLm', - titleId: BigInt(0x00040000000E6500), + titleId: 0x00040000000E6500n, titleRegion: 'USA', name: 'Skylanders SWAP Force™', tasks: ['SLGTASK'] }, { bossAppId: 'hKHhZ3jJeHOdJ1dh', - titleId: BigInt(0x0004000000178000), + titleId: 0x0004000000178000n, titleRegion: 'JPN', name: 'ドリームガール プルミエ', tasks: ['MO5000', 'MO5001'] }, { bossAppId: 'SVCHF7ayaPXyV8da', - titleId: BigInt(0x000400000012D700), + titleId: 0x000400000012D700n, titleRegion: 'USA', name: 'Gardening Mama 2 Forest Friends', tasks: ['add-on'] }, { bossAppId: 'DRegPN9WZfUgWBa2', - titleId: BigInt(0x0004000000090800), + titleId: 0x0004000000090800n, titleRegion: 'TWN', name: 'nintendogs + cats', tasks: ['task_CH', 'task_TW'] }, { bossAppId: 'gSU6Tz36YEQi4zCC', - titleId: BigInt(0x00040000000C9E00), + titleId: 0x00040000000C9E00n, titleRegion: 'JPN', name: '実写でちびロボ!', tasks: ['mesdat', 'dat', 'mesdat2'] }, { bossAppId: 'SXDe1d24jEHW5Ddy', - titleId: BigInt(0x000400000017B700), + titleId: 0x000400000017B700n, titleRegion: 'UNK', name: 'Fire Emblem if', tasks: ['TASK00', 'TASK02'] }, { bossAppId: 'd9DVo37kDln5fsfx', - titleId: BigInt(0x00040000000F9C00), + titleId: 0x00040000000F9C00n, titleRegion: 'JPN', name: 'A Penguin\'s Troubles +', tasks: ['PGDL000'] }, { bossAppId: 'gt2GNl074uoLp9VR', - titleId: BigInt(0x00040000000BAA00), + titleId: 0x00040000000BAA00n, titleRegion: 'JPN', name: '逆転裁判5', tasks: ['GS5'] }, { bossAppId: 'VEC7I57wA8AL2D5C', - titleId: BigInt(0x00040000000B6300), + titleId: 0x00040000000B6300n, titleRegion: 'UNK', name: 'Girls\' Fashion Shoot', tasks: ['MO2000', 'MO2001'] }, { bossAppId: 'nhIB89NAaMLIOJq5', - titleId: BigInt(0x0004000000053600), + titleId: 0x0004000000053600n, titleRegion: 'JPN', name: 'ペンギンの問題 ザ・ウォーズ 1.1', tasks: ['penwar0'] }, { bossAppId: 'K3cSIGJ2usdI91Mn', - titleId: BigInt(0x0004000000078B00), + titleId: 0x0004000000078B00n, titleRegion: 'JPN', name: 'PROFESSOR LAYTON VS ACE ATTORNEY', tasks: ['FGONLYT'] }, { bossAppId: 'jm9bQFGAHJxWREsU', - titleId: BigInt(0x00040000000A4D00), + titleId: 0x00040000000A4D00n, titleRegion: 'JPN', name: '戦国無双クロニクル セカンド 更新データ', tasks: ['SC2DLC0'] }, { bossAppId: 'SAV0iYvYj2VU3WLm', - titleId: BigInt(0x0004000000091D00), + titleId: 0x0004000000091D00n, titleRegion: 'USA', name: 'Skylanders Giants™', tasks: ['SLGTASK'] }, { bossAppId: 'QehlmytT9n95yB2j', - titleId: BigInt(0x0004000000197F00), + titleId: 0x0004000000197F00n, titleRegion: 'JPN', name: 'アイカツスターズ! ファーストアピール', tasks: ['AKT5F2P'] }, { bossAppId: 'EGcBTbh5QbT5PjmV', - titleId: BigInt(0x0004000000156300), + titleId: 0x0004000000156300n, titleRegion: 'JPN', name: 'SHIKAKUMARU', tasks: ['SAM01'] }, { bossAppId: 'iZWCrW72Sx50ObA4', - titleId: BigInt(0x0004000000112800), + titleId: 0x0004000000112800n, titleRegion: 'JPN', name: 'マギ 新たなる世界', tasks: ['mg20bst'] }, { bossAppId: 'r6xdZT62kmgPHbxK', - titleId: BigInt(0x0004000000156A00), + titleId: 0x0004000000156A00n, titleRegion: 'JPN', name: 'SHIKAKUMARU', tasks: ['SAM02'] }, { bossAppId: '4zQ0tSdYf0MbtMcf', - titleId: BigInt(0x00040000000C9500), + titleId: 0x00040000000C9500n, titleRegion: 'JPN', name: 'peakvox Mew Mew Train', tasks: ['3MD', '3MD2'] }, { bossAppId: 'UikW5d4DcNOUltN1', - titleId: BigInt(0x0004000000156500), + titleId: 0x0004000000156500n, titleRegion: 'JPN', name: 'SHIKAKUMARU', tasks: ['SAM03'] }, { bossAppId: '3B5MnqGceTBxIyHq', - titleId: BigInt(0x00040000000FB200), + titleId: 0x00040000000FB200n, titleRegion: 'JPN', name: 'Nobunaga\'s Ambition', tasks: ['NA3DFR0'] }, { bossAppId: 'SAV0iYvYj2VU3WLm', - titleId: BigInt(0x00040000000A3600), + titleId: 0x00040000000A3600n, titleRegion: 'UNK', name: 'Skylanders Giants™', tasks: ['SLGTASK'] }, { bossAppId: 'VEC7I57wA8AL2D5C', - titleId: BigInt(0x00040000000B6F00), + titleId: 0x00040000000B6F00n, titleRegion: 'UNK', name: 'Girls\' Fashion Shoot', tasks: ['MO2000'] }, { bossAppId: 'VEC7I57wA8AL2D5C', - titleId: BigInt(0x0004000000065700), + titleId: 0x0004000000065700n, titleRegion: 'JPN', name: 'nicola監修 モデル☆おしゃれオーディション2', tasks: ['MO2000', 'MO2001'] }, { bossAppId: 'RkgNduGP6rRkubPC', - titleId: BigInt(0x0004000000068300), + titleId: 0x0004000000068300n, titleRegion: 'UNK', name: 'とびだすプリクラ☆ キラデコレボリューション', tasks: ['NadlTsk'] }, { bossAppId: 'oqxYfft6J9X33Xcm', - titleId: BigInt(0x0004000000170400), + titleId: 0x0004000000170400n, titleRegion: 'JPN', name: 'nicopuchi', tasks: ['BNPJFOR'] }, { bossAppId: 'rS9N1JsddhwfKlVp', - titleId: BigInt(0x000400000014DF00), + titleId: 0x000400000014DF00n, titleRegion: 'JPN', name: '戦国無双 Chronicle 3', tasks: ['SC3DLC0', 'FGONLYT'] }, { bossAppId: 'VEC7I57wA8AL2D5C', - titleId: BigInt(0x00040000000B4800), + titleId: 0x00040000000B4800n, titleRegion: 'UNK', name: 'Girls\' Fashion Shoot', tasks: ['MO2000'] }, { bossAppId: '8xzpJYrYTQQLhs5U', - titleId: BigInt(0x00040000000FB400), + titleId: 0x00040000000FB400n, titleRegion: 'JPN', name: 'モデル☆おしゃれオーディション ドリームガール', tasks: ['MO4000', 'MO4001'] }, { bossAppId: 'huaVwH8vmGDlynY5', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['0000001'] }, { bossAppId: 'gQdtAv8S5QqhAMq6', - titleId: BigInt(0x00040000000F8000), + titleId: 0x00040000000F8000n, titleRegion: 'UNK', name: 'Inazuma Eleven 3 Team Ogre Attacks!', tasks: ['news', 'FGONLYT'] }, { bossAppId: 'TPqWmZW8y6NEWz9N', - titleId: BigInt(0x000400000008B500), + titleId: 0x000400000008B500n, titleRegion: 'UNK', name: 'MARIO KART 7', tasks: ['comm', 'ranking', 'ghost'] }, { bossAppId: '4813nyO3ACQf2cwz', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['item'] }, { bossAppId: 'sk1jvwem58tprBkZ', - titleId: BigInt(0x0004000000032000), + titleId: 0x0004000000032000n, titleRegion: 'JPN', name: 'DEAD OR ALIVE Dimensions', tasks: ['costume', 'foeevnt', 'info'] }, { bossAppId: 'crLAF7DBkd3coa3v', - titleId: BigInt(0x0004000000030500), + titleId: 0x0004000000030500n, titleRegion: 'JPN', name: 'SUPER STREET FIGHTER Ⅳ 3D EDITION', tasks: ['SPA4APP'] }, { bossAppId: '9DLWs8Zl50qCaWxp', - titleId: BigInt(0x0004000000040200), + titleId: 0x0004000000040200n, titleRegion: 'UNK', name: 'SAMURAI WARRIORS: Chronicles', tasks: ['JMCDLC0', 'JMCDLC1', 'JMCDLC2'] }, { bossAppId: 'gzPtSkOPonlun2Ug', - titleId: BigInt(0x0004000000173B00), + titleId: 0x0004000000173B00n, titleRegion: 'EUR', name: 'SAMURAI WARRIORS: Chronicles 3', tasks: ['SC3DLC0', 'FGONLYT'] }, { bossAppId: 'J8jLC3PD2ERSNrmK', - titleId: BigInt(0x000400000008CC00), + titleId: 0x000400000008CC00n, titleRegion: 'EUR', name: 'Sparkle Snapshots 3D', tasks: ['NadlTsk'] }, { bossAppId: 'HEMBJCoTyLKjm9pU', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['weekly'] }, { bossAppId: 'OTjVevatduCI863l', - titleId: BigInt(0x00040000000F7B00), + titleId: 0x00040000000F7B00n, titleRegion: 'EUR', name: 'Inazuma Eleven 3 Bomb Blast', tasks: ['news', 'FGONLYT'] }, { bossAppId: 'NiAJFnn4fyAAiggq', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['PTASK01', 'ITASK01', 'PTASK02'] }, { bossAppId: 'oHl1Ay3RPWlWngp3', - titleId: BigInt(0x0004000000030B00), + titleId: 0x0004000000030B00n, titleRegion: 'JPN', name: 'nintendogs + cats', tasks: ['task_JP'] }, { bossAppId: 'V3xe0ZHtBxzJwJtL', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'v5fxzRdEaBW5ujTi', - titleId: BigInt(0x00040000000CC900), + titleId: 0x00040000000CC900n, titleRegion: 'KOR', name: 'Swapnote', tasks: ['JFR_NT1', 'JFR_NT2', 'JFR_NT3', 'JFR_LS2', 'JFR_AP2', 'JFR_DFR', 'JFR_GM2', 'JFR_U01', 'JFR_U02', 'JFR_U03', 'JFR_U04', 'JFR_U05', 'JFR_U06', 'JFR_U07', 'JFR_UUS'] }, { bossAppId: 'OjI5Ysd6x9mFk22c', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['zdltdat', 'FGONLYT'] }, { bossAppId: '99qH2qwTlJy9XDKs', - titleId: BigInt(0x0004000000030F00), + titleId: 0x0004000000030F00n, titleRegion: 'KOR', name: 'nintendogs + cats', tasks: ['task_KR'] }, { bossAppId: '7Fqa3EZYYlvKEh5v', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JQ5J'] }, { bossAppId: '4r5wpBYnaf6rldJ3', - titleId: BigInt(0x00040000000F7E00), + titleId: 0x00040000000F7E00n, titleRegion: 'UNK', name: 'Inazuma Eleven 3 Lightning Bolt', tasks: ['FGONLYT', 'news'] }, { bossAppId: '3ERCXw1170g54UBs', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JC9K'] }, { bossAppId: '9dXYzFjKdKbJMCWe', - titleId: BigInt(0x0004001000027800), + titleId: 0x0004001000027800n, titleRegion: 'UNK', name: 'Unknown', tasks: ['MIIDATA', 'PANEL', 'PANELLM', 'LEGEND', 'UPDATE'] }, { bossAppId: 'hMwDQ4myueRLe1J9', - titleId: BigInt(0x0004000000092E00), + titleId: 0x0004000000092E00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JZ8J'] }, { bossAppId: 'L7j0ivgZfXSskmmY', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JMCDLC0', 'JMCDLC1', 'JMCDLC2'] }, { bossAppId: 'CAsKat7WKddnAZX3', - titleId: BigInt(0x000400000019F600), + titleId: 0x000400000019F600n, titleRegion: 'JPN', name: 'Girls Mode 4 スター☆スタイリスト', tasks: ['NEWS', 'POSTER'] }, { bossAppId: 'S8m7Nskf5LDHOAFr', - titleId: BigInt(0x0004000000182E00), + titleId: 0x0004000000182E00n, titleRegion: 'JPN', name: 'PUZZLE & DRAGONS CROSS DRAGON type', tasks: ['INFITEM', 'INFEVT', 'INFPUB', 'FGONLYT'] }, { bossAppId: 's9byficX77gemj2B', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'hXDTHQ4WSEmdUHmH', - titleId: BigInt(0x000400000012D800), + titleId: 0x000400000012D800n, titleRegion: 'JPN', name: 'GIRLS MODE 3 キラキラ☆コーデ', tasks: ['NEWS', 'POSTER', 'FGONLYT'] }, { bossAppId: 'txq0HfojeCsTXwdB', - titleId: BigInt(0x0004001000028800), + titleId: 0x0004001000028800n, titleRegion: 'UNK', name: 'Unknown', tasks: ['PANELLM', 'MIIDATA', 'PANEL', 'LEGEND'] }, { bossAppId: 'v77n3f1JniCBO6DY', - titleId: BigInt(0x00040000000EC000), + titleId: 0x00040000000EC000n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SPAJ'] }, { bossAppId: 'IWu6JntKmBfUFK0D', - titleId: BigInt(0x0004000000105E00), + titleId: 0x0004000000105E00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SPFJ'] }, { bossAppId: 'x6bxVnGPunxrUEvR', - titleId: BigInt(0x00040000000B9700), + titleId: 0x00040000000B9700n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SN2J'] }, { bossAppId: 'oYzBvnIepXcRAWB8', - titleId: BigInt(0x0004000000155D00), + titleId: 0x0004000000155D00n, titleRegion: 'KOR', name: 'Cooking Mama', tasks: ['weekly'] }, { bossAppId: 'wSajRGAQdY6dffMd', - titleId: BigInt(0x000400000006C500), + titleId: 0x000400000006C500n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJGJ'] }, { bossAppId: 'riNWIl9p0ZdQORe5', - titleId: BigInt(0x0004000000095700), + titleId: 0x0004000000095700n, titleRegion: 'JPN', name: 'New Art Academy', tasks: ['london1'] }, { bossAppId: 'IGctFNjjb8E1TONI', - titleId: BigInt(0x000400000018F100), + titleId: 0x000400000018F100n, titleRegion: 'USA', name: 'Dragon Quest VIII', tasks: ['FGONLYT'] }, { bossAppId: 'XT7XAO9p7C9Rfvvx', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JQZJ'] }, { bossAppId: 'LUX1uLHXK82phCiX', - titleId: BigInt(0x00040000001AB900), + titleId: 0x00040000001AB900n, titleRegion: 'EUR', name: 'Team Kirby Clash Deluxe', tasks: ['FGONLYT'] }, { bossAppId: 'uzKXEvs4hNMA25oG', - titleId: BigInt(0x0004000000137800), + titleId: 0x0004000000137800n, titleRegion: 'EUR', name: 'Puzzle & Dragons Z + Super Mario Bros. Edition', tasks: ['FGONLYT'] }, { bossAppId: 'iR1A5X1wiLEkSynO', - titleId: BigInt(0x0004000000167C00), + titleId: 0x0004000000167C00n, titleRegion: 'KOR', name: 'Super Smash Bros. for Nintendo 3DS', tasks: ['NEWS', 'amiibo', 'FGONLYT'] }, { bossAppId: '24XiNVrAmhfjS82s', - titleId: BigInt(0x0004000000137700), + titleId: 0x0004000000137700n, titleRegion: 'USA', name: 'Puzzle & Dragons Z + Super Mario Bros. Edition', tasks: ['FGONLYT'] }, { bossAppId: 'S2OgfKUn0ZVKpO2G', - titleId: BigInt(0x000400000004DC00), + titleId: 0x000400000004DC00n, titleRegion: 'UNK', name: 'Imagine™ Fashion World 3D', tasks: ['NL00001'] }, { bossAppId: 'drVvdr4bzLr25i9t', - titleId: BigInt(0x00040000001A0000), + titleId: 0x00040000001A0000n, titleRegion: 'JPN', name: 'Team Kirby Clash Deluxe', tasks: ['FGONLYT'] }, { bossAppId: 'l3gOdKu5CZ45zxTk', - titleId: BigInt(0x000400000009AD00), + titleId: 0x000400000009AD00n, titleRegion: 'UNK', name: 'Foto Glitter 3D', tasks: ['NadlTsk'] }, { bossAppId: '0AgBH2eLZV5YqTMW', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['0000001'] }, { bossAppId: '4n7UxjbCGnuq5FYy', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['0000001'] }, { bossAppId: '7Cs4OOUfDbCC9su5', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['0000001'] }, { bossAppId: 'Duu3faN13x95aVMY', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['0000001'] }, { bossAppId: 'ERl9vWxc1lKwBCYk', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['0000001'] }, { bossAppId: 'NbmO38IyGwh7nt7t', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['0000001'] }, { bossAppId: 'X00vNlT4qqcyzxlL', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['0000001'] }, { bossAppId: 'XUWPufbAdkuBsj26', - titleId: BigInt(0x0004000000198500), + titleId: 0x0004000000198500n, titleRegion: 'KOR', name: 'Dragon Quest VIII', tasks: ['FGONLYT'] }, { bossAppId: 'YqwaX4YDDsNecFub', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['miku_01', 'miku'] }, { bossAppId: 'oX0GI0HwZvOJHMo9', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['0000001'] }, { bossAppId: 'tY7USAZRiWlUgQVe', - titleId: BigInt(0x0004000000047800), + titleId: 0x0004000000047800n, titleRegion: 'USA', name: 'Imagine™ Fashion Life', tasks: ['NL00001'] }, { bossAppId: 'wJoxCKvP6AFEHecL', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['0000001'] }, { bossAppId: 'wTy7ND4OqUdfR7nH', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['0000001'] }, { bossAppId: 'xSycjLbQyJJBlc7x', - titleId: BigInt(0x000400000015CD00), + titleId: 0x000400000015CD00n, titleRegion: 'JPN', name: 'Dragon Quest VIII', tasks: ['FGONLYT'] }, { bossAppId: 'xedkyOsho6iccqUT', - titleId: BigInt(0x000400000018F200), + titleId: 0x000400000018F200n, titleRegion: 'EUR', name: 'Dragon Quest VIII', tasks: ['FGONLYT'] }, { bossAppId: 'y1kgSu9oYpgC1oOm', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['0000001'] }, { bossAppId: '2qmhIndG8vluZoEO', - titleId: BigInt(0x000400000008C800), + titleId: 0x000400000008C800n, titleRegion: 'JPN', name: 'DRAGON QUEST Ⅹ Morph de Battle', tasks: ['lucky01', 'lucky03', 'lucky02'] }, { bossAppId: 'SDIA7fBPxg6X01AW', - titleId: BigInt(0x0004001000026800), + titleId: 0x0004001000026800n, titleRegion: 'UNK', name: 'Unknown', tasks: ['PANEL', 'MIIDATA', 'LEGEND', 'PANELLM'] }, { bossAppId: 'oUTbEtsyaJ7Fzx4h', - titleId: BigInt(0x000400000006B600), + titleId: 0x000400000006B600n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJYJ'] }, { bossAppId: 'ZwMc34IYS96zWJRD', - titleId: BigInt(0x00040000000F7F00), + titleId: 0x00040000000F7F00n, titleRegion: 'EUR', name: 'Inazuma Eleven 3 Team Ogre Attacks!', tasks: ['news', 'FGONLYT'] }, { bossAppId: '4mMi9ll6zFrSftt9', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JQ4J'] }, { bossAppId: 'MSN04ONcEZyPL5dW', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SPEJ'] }, { bossAppId: '2hZYkL2D64eCrTzg', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['MESSAGE', 'DL'] }, { bossAppId: 'FMhu53ZtDLnz2ZZ1', - titleId: BigInt(0x0004000000168600), + titleId: 0x0004000000168600n, titleRegion: 'TWN', name: 'Super Smash Bros. for Nintendo 3DS', tasks: ['NEWS', 'amiibo', 'FGONLYT'] }, { bossAppId: 'xjMBeunnHUfxuK2d', - titleId: BigInt(0x0004000000151F00), + titleId: 0x0004000000151F00n, titleRegion: 'TWN', name: 'Pokémon Art Academy', tasks: ['pnote', 'FGONLYT'] }, { bossAppId: 'kuQRrwInvnbJvfa6', - titleId: BigInt(0x0004000000143400), + titleId: 0x0004000000143400n, titleRegion: 'UNK', name: 'New SUPER MARIO BROS. 2: Gold Edition', tasks: ['present', 'patch'] }, { bossAppId: 'HWC1oxxoFRhulvSL', - titleId: BigInt(0x00040000000B9600), + titleId: 0x00040000000B9600n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNZJ'] }, { bossAppId: 'cxWp2lsjKrG7diwk', - titleId: BigInt(0x00040000000B9200), + titleId: 0x00040000000B9200n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNVJ'] }, { bossAppId: 'h8S6cmA4FdBvPYZc', - titleId: BigInt(0x00040000000EC100), + titleId: 0x00040000000EC100n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SPBJ'] }, { bossAppId: 'i3iGTnVbl4tKi9KW', - titleId: BigInt(0x00040000000EBF00), + titleId: 0x00040000000EBF00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SN9J'] }, { bossAppId: 'NxEAqX0zZl4f0Im0', - titleId: BigInt(0x00040000000FA500), + titleId: 0x00040000000FA500n, titleRegion: 'JPN', name: 'クッキングママ5', tasks: ['weekly'] }, { bossAppId: 'N6VoGN3wJ698hOiI', - titleId: BigInt(0x0004000000106000), + titleId: 0x0004000000106000n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SPHJ'] }, { bossAppId: '9SCQWB4mFxhoXyQK', - titleId: BigInt(0x00040000001AE200), + titleId: 0x00040000001AE200n, titleRegion: 'JPN', name: '大逆転裁判2 ―成歩堂龍ノ介の覺悟―', tasks: ['SHOLMES'] }, { bossAppId: 'pdR21q4ZRkxaSeVJ', - titleId: BigInt(0x000400000006BC00), + titleId: 0x000400000006BC00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJSJ'] }, { bossAppId: '3BraiaIl0mpnuoOA', - titleId: BigInt(0x00040000000AA000), + titleId: 0x00040000000AA000n, titleRegion: 'JPN', name: 'Unknown', tasks: ['MAJ'] }, { bossAppId: 'YVH4WfoFszQwwG3w', - titleId: BigInt(0x00040000000CCA00), + titleId: 0x00040000000CCA00n, titleRegion: 'TWN', name: 'Swapnote', tasks: ['JFR_LS2', 'JFR_NT1', 'JFR_NT2', 'JFR_NT3', 'JFR_AP2', 'JFR_DFR', 'JFR_GM2', 'JFR_U01', 'JFR_U02', 'JFR_U03', 'JFR_U04', 'JFR_U05', 'JFR_U06', 'JFR_U07', 'JFR_UUS'] }, { bossAppId: 'DciJTpUDHw8APohR', - titleId: BigInt(0x000400000009AB00), + titleId: 0x000400000009AB00n, titleRegion: 'UNK', name: 'Foto-Zauber 3D', tasks: ['NadlTsk'] }, { bossAppId: 'HLLrgikagMK0pzbs', - titleId: BigInt(0x000400000006B000), + titleId: 0x000400000006B000n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ6J'] }, { bossAppId: 'prddiG9hlRiH8NEC', - titleId: BigInt(0x000400000006AF00), + titleId: 0x000400000006AF00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ7J'] }, { bossAppId: 'cmL3N7HUvMJGfGFp', - titleId: BigInt(0x000400000006B100), + titleId: 0x000400000006B100n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ5J'] }, { bossAppId: 'Ryh3zAYKR1zjJWc3', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['zdltdat'] }, { bossAppId: 'HvwgRXicx4vCBFTL', - titleId: BigInt(0x0004000000199000), + titleId: 0x0004000000199000n, titleRegion: 'KOR', name: 'Animal Crossing: New Leaf - Welcome amiibo', tasks: ['news', 'news_p', 'dream', 'dream_p', 'FGONLYT'] }, { bossAppId: 'OIC58iiMakdqQSLH', - titleId: BigInt(0x00040000000C4F00), + titleId: 0x00040000000C4F00n, titleRegion: 'KOR', name: 'Nintendo presents: New Style Boutique', tasks: ['GMTM', 'girls', 'info'] }, { bossAppId: 'J0ovIyLP9tYaJqaq', - titleId: BigInt(0x00040000000D1C00), + titleId: 0x00040000000D1C00n, titleRegion: 'JPN', name: 'Unknown', tasks: ['MBE'] }, { bossAppId: 'BAIXhLmD2rGJVAvQ', - titleId: BigInt(0x00040000000AA400), + titleId: 0x00040000000AA400n, titleRegion: 'JPN', name: 'Unknown', tasks: ['MAL'] }, { bossAppId: 'AOGimz8AhOCZLJks', - titleId: BigInt(0x00040000000D1B00), + titleId: 0x00040000000D1B00n, titleRegion: 'JPN', name: 'Unknown', tasks: ['MBD'] }, { bossAppId: 'MSswwTMHhCzsTg4l', - titleId: BigInt(0x00040000000AA300), + titleId: 0x00040000000AA300n, titleRegion: 'JPN', name: 'Unknown', tasks: ['MAK'] }, { bossAppId: '83j0DZ0YuEbd6OTR', - titleId: BigInt(0x000400000006B700), + titleId: 0x000400000006B700n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJXJ'] }, { bossAppId: '0kXLgixMNsmA8Ulw', - titleId: BigInt(0x000400000006AE00), + titleId: 0x000400000006AE00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ8J'] }, { bossAppId: '34pqkzXxOsXTju6N', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JQBJ'] }, { bossAppId: '3mqMq8p8Vu7clovO', - titleId: BigInt(0x000400000006C800), + titleId: 0x000400000006C800n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJDJ'] }, { bossAppId: '4z69DLEmm8eWmAWh', - titleId: BigInt(0x00040000000AC100), + titleId: 0x00040000000AC100n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNNJ'] }, { bossAppId: '7NAqDIHJVMr4Svya', - titleId: BigInt(0x0004000000092F00), + titleId: 0x0004000000092F00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JZ7J'] }, { bossAppId: 'A9ikIHjkbhlbkQGZ', - titleId: BigInt(0x000400000006C700), + titleId: 0x000400000006C700n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJEJ'] }, { bossAppId: 'aA9hBmp4EagWTNM5', - titleId: BigInt(0x000400000006AD00), + titleId: 0x000400000006AD00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ9J'] }, { bossAppId: 'AY0oNJfCHmoAqyGJ', - titleId: BigInt(0x000400000006C200), + titleId: 0x000400000006C200n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJLJ'] }, { bossAppId: 'BDgoKPBE6iIm6GvU', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JQYJ'] }, { bossAppId: 'BnYCVYAGDPA2ODsF', - titleId: BigInt(0x000400000006BB00), + titleId: 0x000400000006BB00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJTJ'] }, { bossAppId: 'bO0kDGfd70dY8jP7', - titleId: BigInt(0x000400000007B500), + titleId: 0x000400000007B500n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JQEJ'] }, { bossAppId: 'bP3RO5aNzfiUhTqG', - titleId: BigInt(0x0004000000110D00), + titleId: 0x0004000000110D00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SPKJ'] }, { bossAppId: 'BXzMU4QDn6gJzHL8', - titleId: BigInt(0x000400000006C900), + titleId: 0x000400000006C900n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJCJ'] }, { bossAppId: 'dCdWyWjvMdwC55jp', - titleId: BigInt(0x00040000000B9500), + titleId: 0x00040000000B9500n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNYJ'] }, { bossAppId: 'eUDyHqSSYL8Dbnw7', - titleId: BigInt(0x000400000006BE00), + titleId: 0x000400000006BE00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJQJ'] }, { bossAppId: 'gH85V9hCAuisHPiI', - titleId: BigInt(0x00040000000A9D00), + titleId: 0x00040000000A9D00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNSJ'] }, { bossAppId: 'gIKNS5K3QTXk8uZ0', - titleId: BigInt(0x000400000006B300), + titleId: 0x000400000006B300n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ3J'] }, { bossAppId: 'IwPki1shz2Sa5Jtf', - titleId: BigInt(0x000400000006C000), + titleId: 0x000400000006C000n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJNJ'] }, { bossAppId: 'kIk3rjwlpys7cBgb', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'MjC52YHyd1mUxL3K', - titleId: BigInt(0x000400000006B500), + titleId: 0x000400000006B500n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJZJ'] }, { bossAppId: 'Mrv7MaHG7NUKIssp', - titleId: BigInt(0x00040000000ABF00), + titleId: 0x00040000000ABF00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNLJ'] }, { bossAppId: 'mvFYPcqk08wjN1Lf', - titleId: BigInt(0x0004000000105F00), + titleId: 0x0004000000105F00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SPGJ'] }, { bossAppId: 'NSfT7yEow0T98638', - titleId: BigInt(0x000400000007B700), + titleId: 0x000400000007B700n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JQGJ'] }, { bossAppId: 'O5RNJSKQSbaGMWMe', - titleId: BigInt(0x000400000006C300), + titleId: 0x000400000006C300n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJKJ'] }, { bossAppId: 'O7IQrnoLPrUckjY6', - titleId: BigInt(0x000400000006C400), + titleId: 0x000400000006C400n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJHJ'] }, { bossAppId: 'PR5223DvK2yJeLm7', - titleId: BigInt(0x00040000000A0300), + titleId: 0x00040000000A0300n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNDJ'] }, { bossAppId: 'QI6XYVa7oCGM7PSb', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SN5J'] }, { bossAppId: 'QIr38311UsvE7kpy', - titleId: BigInt(0x000400000006C600), + titleId: 0x000400000006C600n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJFJ'] }, { bossAppId: 'rbxZVYCrNIIZiV1Y', - titleId: BigInt(0x00040000000B9400), + titleId: 0x00040000000B9400n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNXJ'] }, { bossAppId: 'Rk3lD7SViJwXHqxY', - titleId: BigInt(0x00040000000A0100), + titleId: 0x00040000000A0100n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNCJ'] }, { bossAppId: 's332O6HVmb1mrZlJ', - titleId: BigInt(0x000400000007B100), + titleId: 0x000400000007B100n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JQAJ'] }, { bossAppId: 'szZ7fUtxGj3C8vAm', - titleId: BigInt(0x000400000006BF00), + titleId: 0x000400000006BF00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJPJ'] }, { bossAppId: 'ufWtleWnZVr175yL', - titleId: BigInt(0x000400000006B900), + titleId: 0x000400000006B900n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJVJ'] }, { bossAppId: 'uZp0tj5WSQXC1ajP', - titleId: BigInt(0x00040000000A9C00), + titleId: 0x00040000000A9C00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNRJ'] }, { bossAppId: 'VaaaQIH2Y4MCHZoE', - titleId: BigInt(0x000400000006B400), + titleId: 0x000400000006B400n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ2J'] }, { bossAppId: 'ValTqsbfLsdprJ9D', - titleId: BigInt(0x00040000000B9300), + titleId: 0x00040000000B9300n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNWJ'] }, { bossAppId: 'VJotYqkLqCNb6WEZ', - titleId: BigInt(0x000400000006C100), + titleId: 0x000400000006C100n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJMJ'] }, { bossAppId: 'vWajW15RdM4DoMkY', - titleId: BigInt(0x000400000007B600), + titleId: 0x000400000007B600n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JQFJ'] }, { bossAppId: 'wiDPs6x6gT5GuQ0L', - titleId: BigInt(0x000400000006B800), + titleId: 0x000400000006B800n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJWJ'] }, { bossAppId: 'wIfkwczLlzXY1Ih0', - titleId: BigInt(0x00040000000A0400), + titleId: 0x00040000000A0400n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNEJ'] }, { bossAppId: 'WvnKyyWlllNC1P3l', - titleId: BigInt(0x00040000000A9B00), + titleId: 0x00040000000A9B00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNQJ'] }, { bossAppId: 'x8ak29C2mgHwjQm5', - titleId: BigInt(0x000400000006B200), + titleId: 0x000400000006B200n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJ4J'] }, { bossAppId: 'XAdIxXWPa1PDWMkm', - titleId: BigInt(0x0004000000150A00), + titleId: 0x0004000000150A00n, titleRegion: 'UNK', name: 'Imagine® Collection', tasks: ['RACHEL'] }, { bossAppId: 'XH9X43PmZ9lGVT7i', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JQ6J'] }, { bossAppId: 'Yhg9sWpNbOT95HTu', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'yMP5bCaKlAgItWqV', - titleId: BigInt(0x00040000000D9200), + titleId: 0x00040000000D9200n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SN6J'] }, { bossAppId: 'yoN5MAgfB2yTKrLz', - titleId: BigInt(0x000400000006BA00), + titleId: 0x000400000006BA00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JJUJ'] }, { bossAppId: 'zztKIdGngfCS9cS5', - titleId: BigInt(0x00040000000A9A00), + titleId: 0x00040000000A9A00n, titleRegion: 'UNK', name: 'Unknown', tasks: ['SNPJ'] }, { bossAppId: 'Bo1owHMnKHjFK2Nk', - titleId: BigInt(0x0004000000143300), + titleId: 0x0004000000143300n, titleRegion: 'UNK', name: 'New SUPER MARIO BROS. 2: Gold Edition', tasks: ['patch', 'present'] }, { bossAppId: 'MLJEOnEgGcwd5uaP', - titleId: BigInt(0x00040000000F7C00), + titleId: 0x00040000000F7C00n, titleRegion: 'UNK', name: 'Inazuma Eleven 3 Bomb Blast', tasks: ['news', 'FGONLYT'] }, { bossAppId: '1j4M12R6obSARq8y', - titleId: BigInt(0x0004000000086500), + titleId: 0x0004000000086500n, titleRegion: 'UNK', name: 'Animal Crossing New Leaf', tasks: ['news', 'pnews', 'FGONLYT', 'dream', 'dream_p', 'news_p'] }, { bossAppId: '3l15Yk6cOm0AWsSP', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'BUvZ7pfEEhcLt4B2', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['news'] }, { bossAppId: 'Qq4luhjWR94QqvhX', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['daily'] }, { bossAppId: 'f9sKNCJ94RghSSyg', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['GS5'] }, { bossAppId: 'CnnviXAnc0qGGTio', - titleId: BigInt(0x00040000000FEB00), + titleId: 0x00040000000FEB00n, titleRegion: 'KOR', name: 'Phoenix Wright: Ace Attorney Dual Destinies', tasks: ['GS5'] }, { bossAppId: 'fMTQWnFhZQ2BijI5', - titleId: BigInt(0x000400000017B800), + titleId: 0x000400000017B800n, titleRegion: 'TWN', name: 'Fire Emblem if', tasks: ['TASK00', 'TASK02'] }, { bossAppId: 'Xv543XtNNYZU2OXz', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT'] }, { bossAppId: 'eyCw6Qph1Hz1uQmC', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['JAU'] }, { bossAppId: 'jeUQ3nnY70ddJ87q', - titleId: BigInt(0x000400000009AE00), + titleId: 0x000400000009AE00n, titleRegion: 'UNK', name: 'Cámara Glamour 3D', tasks: ['NadlTsk'] }, { bossAppId: 'bRWXguqQXXAT99Th', - titleId: BigInt(0x00040000001BC900), + titleId: 0x00040000001BC900n, titleRegion: 'TWN', name: 'Fire Emblem Echoes: Shadows of Valentia', tasks: ['TASK00', 'FGONLYT', 'TASK01'] }, { bossAppId: '1XCuNDvhVlp5r7mO', - titleId: BigInt(0x00040000000C3900), + titleId: 0x00040000000C3900n, titleRegion: 'JPN', name: 'パズドラZ', tasks: ['FGONLYT', 'PDZDLDG', 'PDZDLNP', 'PDZDLTI'] }, { bossAppId: '7f094kCjE5HDVGlu', - titleId: BigInt(0x00040000000CBD00), + titleId: 0x00040000000CBD00n, titleRegion: 'JPN', name: 'Oshaberi-Chat', tasks: ['JCTTask'] }, { bossAppId: 'aOJyK9TocYWjxMRR', - titleId: BigInt(0x000400000004A900), + titleId: 0x000400000004A900n, titleRegion: 'UNK', name: 'Nintendo Video', tasks: ['ESJ_CNF', 'ESJ_MD1', 'ESJ_MD2', 'ESJ_MD3', 'ESJ_MD4'] }, { bossAppId: 'G0VUurQf4EVYe37v', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['0ER_CNF', '1ER_DBN', '2ER_DCM', '3ER_AX1', '4ER_CX1', '5ER_AX2', '6ER_CX2', '7ER_AX3', '8ER_CX3', '9ER_NTD'] }, { bossAppId: '3PSCke6Q3B84IzWl', - titleId: BigInt(0x0004000000031900), + titleId: 0x0004000000031900n, titleRegion: 'KOR', name: 'nintendogs + cats', tasks: ['task_KR'] }, { bossAppId: 'XaphN80NdywuA9Ef', - titleId: BigInt(0x0004000000155E00), + titleId: 0x0004000000155E00n, titleRegion: 'KOR', name: 'Gardening Mama', tasks: ['add-on'] }, { bossAppId: '0hYP8YjQTWpdlt0v', - titleId: BigInt(0x0004000000031400), + titleId: 0x0004000000031400n, titleRegion: 'KOR', name: 'nintendogs + cats', tasks: ['task_KR'] }, { bossAppId: '7HXSa828OHpIlsMh', - titleId: BigInt(0x000400000012C700), + titleId: 0x000400000012C700n, titleRegion: 'KOR', name: 'Disney Magical World', tasks: ['BNMCASI', 'BNMCASL', 'FGONLYT'] }, { bossAppId: 'j3JOMyqvsV4ocbUM', - titleId: BigInt(0x0004000000150B00), + titleId: 0x0004000000150B00n, titleRegion: 'KOR', name: 'Pokémon Art Academy', tasks: ['FGONLYT', 'pnote'] }, { bossAppId: 'l0BkjqC5MI1bEY29', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['Ds2ocTk'] }, { bossAppId: 'sKFrnzQCMkGb60y7', - titleId: BigInt(0x0004000000106300), + titleId: 0x0004000000106300n, titleRegion: 'KOR', name: 'Mario Golf: World Tour', tasks: ['FGONLYT', 'spnt_t', 'spnt_x'] }, { bossAppId: '3V3eHMNiNmweRqQO', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'HFnqrJv4fmyO5ItT', - titleId: BigInt(0x0004000000091F00), + titleId: 0x0004000000091F00n, titleRegion: 'KOR', name: 'Model Audition Superstar 2 ', tasks: ['MO2000', 'MO2001'] }, { bossAppId: 'IyMJMSHvuD2nC8r1', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'JJVi8mFl1LRVk4gS', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'VQU2jX0YCWHynn03', - titleId: BigInt(0x000400000015A700), + titleId: 0x000400000015A700n, titleRegion: 'KOR', name: 'Puzzle & Dragons Z + Super Mario Bros. Edition', tasks: ['FGONLYT'] }, { bossAppId: 'cm0cPPTXpEkGEeII', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'y4Q3Z35IQzh6nWds', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['FGONLYT', 'news'] }, { bossAppId: 'aKd4hjqxg87UDbwy', - titleId: BigInt(0x000400000018F900), + titleId: 0x000400000018F900n, titleRegion: 'KOR', name: 'Phoenix Wright: Ace Attorney Spirit of Justice', tasks: ['GS6'] }, { bossAppId: '4q11abiKEAOBaBMD', - titleId: BigInt(0x000400000016D100), + titleId: 0x000400000016D100n, titleRegion: 'TWN', name: 'Puzzle & Dragons Z + Super Mario Bros. Edition', tasks: ['FGONLYT'] }, { bossAppId: '3yire4GGq6BfeD71', - titleId: BigInt(0x0), + titleId: 0x0n, titleRegion: 'UNK', name: 'Unknown', tasks: ['news'] From 18b71796c69a5b21aefb827c9747c2c42a6bf872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sun, 9 Nov 2025 00:54:05 +0000 Subject: [PATCH 18/30] chore: Update pretendonetwork/grpc to 2.3.4 --- package-lock.json | 42 +++++-- package.json | 2 +- src/cli/tasks.cmd.ts | 2 +- .../grpc/boss/v1/list-known-boss-apps.ts | 114 +++++++++--------- src/services/grpc/boss/v1/list-tasks.ts | 2 +- src/services/grpc/boss/v1/register-task.ts | 4 +- src/services/grpc/boss/v1/update-task.ts | 2 +- 7 files changed, 95 insertions(+), 73 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7cbeca8..233d95f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@aws-sdk/client-s3": "^3.723.0", "@pretendonetwork/boss-crypto": "^1.1.3", - "@pretendonetwork/grpc": "^2.3.3", + "@pretendonetwork/grpc": "^2.3.4", "@typegoose/auto-increment": "^4.13.1", "commander": "^14.0.0", "cron": "^4.3.3", @@ -358,6 +358,7 @@ "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.723.0.tgz", "integrity": "sha512-9IH90m4bnHogBctVna2FnXaIGVORncfdxcqeEIovOxjIJJyHDmEAtA7B91dAM4sruddTbVzOYnqfPVst3odCbA==", + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -410,6 +411,7 @@ "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.723.0.tgz", "integrity": "sha512-YyN8x4MI/jMb4LpHsLf+VYqvbColMK8aZeGWVk2fTFsmt8lpTYGaGC1yybSwGX42mZ4W8ucu8SAYSbUraJZEjA==", + "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -2070,9 +2072,9 @@ } }, "node_modules/@pretendonetwork/grpc": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.3.tgz", - "integrity": "sha512-65QH9XneS1xQDTmZ/0XhHEXCPd4Sx3fMt9RDlCc0m7nl2LhaupkAW//GJhJ4QTGbN3gggv5JKME0ZfFqO6Pqbg==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.4.tgz", + "integrity": "sha512-Ajtk8nHR2Auaks6SKUkm0cDVivjuIhitz6xaaxmVfb0hyVdZZO4EfjEtQdsRLmfbA+SWDxdszBPRD+LBsIdjxQ==", "license": "AGPL-3.0-only", "dependencies": { "@bufbuild/protobuf": "^2.2.2", @@ -3389,6 +3391,7 @@ "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.39.1", "@typescript-eslint/types": "8.39.1", @@ -4002,6 +4005,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5045,6 +5049,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -5112,6 +5117,7 @@ "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -5322,6 +5328,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7394,6 +7401,7 @@ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.18.1.tgz", "integrity": "sha512-K0RfrUXXufqNRZZjvAGdyjydB91SnbWxlwFYi5t7zN2DxVWFD3c6puia0/7xfBwZm6RCpYOVdYFlRFpoDWiC+w==", "license": "MIT", + "peer": true, "dependencies": { "bson": "^6.10.4", "kareem": "2.6.3", @@ -9139,6 +9147,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -9473,6 +9482,7 @@ "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9569,6 +9579,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -10175,6 +10186,7 @@ "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.723.0.tgz", "integrity": "sha512-9IH90m4bnHogBctVna2FnXaIGVORncfdxcqeEIovOxjIJJyHDmEAtA7B91dAM4sruddTbVzOYnqfPVst3odCbA==", + "peer": true, "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -10221,6 +10233,7 @@ "version": "3.723.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.723.0.tgz", "integrity": "sha512-YyN8x4MI/jMb4LpHsLf+VYqvbColMK8aZeGWVk2fTFsmt8lpTYGaGC1yybSwGX42mZ4W8ucu8SAYSbUraJZEjA==", + "peer": true, "requires": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", @@ -11307,9 +11320,9 @@ } }, "@pretendonetwork/grpc": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.3.tgz", - "integrity": "sha512-65QH9XneS1xQDTmZ/0XhHEXCPd4Sx3fMt9RDlCc0m7nl2LhaupkAW//GJhJ4QTGbN3gggv5JKME0ZfFqO6Pqbg==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.4.tgz", + "integrity": "sha512-Ajtk8nHR2Auaks6SKUkm0cDVivjuIhitz6xaaxmVfb0hyVdZZO4EfjEtQdsRLmfbA+SWDxdszBPRD+LBsIdjxQ==", "requires": { "@bufbuild/protobuf": "^2.2.2", "nice-grpc-common": "^2.0.2", @@ -12277,6 +12290,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.1.tgz", "integrity": "sha512-pUXGCuHnnKw6PyYq93lLRiZm3vjuslIy7tus1lIQTYVK9bL8XBgJnCWm8a0KcTtHC84Yya1Q6rtll+duSMj0dg==", "dev": true, + "peer": true, "requires": { "@typescript-eslint/scope-manager": "8.39.1", "@typescript-eslint/types": "8.39.1", @@ -12617,7 +12631,8 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true + "dev": true, + "peer": true }, "acorn-jsx": { "version": "5.3.2", @@ -13341,6 +13356,7 @@ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "dev": true, + "peer": true, "requires": { "@esbuild/aix-ppc64": "0.25.9", "@esbuild/android-arm": "0.25.9", @@ -13391,6 +13407,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.33.0.tgz", "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "dev": true, + "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -13572,6 +13589,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, + "peer": true, "requires": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -14905,6 +14923,7 @@ "version": "8.18.1", "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.18.1.tgz", "integrity": "sha512-K0RfrUXXufqNRZZjvAGdyjydB91SnbWxlwFYi5t7zN2DxVWFD3c6puia0/7xfBwZm6RCpYOVdYFlRFpoDWiC+w==", + "peer": true, "requires": { "bson": "^6.10.4", "kareem": "2.6.3", @@ -16086,7 +16105,8 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true + "dev": true, + "peer": true } } }, @@ -16306,7 +16326,8 @@ "typescript": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==" + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "peer": true }, "typescript-eslint": { "version": "8.39.1", @@ -16363,6 +16384,7 @@ "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", "dev": true, + "peer": true, "requires": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", diff --git a/package.json b/package.json index 7e0cf81..dd4bf9a 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "dependencies": { "@aws-sdk/client-s3": "^3.723.0", "@pretendonetwork/boss-crypto": "^1.1.3", - "@pretendonetwork/grpc": "^2.3.3", + "@pretendonetwork/grpc": "^2.3.4", "@typegoose/auto-increment": "^4.13.1", "commander": "^14.0.0", "cron": "^4.3.3", diff --git a/src/cli/tasks.cmd.ts b/src/cli/tasks.cmd.ts index 13b4282..ef41894 100644 --- a/src/cli/tasks.cmd.ts +++ b/src/cli/tasks.cmd.ts @@ -62,7 +62,7 @@ const createCmd = new Command('create') const { task } = await ctx.grpc.registerTask({ bossAppId: appId, id: opts.id, - titleId: BigInt(parseInt(opts.titleId, 16)), + titleId: opts.titleId, description: opts.desc ?? '', country: 'This value isnt used' }); diff --git a/src/services/grpc/boss/v1/list-known-boss-apps.ts b/src/services/grpc/boss/v1/list-known-boss-apps.ts index 81c5446..039d0c9 100644 --- a/src/services/grpc/boss/v1/list-known-boss-apps.ts +++ b/src/services/grpc/boss/v1/list-known-boss-apps.ts @@ -5,399 +5,399 @@ export async function listKnownBOSSApps(): Promise { apps: [ { bossAppId: 'WJDaV6ePVgrS0TRa', - titleId: 0x0005003010016000n, + titleId: '0005003010016000', titleRegion: 'UNK', name: 'Unknown', tasks: ['olvinfo'] }, { bossAppId: 'VFoY6V7u7UUq1EG5', - titleId: 0x0005003010016100n, + titleId: '0005003010016100', titleRegion: 'UNK', name: 'Unknown', tasks: ['olvinfo'] }, { bossAppId: '8MNOVprfNVAJjfCM', - titleId: 0x0005003010016200n, + titleId: '0005003010016200', titleRegion: 'UNK', name: 'Unknown', tasks: ['olvinfo'] }, { bossAppId: 'v1cqzWykBKUg0rHQ', - titleId: 0x000500301001900An, + titleId: '000500301001900A', titleRegion: 'JPN', name: 'Miiverse Post All', tasks: ['solv'] }, { bossAppId: 'bieC9ACJlisFg5xS', - titleId: 0x000500301001910An, + titleId: '000500301001910A', titleRegion: 'USA', name: 'Miiverse Post All', tasks: ['solv'] }, { bossAppId: 'tOaQcoBLtPTgVN3Y', - titleId: 0x000500301001920An, + titleId: '000500301001920A', titleRegion: 'EUR', name: 'Miiverse Post All', tasks: ['solv'] }, { bossAppId: 'HX8a16MMNn6i1z0Y', - titleId: 0x000500301001400An, + titleId: '000500301001400A', titleRegion: 'JPN', name: 'Nintendo eShop', tasks: ['wood1', 'woodBGM'] }, { bossAppId: '07E3nY6lAwlwrQRo', - titleId: 0x000500301001410An, + titleId: '000500301001410A', titleRegion: 'USA', name: 'Nintendo eShop', tasks: ['wood1', 'woodBGM'] }, { bossAppId: '8UsM86l8xgkjFk8z', - titleId: 0x000500301001420An, + titleId: '000500301001420A', titleRegion: 'EUR', name: 'Nintendo eShop', tasks: ['wood1', 'woodBGM'] }, { bossAppId: 'IXmFUqR2qenXfF61', - titleId: 0x0005001010066000n, + titleId: '0005001010066000', titleRegion: 'ALL', name: 'ECO Process', tasks: ['promo1', 'promo2', 'promo3', 'push'] }, { bossAppId: 'BMQAm5iUVtPsJVsU', - titleId: 0x000500101004D000n, + titleId: '000500101004D000', titleRegion: 'JPN', name: 'Notifications', tasks: ['sysmsg1', 'sysmsg2'] }, { bossAppId: 'LRmanFo4Tx3kEGDp', - titleId: 0x000500101004D100n, + titleId: '000500101004D100', titleRegion: 'USA', name: 'Notifications', tasks: ['sysmsg1', 'sysmsg2'] }, { bossAppId: 'TZr27FE8wzKiEaTO', - titleId: 0x000500101004D200n, + titleId: '000500101004D200', titleRegion: 'EUR', name: 'Notifications', tasks: ['sysmsg1', 'sysmsg2'] }, { bossAppId: 'JnIrm9c4E9JBmxBo', - titleId: 0x0005000010185200n, + titleId: '0005000010185200', titleRegion: 'JPN', name: 'NewスーパーマリオブラザーズU 無料お試し版 (New SUPER MARIO BROS. U (Trial))', tasks: ['news'] }, { bossAppId: 'dadlI27Ww8H2d56x', - titleId: 0x0005000010101C00n, + titleId: '0005000010101C00', titleRegion: 'JPN', name: 'NewスーパーマリオブラザーズU (New SUPER MARIO BROS. U)', tasks: ['news'] }, { bossAppId: 'RaPn5saabzliYrpo', - titleId: 0x0005000010101D00n, + titleId: '0005000010101D00', titleRegion: 'USA', name: 'New SUPER MARIO BROS. U', tasks: ['news'] }, { bossAppId: '14VFIK3rY2SP0WRE', - titleId: 0x0005000010101E00n, + titleId: '0005000010101E00', titleRegion: 'EUR', name: 'New SUPER MARIO BROS. U', tasks: ['news'] }, { bossAppId: 'RbEQ44t2AocC4rvu', - titleId: 0x000500001014B700n, + titleId: '000500001014B700', titleRegion: 'USA', name: 'New SUPER MARIO BROS. U + New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: '287gv3WZdxo1QRhl', - titleId: 0x000500001014B800n, + titleId: '000500001014B800', titleRegion: 'EUR', name: 'New SUPER MARIO BROS. U + New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: 'bb6tOEckvgZ50ciH', - titleId: 0x0005000010162B00n, + titleId: '0005000010162B00', titleRegion: 'JPN', name: 'スプラトゥーン (Splatoon)', tasks: ['optdat2', 'schdat2', 'schdata'] }, { bossAppId: 'rjVlM7hUXPxmYQJh', - titleId: 0x0005000010176900n, + titleId: '0005000010176900', titleRegion: 'USA', name: 'Splatoon', tasks: ['optdat2', 'schdat2', 'schdata', 'optdata2', 'schdata2'] }, { bossAppId: 'zvGSM4kOrXpkKnpT', - titleId: 0x0005000010176A00n, + titleId: '0005000010176A00', titleRegion: 'EUR', name: 'Splatoon', tasks: ['optdat2', 'schdat2', 'schdata', 'optdata'] }, { bossAppId: 'm8KJPtmPweiPuETE', - titleId: 0x000500001012F100n, + titleId: '000500001012F100', titleRegion: 'JPN', name: 'Wii Sports Club', tasks: ['sp1_ans'] }, { bossAppId: 'pO72Hi5uqf5yuNd8', - titleId: 0x0005000010144D00n, + titleId: '0005000010144D00', titleRegion: 'USA', name: 'Wii Sports Club', tasks: ['sp1_ans'] }, { bossAppId: '4m8Xme1wKgzwslTJ', - titleId: 0x0005000010144E00n, + titleId: '0005000010144E00', titleRegion: 'EUR', name: 'Wii Sports Club', tasks: ['sp1_ans'] }, { bossAppId: 'ESLqtAhxS8KQU4eu', - titleId: 0x000500001018DB00n, + titleId: '000500001018DB00', titleRegion: 'JPN', name: 'Super Mario Maker (スーパーマリオメーカー)', tasks: ['CHARA'] }, { bossAppId: 'vGwChBW1ExOoHDsm', - titleId: 0x000500001018DC00n, + titleId: '000500001018DC00', titleRegion: 'USA', name: 'Super Mario Maker', tasks: ['CHARA'] }, { bossAppId: 'IeUc4hQsKKe9rJHB', - titleId: 0x000500001018DD00n, + titleId: '000500001018DD00', titleRegion: 'EUA', name: 'Super Mario Maker', tasks: ['CHARA'] }, { bossAppId: '4krJA4Gx3jF5nhQf', - titleId: 0x000500001012BC00n, + titleId: '000500001012BC00', titleRegion: 'JPN', name: 'ピクミン3 (PIKMIN 3)', tasks: ['histgrm'] }, { bossAppId: '9jRZEoWYLc3OG9a8', - titleId: 0x000500001012BD00n, + titleId: '000500001012BD00', titleRegion: 'USA', name: 'PIKMIN 3', tasks: ['histgrm'] }, { bossAppId: 'VWqUTspR5YtjDjxa', - titleId: 0x000500001012BE00n, + titleId: '000500001012BE00', titleRegion: 'EUR', name: 'PIKMIN 3', tasks: ['histgrm'] }, { bossAppId: 'Ge1KtMu8tYlf4AUM', - titleId: 0x0005000010192000n, + titleId: '0005000010192000', titleRegion: 'JPN', name: '太鼓の達人 特盛り! (Taiko no Tatsujin Tokumori!)', tasks: ['notice1'] }, { bossAppId: 'gycVtTzCouZmukZ6', - titleId: 0x0005000010110E00n, + titleId: '0005000010110E00', titleRegion: 'JPN', name: '大乱闘スマッシュブラザーズ for Wii U (Super Smash Bros. for Wii U)', tasks: ['NEWS', 'amiibo'] }, { bossAppId: 'o2Ug1pIp9Uhri6Nh', - titleId: 0x0005000010144F00n, + titleId: '0005000010144F00', titleRegion: 'USA', name: 'Super Smash Bros. for Wii U', tasks: ['amiibo', 'NEWS', 'friend', 'CONQ'] }, { bossAppId: 'n6rAJ1nnfC1Sgcpl', - titleId: 0x0005000010145000n, + titleId: '0005000010145000', titleRegion: 'EUR', name: 'Super Smash Bros. for Wii U', tasks: ['amiibo', 'NEWS', 'friend', 'CONQ'] }, { bossAppId: 'CHUN6T1m7Xk4EBg4', - titleId: 0x00050000101DFF00n, + titleId: '00050000101DFF00', titleRegion: 'JPN', name: 'プチコンBIG (Petitcom BIG)', tasks: ['ptcbnws'] }, { bossAppId: 'zyXdCW9jGdi9rjaz', - titleId: 0x0005000010142200n, + titleId: '0005000010142200', titleRegion: 'JPN', name: 'NewスーパールイージU (New SUPER LUIGI U)', tasks: ['news'] }, { bossAppId: 'jPHLlJr2fJyTzffp', - titleId: 0x0005000010142300n, + titleId: '0005000010142300', titleRegion: 'USA', name: 'New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: 'YsXB6IRGSI56tPxl', - titleId: 0x0005000010142400n, + titleId: '0005000010142400', titleRegion: 'EUR', name: 'New SUPER LUIGI U', tasks: ['news'] }, { bossAppId: 'Lbqp9Sg1i0xUzFFa', - titleId: 0x0005000010113800n, + titleId: '0005000010113800', titleRegion: 'EUR', name: 'Zen Pinball 2', tasks: ['PTS'] }, { bossAppId: 'DwU7n0FidGrLNiOo', - titleId: 0x000500001014D900n, + titleId: '000500001014D900', titleRegion: 'JPN', name: 'ぷよぷよテトリス (PUYOPUYOTETRIS)', tasks: ['boss1', 'boss2', 'boss3'] }, { bossAppId: 'yIUkFmuGVkGP8pDb', - titleId: 0x0005000010132200n, + titleId: '0005000010132200', titleRegion: 'JPN', name: '太鼓の達人 Wii Uば~じょん! (Taiko no Tatsujin Wii U version!)', tasks: ['notice1'] }, { bossAppId: 'v4WRObSzD7VU3dcJ', - titleId: 0x00050000101D3000n, + titleId: '00050000101D3000', titleRegion: 'JPN', name: '太鼓の達人 あつめて★ともだち大作戦! (Taiko no Tatsujin Atsumete★ TomodachiDaisakusen!)', tasks: ['notice1'] }, { bossAppId: '3zDjXIA57bSceyaw', - titleId: 0x00050000101BEC00n, + titleId: '00050000101BEC00', titleRegion: 'USA', name: 'Star Fox Guard', tasks: ['param'] }, { bossAppId: 'NL38jhExI2CQqhWd', - titleId: 0x00050000101CDB00n, + titleId: '00050000101CDB00', titleRegion: 'JPN', name: 'Splatoon Pre-Launch Review', tasks: ['schdata'] }, { bossAppId: 'sE6KwEpQYyg6tdU7', - titleId: 0x00050000101CDC00n, + titleId: '00050000101CDC00', titleRegion: 'USA', name: 'Splatoon Pre-Launch Review', tasks: ['schdata'] }, { bossAppId: 'pTKZ9q5KrCP3gBag', - titleId: 0x00050000101CDD00n, + titleId: '00050000101CDD00', titleRegion: 'EUR', name: 'Splatoon Pre-Launch Review', tasks: ['schdata'] }, { bossAppId: 'CJT88RO008LAnD51', - titleId: 0x0005000010170600n, + titleId: '0005000010170600', titleRegion: 'JPN', name: '仮面ライダー バトライド・ウォーⅡ プレミアムTV&MOVIEサウンドED. (KAMEN RIDER BATTRIDE WAR Ⅱ PREMIUM TV&MOVIE SOUND ED.)', tasks: ['PE_GAK', 'PE_ZNG'] }, { bossAppId: 'FyyMFzEByuQJc6sJ', - titleId: 0x0005000010135200n, + titleId: '0005000010135200', titleRegion: 'USA', name: 'Star Wars Pinball', tasks: ['PTS'] }, { bossAppId: 'A4yyXWKZZUToFtrt', - titleId: 0x0005000010132A00n, + titleId: '0005000010132A00', titleRegion: 'EUR', name: 'Star Wars Pinball', tasks: ['PTS'] }, { bossAppId: 'HauaFQ1sPsnQ6rBj', - titleId: 0x0005000010171F00n, + titleId: '0005000010171F00', titleRegion: 'USA', name: 'Pushmo World', tasks: ['annouce'] }, { bossAppId: 'qDUeFmk0Az71nHyD', - titleId: 0x0005000010110900n, + titleId: '0005000010110900', titleRegion: 'JPN', name: 'NINJA GAIDEN 3: Razor\'s Edge', tasks: ['DLCINFO'] }, { bossAppId: 'yVsSPM2E0DEOxroT', - titleId: 0x0005000010110A00n, + titleId: '0005000010110A00', titleRegion: 'USA', name: 'NINJA GAIDEN 3: Razor\'s Edge', tasks: ['DLCINFO'] }, { bossAppId: 'Xw6OvZkQofQ3O8Bi', - titleId: 0x0005000010110B00n, + titleId: '0005000010110B00', titleRegion: 'EUR', name: 'Ninja Gaiden 3: Razor\'s Edge', tasks: ['DLCINFO'] }, { bossAppId: 'LUQX5swEjBUPQ8nR', - titleId: 0x0005000010110200n, + titleId: '0005000010110200', titleRegion: 'USA', name: 'WARRIORS OROCHI 3 Hyper(NA)', tasks: ['OR2H000'] }, { bossAppId: 'y4pXrgLe0JGao3No', - titleId: 0x0005000010112B00n, + titleId: '0005000010112B00', titleRegion: 'EUR', name: 'WARRIORS OROCHI 3 Hyper(EU)', tasks: ['OR2H000'] }, { bossAppId: 'j01mRJ9sNe00MWPC', - titleId: 0x0005000010170700n, + titleId: '0005000010170700', titleRegion: 'JPN', name: '仮面ライダー バトライド・ウォーⅡ (KAMEN RIDER BATTRIDE WAR Ⅱ)', tasks: ['CHR_GAK', 'CHR_ZNG'] diff --git a/src/services/grpc/boss/v1/list-tasks.ts b/src/services/grpc/boss/v1/list-tasks.ts index a3b93ea..983afc4 100644 --- a/src/services/grpc/boss/v1/list-tasks.ts +++ b/src/services/grpc/boss/v1/list-tasks.ts @@ -12,7 +12,7 @@ export async function listTasks(): Promise { bossAppId: task.boss_app_id, creatorPid: task.creator_pid, status: task.status, - titleId: BigInt(parseInt(task.title_id, 16)), + titleId: task.title_id, description: task.description, createdTimestamp: task.created, updatedTimestamp: task.updated diff --git a/src/services/grpc/boss/v1/register-task.ts b/src/services/grpc/boss/v1/register-task.ts index cade547..884c033 100644 --- a/src/services/grpc/boss/v1/register-task.ts +++ b/src/services/grpc/boss/v1/register-task.ts @@ -15,7 +15,7 @@ export async function registerTask(request: RegisterTaskRequest, context: CallCo const taskID = request.id.trim(); const bossAppID = request.bossAppId.trim(); - const titleID = request.titleId.toString(16).toLowerCase().padStart(16, '0'); + const titleID = request.titleId.trim().toLocaleLowerCase(); const description = request.description.trim(); if (!taskID) { @@ -68,7 +68,7 @@ export async function registerTask(request: RegisterTaskRequest, context: CallCo bossAppId: task.boss_app_id, creatorPid: task.creator_pid, status: task.status, - titleId: BigInt(parseInt(task.title_id, 16)), + titleId: task.title_id, description: task.description, createdTimestamp: task.created, updatedTimestamp: task.updated diff --git a/src/services/grpc/boss/v1/update-task.ts b/src/services/grpc/boss/v1/update-task.ts index 69c7c42..f8c3c41 100644 --- a/src/services/grpc/boss/v1/update-task.ts +++ b/src/services/grpc/boss/v1/update-task.ts @@ -43,7 +43,7 @@ export async function updateTask(request: UpdateTaskRequest, context: CallContex } task.boss_app_id = updateData.bossAppId ? updateData.bossAppId : task.boss_app_id; - task.title_id = updateData.titleId ? updateData.titleId.toString(16).toLowerCase().padStart(16, '0') : task.title_id; + task.title_id = updateData.titleId ? updateData.titleId : task.title_id; task.status = updateData.status ? updateData.status : task.status; task.description = updateData.description ? updateData.description : task.description; task.updated = BigInt(Date.now()); From 767730e42ea9d42febba30cfb83fa1547d82c47d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sun, 9 Nov 2025 01:19:01 +0000 Subject: [PATCH 19/30] fix(grpc/v1): Populate attributes on UploadFile --- src/services/grpc/boss/v1/upload-file.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/services/grpc/boss/v1/upload-file.ts b/src/services/grpc/boss/v1/upload-file.ts index 722baef..a83e6b8 100644 --- a/src/services/grpc/boss/v1/upload-file.ts +++ b/src/services/grpc/boss/v1/upload-file.ts @@ -128,6 +128,12 @@ export async function uploadFile(request: UploadFileRequest, context: CallContex file_key: key, supported_countries: supportedCountries, supported_languages: supportedLanguages, + attributes: { + attribute1: request.attribute1, + attribute2: request.attribute2, + attribute3: request.attribute3, + description: request.password + }, creator_pid: context.user?.pid, name: name, type: type, From dd8ef4dde6ac6604f0c19653e5f719190111feba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sun, 9 Nov 2025 16:52:43 +0000 Subject: [PATCH 20/30] feat(cli): Update to gRPC v2 --- src/cli/apps.cmd.ts | 7 ++++++- src/cli/files.cmd.ts | 12 ++++++----- src/cli/seed.cmd.ts | 12 ++++++----- src/cli/tasks.cmd.ts | 4 ++-- src/cli/utils.ts | 8 ++++---- src/services/grpc/boss/v2/upload-file-ctr.ts | 9 +++++++++ src/services/grpc/boss/v2/upload-file-wup.ts | 21 ++++++++++---------- 7 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/cli/apps.cmd.ts b/src/cli/apps.cmd.ts index 365ce72..0c61434 100644 --- a/src/cli/apps.cmd.ts +++ b/src/cli/apps.cmd.ts @@ -20,6 +20,11 @@ const listCmd = new Command('ls') if (key === 'name') { return prettyTrunc(value, 20); } + + if (key === 'titleId') { + return value.toString(16).toLowerCase().padStart(16, '0'); + } + return value; } }); @@ -41,7 +46,7 @@ const viewCmd = new Command('view') const obj = { appId: app.bossAppId, name: app.name, - titleId: app.titleId, + titleId: app.titleId.toString(16).toLowerCase().padStart(16, '0'), titleRegion: app.titleRegion, knownTasks: app.tasks }; diff --git a/src/cli/files.cmd.ts b/src/cli/files.cmd.ts index 5e51b0f..f0bab7f 100644 --- a/src/cli/files.cmd.ts +++ b/src/cli/files.cmd.ts @@ -4,6 +4,7 @@ import { Readable } from 'node:stream'; import { request } from 'undici'; import { Command } from 'commander'; import { decryptWiiU } from '@pretendonetwork/boss-crypto'; +import { PlatformType } from '@pretendonetwork/grpc/boss/v2/platform_type'; import { commandHandler, getCliContext } from './utils'; import { logOutputList, logOutputObject } from './output'; @@ -14,7 +15,7 @@ const listCmd = new Command('ls') .action(commandHandler<[string, string]>(async (cmd): Promise => { const [appId, taskId] = cmd.args; const ctx = getCliContext(); - const { files } = await ctx.grpc.listFiles({ + const { files } = await ctx.grpc.listFilesWUP({ bossAppId: appId, taskId: taskId }); @@ -38,7 +39,7 @@ const viewCmd = new Command('view') .action(commandHandler<[string, string, bigint]>(async (cmd): Promise => { const [appId, taskId, dataId] = cmd.args; const ctx = getCliContext(); - const { files } = await ctx.grpc.listFiles({ + const { files } = await ctx.grpc.listFilesWUP({ bossAppId: appId, taskId: taskId }); @@ -76,7 +77,7 @@ const downloadCmd = new Command('download') .action(commandHandler<[string, string, bigint]>(async (cmd): Promise => { const [appId, taskId, dataId] = cmd.args; const ctx = getCliContext(); - const { files } = await ctx.grpc.listFiles({ + const { files } = await ctx.grpc.listFilesWUP({ bossAppId: appId, taskId: taskId }); @@ -130,7 +131,7 @@ const createCmd = new Command('create') const opts = cmd.opts<{ name: string; country: string[]; notifyNew: string[]; notifyLed: boolean; lang: string[]; nameAsId?: boolean; type: string; file: string }>(); const fileBuf = await fs.readFile(opts.file); const ctx = getCliContext(); - const { file } = await ctx.grpc.uploadFile({ + const { file } = await ctx.grpc.uploadFileWUP({ taskId: taskId, bossAppId: appId, name: opts.name, @@ -160,7 +161,8 @@ const deleteCmd = new Command('delete') const ctx = getCliContext(); await ctx.grpc.deleteFile({ bossAppId: appId, - dataId: dataId + dataId: dataId, + platformType: PlatformType.PLATFORM_TYPE_WUP }); console.log(`Deleted task file with ID ${dataId}`); })); diff --git a/src/cli/seed.cmd.ts b/src/cli/seed.cmd.ts index 4b1d929..98d5473 100644 --- a/src/cli/seed.cmd.ts +++ b/src/cli/seed.cmd.ts @@ -3,6 +3,7 @@ import fs from 'fs/promises'; import { Command } from 'commander'; import { xml2js } from 'xml-js'; import { decryptWiiU } from '@pretendonetwork/boss-crypto'; +import { PlatformType } from '@pretendonetwork/grpc/boss/v2/platform_type'; import { getCliContext } from './utils'; import { seedFolder } from './root'; import type { CliContext } from './utils'; @@ -31,7 +32,7 @@ export async function uploadFileIfChanged(ops: UploadFileOptions): Promise fileContents = decryptedContents.content; } - const allExistingTaskFiles = await ops.ctx.grpc.listFiles({ + const allExistingTaskFiles = await ops.ctx.grpc.listFilesWUP({ bossAppId: ops.bossAppId, taskId: ops.taskId }); @@ -40,11 +41,12 @@ export async function uploadFileIfChanged(ops: UploadFileOptions): Promise console.warn(`${ops.dataId}: File already uploaded, reuploading`); await ops.ctx.grpc.deleteFile({ bossAppId: ops.bossAppId, - dataId: BigInt(ops.dataId) + dataId: BigInt(ops.dataId), + platformType: PlatformType.PLATFORM_TYPE_WUP }); } - await ops.ctx.grpc.uploadFile({ + await ops.ctx.grpc.uploadFileWUP({ bossAppId: ops.bossAppId, taskId: ops.taskId, name: ops.fileXml.Filename._text, @@ -78,7 +80,7 @@ export async function processTasksheet(ctx: CliContext, taskFiles: string[], fil await ctx.grpc.registerTask({ bossAppId: bossAppId, id: taskName, - titleId: xmlContents.TaskSheet.TitleId._text, + titleId: BigInt(parseInt(xmlContents.TaskSheet.TitleId._text, 16)), country: 'This value isnt used' }); console.log(`${filename}: Created task`); @@ -87,7 +89,7 @@ export async function processTasksheet(ctx: CliContext, taskFiles: string[], fil bossAppId: bossAppId, id: taskName, updateData: { - titleId: xmlContents.TaskSheet.TitleId._text, + titleId: BigInt(parseInt(xmlContents.TaskSheet.TitleId._text, 16)), status: xmlContents.TaskSheet.ServiceStatus._text } }); diff --git a/src/cli/tasks.cmd.ts b/src/cli/tasks.cmd.ts index ef41894..5b7ac21 100644 --- a/src/cli/tasks.cmd.ts +++ b/src/cli/tasks.cmd.ts @@ -38,7 +38,7 @@ const viewCmd = new Command('view') taskId: task.id, inGameId: task.inGameId, description: task.description, - titleId: task.titleId, + titleId: task.titleId.toString(16).toLowerCase().padStart(16, '0'), bossAppId: task.bossAppId, creatorPid: task.creatorPid, status: task.status, @@ -62,7 +62,7 @@ const createCmd = new Command('create') const { task } = await ctx.grpc.registerTask({ bossAppId: appId, id: opts.id, - titleId: opts.titleId, + titleId: BigInt(parseInt(opts.titleId, 16)), description: opts.desc ?? '', country: 'This value isnt used' }); diff --git a/src/cli/utils.ts b/src/cli/utils.ts index 6d7ab63..06ea1cb 100644 --- a/src/cli/utils.ts +++ b/src/cli/utils.ts @@ -1,7 +1,7 @@ -import { BOSSDefinition } from '@pretendonetwork/grpc/boss/boss_service'; +import { BossServiceDefinition } from '@pretendonetwork/grpc/boss/v2/boss_service'; import { createChannel, createClient, Metadata } from 'nice-grpc'; import dotenv from 'dotenv'; -import type { BOSSClient } from '@pretendonetwork/grpc/boss/boss_service'; +import type { BossServiceClient } from '@pretendonetwork/grpc/boss/v2/boss_service'; import type { Command } from 'commander'; import type { FormatOption } from './output'; @@ -12,7 +12,7 @@ export type NpdiUrl = { }; export type CliContext = { - grpc: BOSSClient; + grpc: BossServiceClient; getNpdiUrl: () => NpdiUrl; getWiiUKeys: () => WiiUKeys; }; @@ -30,7 +30,7 @@ export function getCliContext(): CliContext { } const channel = createChannel(grpcHost); - const client: BOSSClient = createClient(BOSSDefinition, channel, { + const client: BossServiceClient = createClient(BossServiceDefinition, channel, { '*': { metadata: new Metadata({ 'X-API-Key': grpcKey diff --git a/src/services/grpc/boss/v2/upload-file-ctr.ts b/src/services/grpc/boss/v2/upload-file-ctr.ts index e9090ad..b787ce6 100644 --- a/src/services/grpc/boss/v2/upload-file-ctr.ts +++ b/src/services/grpc/boss/v2/upload-file-ctr.ts @@ -61,6 +61,15 @@ export async function uploadFileCTR(request: UploadFileCTRRequest, context: Call throw new ServerError(Status.INVALID_ARGUMENT, 'Cannot upload empty file'); } + if (!request.attributes) { + request.attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + description: '' + }; + } + const session = await databaseConnection().startSession(); await session.startTransaction(); diff --git a/src/services/grpc/boss/v2/upload-file-wup.ts b/src/services/grpc/boss/v2/upload-file-wup.ts index 47cde27..6eaf577 100644 --- a/src/services/grpc/boss/v2/upload-file-wup.ts +++ b/src/services/grpc/boss/v2/upload-file-wup.ts @@ -78,6 +78,15 @@ export async function uploadFileWUP(request: UploadFileWUPRequest, context: Call throw new ServerError(Status.INVALID_ARGUMENT, 'Cannot upload empty file'); } + if (!request.attributes) { + request.attributes = { + attribute1: '', + attribute2: '', + attribute3: '', + description: '' + }; + } + let encryptedData: Buffer; try { @@ -129,6 +138,7 @@ export async function uploadFileWUP(request: UploadFileWUPRequest, context: Call file_key: key, supported_countries: supportedCountries, supported_languages: supportedLanguages, + attributes: request.attributes, creator_pid: context.user?.pid, name: name, type: type, @@ -142,17 +152,6 @@ export async function uploadFileWUP(request: UploadFileWUPRequest, context: Call updated: Date.now() }); - if (request.attributes) { - file.attributes = { - attribute1: request.attributes.attribute1, - attribute2: request.attributes.attribute2, - attribute3: request.attributes.attribute3, - description: request.attributes.description - }; - - await file.save(); - } - if (nameEqualsDataID) { file.name = file.data_id.toString(16).padStart(8, '0'); await file.save(); From 90f02ac286ffaab9c50462087cdab68d41925559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sun, 9 Nov 2025 20:33:48 +0000 Subject: [PATCH 21/30] feat(cli): Implement 3DS support Also add attributes support for Wii U. --- src/cli/cli.ts | 4 +- src/cli/files-3ds.cmd.ts | 193 +++++++++++++++++++++++++++++++++++++++ src/cli/files.cmd.ts | 14 ++- src/cli/utils.ts | 26 ++++++ 4 files changed, 234 insertions(+), 3 deletions(-) create mode 100644 src/cli/files-3ds.cmd.ts diff --git a/src/cli/cli.ts b/src/cli/cli.ts index b2a3ec9..c950529 100644 --- a/src/cli/cli.ts +++ b/src/cli/cli.ts @@ -1,6 +1,7 @@ import { program as baseProgram } from 'commander'; import { taskCmd } from './tasks.cmd'; import { fileCmd } from './files.cmd'; +import { file3DSCmd } from './files-3ds.cmd'; import { appCmd } from './apps.cmd'; import { importCmd } from './import.cmd'; @@ -11,7 +12,8 @@ const program = baseProgram .addCommand(appCmd) .addCommand(taskCmd) .addCommand(importCmd) - .addCommand(fileCmd); + .addCommand(fileCmd) + .addCommand(file3DSCmd); program.parseAsync(process.argv) .catch(console.error) diff --git a/src/cli/files-3ds.cmd.ts b/src/cli/files-3ds.cmd.ts new file mode 100644 index 0000000..a5d42e7 --- /dev/null +++ b/src/cli/files-3ds.cmd.ts @@ -0,0 +1,193 @@ +import fs from 'node:fs/promises'; +import { pipeline } from 'node:stream/promises'; +import { Readable } from 'node:stream'; +import { request } from 'undici'; +import { Command } from 'commander'; +import { decrypt3DS } from '@pretendonetwork/boss-crypto'; +import { PlatformType } from '@pretendonetwork/grpc/boss/v2/platform_type'; +import { commandHandler, getCliContext } from './utils'; +import { logOutputList, logOutputObject } from './output'; + +const listCmd = new Command('ls') + .description('List all task files in BOSS') + .argument('', 'BOSS app to search in') + .argument('', 'Task to search in') + .action(commandHandler<[string, string]>(async (cmd): Promise => { + const [appId, taskId] = cmd.args; + const ctx = getCliContext(); + const { files } = await ctx.grpc.listFilesCTR({ + bossAppId: appId, + taskId: taskId + }); + logOutputList(files, { + format: cmd.format, + onlyIncludeKeys: ['dataId', 'name', 'size'], + mapping: { + dataId: 'Data ID', + name: 'Name', + size: 'Size (bytes)' + } + }); + })); + +const viewCmd = new Command('view') + .description('Look up a specific task file') + .argument('', 'BOSS app that contains the task') + .argument('', 'Task that contains the task file') + .argument('', 'Task file ID to lookup', BigInt) + .action(commandHandler<[string, string, bigint]>(async (cmd): Promise => { + const [appId, taskId, dataId] = cmd.args; + const ctx = getCliContext(); + const { files } = await ctx.grpc.listFilesCTR({ + bossAppId: appId, + taskId: taskId + }); + const file = files.find(v => v.dataId === dataId); + if (!file) { + console.log(`Could not find task file with data ID ${dataId} in task ${taskId}`); + return; + } + logOutputObject({ + dataId: file.dataId, + name: file.name, + size: file.size, + hash: file.hash, + supportedCountries: file.supportedCountries, + supportedLanguages: file.supportedLanguages, + attributes: file.attributes, + creatorPid: file.creatorPid, + payloadContents: file.payloadContents, + flags: file.flags, + createdAt: new Date(Number(file.createdTimestamp)), + updatedAt: new Date(Number(file.updatedTimestamp)) + }, { + format: cmd.format + }); + })); + +const downloadCmd = new Command('download') + .description('Download a task file') + .argument('', 'BOSS app that contains the task') + .argument('', 'Task that contains the task file') + .argument('', 'Task file ID to lookup', BigInt) + .option('-d, --decrypt', 'Decrypt the file before return') + .action(commandHandler<[string, string, bigint]>(async (cmd): Promise => { + const [appId, taskId, dataId] = cmd.args; + const ctx = getCliContext(); + const { files } = await ctx.grpc.listFilesCTR({ + bossAppId: appId, + taskId: taskId + }); + const file = files.find(v => v.dataId === dataId); + if (!file) { + console.error(`Could not find task file with data ID ${dataId} in task ${taskId}`); + process.exit(1); + } + + const npdl = ctx.getNpdlUrl(); + const country = file.supportedCountries.length > 0 ? '/' + file.supportedCountries[0] : ''; + const language = file.supportedLanguages.length > 0 ? '/' + file.supportedCountries[0] : ''; + const { body, statusCode } = await request(`${npdl.url}/p01/nsa/${file.bossAppId}/${file.taskId}${country}${language}/${file.name}`, { + headers: { + Host: npdl.host + } + }); + if (statusCode > 299) { + console.error(`Failed to download: invalid status code (${statusCode})`); + process.exit(1); + } + + const chunks: Buffer[] = []; + for await (const chunk of body) { + chunks.push(Buffer.from(chunk)); + } + + let buffer: Buffer = Buffer.concat(chunks); + + if (cmd.opts().decrypt) { + const keys = ctx.get3DSKeys(); + const decrypted = decrypt3DS(buffer, keys.aesKey); + // TODO - Handle multiple payloads + buffer = decrypted.payload_contents[0].content; + } + + await pipeline(Readable.from(buffer), process.stdout); + })); + +const createCmd = new Command('create') + .description('Create a new task file') + .argument('', 'BOSS app to store the task file in') + .argument('', 'Task to store the task file in') + .requiredOption('--name ', 'Name of the task file') + .requiredOption('--title-id ', 'Target title ID of the payload') + .requiredOption('--content-datatype ', 'Content datatype of the payload') + .requiredOption('--ns-data-id ', 'NS Data ID of the payload') + .requiredOption('--version ', 'Version of the payload') + .requiredOption('--file ', 'Path of the file to upload') + .option('--country ', 'Countries for this task file') + .option('--lang ', 'Languages for this task file') + .option('--attribute1 [attribute1]', 'Attribute 1 for this task file') + .option('--attribute2 [attribute2]', 'Attribute 2 for this task file') + .option('--attribute3 [attribute3]', 'Attribute 3 for this task file') + .option('--desc [desc]', 'Description for this task file') + .option('-m, --mark-arrived-privileged', 'Only notify of new content to privileged titles') + .action(commandHandler<[string, string]>(async (cmd): Promise => { + const [appId, taskId] = cmd.args; + // TODO - Handle multiple payload contents + const opts = cmd.opts<{ name: string; titleId: string; contentDatatype: string; nsDataId: string; version: string; file: string; country: string[]; lang: string[]; attribute1?: string; attribute2?: string; attribute3?: string; desc?: string; markArrivedPrivileged: boolean }>(); + const fileBuf = await fs.readFile(opts.file); + const ctx = getCliContext(); + const { file } = await ctx.grpc.uploadFileCTR({ + taskId: taskId, + bossAppId: appId, + supportedCountries: opts.country, + supportedLanguages: opts.lang, + attributes: { + attribute1: opts.attribute1, + attribute2: opts.attribute2, + attribute3: opts.attribute3, + description: opts.desc + }, + name: opts.name, + payloadContents: [{ + titleId: BigInt(parseInt(opts.titleId, 16)), + contentDatatype: Number(opts.contentDatatype), + nsDataId: Number(opts.nsDataId), + version: Number(opts.version), + content: fileBuf + }], + flags: { + markArrivedPrivileged: opts.markArrivedPrivileged + } + }); + if (!file) { + console.log(`Failed to create file!`); + return; + } + console.log(`Created file with ID ${file.dataId}`); + })); + +const deleteCmd = new Command('delete') + .description('Delete a task file') + .argument('', 'BOSS app that contains the task') + .argument('', 'Task that contains the task file') + .argument('', 'Task file ID to delete', BigInt) + .action(commandHandler<[string, string, bigint]>(async (cmd): Promise => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars -- I want to use destructuring + const [appId, _taskId, dataId] = cmd.args; + const ctx = getCliContext(); + await ctx.grpc.deleteFile({ + bossAppId: appId, + dataId: dataId, + platformType: PlatformType.PLATFORM_TYPE_CTR + }); + console.log(`Deleted task file with ID ${dataId}`); + })); + +export const file3DSCmd = new Command('file-3ds') + .description('Manage all the 3DS task files in BOSS') + .addCommand(listCmd) + .addCommand(createCmd) + .addCommand(deleteCmd) + .addCommand(downloadCmd) + .addCommand(viewCmd); diff --git a/src/cli/files.cmd.ts b/src/cli/files.cmd.ts index f0bab7f..99469ac 100644 --- a/src/cli/files.cmd.ts +++ b/src/cli/files.cmd.ts @@ -123,12 +123,16 @@ const createCmd = new Command('create') .requiredOption('--file ', 'Path of the file to upload') .option('--country ', 'Countries for this task file') .option('--lang ', 'Languages for this task file') + .option('--attribute1 [attribute1]', 'Attribute 1 for this task file') + .option('--attribute2 [attribute2]', 'Attribute 2 for this task file') + .option('--attribute3 [attribute3]', 'Attribute 3 for this task file') + .option('--desc [desc]', 'Description for this task file') .option('--name-as-id', 'Force the name as the data ID') .option('--notify-new ', 'Add entry to NotifyNew') .option('--notify-led', 'Enable NotifyLED') .action(commandHandler<[string, string]>(async (cmd): Promise => { const [appId, taskId] = cmd.args; - const opts = cmd.opts<{ name: string; country: string[]; notifyNew: string[]; notifyLed: boolean; lang: string[]; nameAsId?: boolean; type: string; file: string }>(); + const opts = cmd.opts<{ name: string; country: string[]; notifyNew: string[]; notifyLed: boolean; lang: string[]; attribute1?: string; attribute2?: string; attribute3?: string; desc?: string; nameAsId?: boolean; type: string; file: string }>(); const fileBuf = await fs.readFile(opts.file); const ctx = getCliContext(); const { file } = await ctx.grpc.uploadFileWUP({ @@ -137,6 +141,12 @@ const createCmd = new Command('create') name: opts.name, supportedCountries: opts.country, supportedLanguages: opts.lang, + attributes: { + attribute1: opts.attribute1, + attribute2: opts.attribute2, + attribute3: opts.attribute3, + description: opts.desc + }, type: opts.type, nameEqualsDataId: opts.nameAsId ?? false, data: fileBuf, @@ -168,7 +178,7 @@ const deleteCmd = new Command('delete') })); export const fileCmd = new Command('file') - .description('Manage all the task files in BOSS') + .description('Manage all the Wii U task files in BOSS') .addCommand(listCmd) .addCommand(createCmd) .addCommand(deleteCmd) diff --git a/src/cli/utils.ts b/src/cli/utils.ts index 06ea1cb..5091855 100644 --- a/src/cli/utils.ts +++ b/src/cli/utils.ts @@ -6,15 +6,22 @@ import type { Command } from 'commander'; import type { FormatOption } from './output'; export type WiiUKeys = { aesKey: string; hmacKey: string }; +export type CtrKeys = { aesKey: string }; export type NpdiUrl = { host: string; url: string; }; +export type NpdlUrl = { + host: string; + url: string; +}; export type CliContext = { grpc: BossServiceClient; getNpdiUrl: () => NpdiUrl; + getNpdlUrl: () => NpdlUrl; getWiiUKeys: () => WiiUKeys; + get3DSKeys: () => CtrKeys; }; export function getCliContext(): CliContext { @@ -49,6 +56,15 @@ export function getCliContext(): CliContext { host: npdiHost }; }, + getNpdlUrl(): NpdiUrl { + const npdlUrl = process.env.PN_BOSS_CLI_NPDL_URL ?? 'https://npdl.cdn.pretendo.cc'; + const npdlHost = process.env.PN_BOSS_CLI_NPDL_HOST ?? new URL(npdlUrl).host; + + return { + url: npdlUrl, + host: npdlHost + }; + }, getWiiUKeys(): WiiUKeys { const aesKey = process.env.PN_BOSS_CLI_WIIU_AES_KEY ?? ''; const hmacKey = process.env.PN_BOSS_CLI_WIIU_HMAC_KEY ?? ''; @@ -63,6 +79,16 @@ export function getCliContext(): CliContext { aesKey, hmacKey }; + }, + get3DSKeys(): CtrKeys { + const aesKey = process.env.PN_BOSS_CLI_3DS_AES_KEY ?? ''; + + if (!aesKey) { + throw new Error('Missing env variable PN_BOSS_CLI_3DS_AES_KEY - needed for decryption'); + } + return { + aesKey + }; } }; } From f3f895ebd67243ca645e1ece3928f05cc7576164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sun, 9 Nov 2025 21:20:39 +0000 Subject: [PATCH 22/30] chore: Update pretendonetwork/grpc to 2.3.5 --- package-lock.json | 14 +++++++------- package.json | 2 +- src/cli/files-3ds.cmd.ts | 12 ++++++++++-- src/cli/files.cmd.ts | 12 ++++++++++-- src/cli/seed.cmd.ts | 11 +---------- src/cli/tasks.cmd.ts | 9 ++++++--- src/database.ts | 20 ++++++++++++++------ src/services/grpc/boss/v1/register-task.ts | 2 +- src/services/grpc/boss/v2/list-files-ctr.ts | 5 +++-- src/services/grpc/boss/v2/list-files-wup.ts | 3 ++- src/services/grpc/boss/v2/register-task.ts | 7 ++++++- src/services/grpc/boss/v2/update-task.ts | 2 +- src/types/mongoose/task.ts | 2 +- 13 files changed, 63 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index 233d95f..e8d3d58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@aws-sdk/client-s3": "^3.723.0", "@pretendonetwork/boss-crypto": "^1.1.3", - "@pretendonetwork/grpc": "^2.3.4", + "@pretendonetwork/grpc": "^2.3.5", "@typegoose/auto-increment": "^4.13.1", "commander": "^14.0.0", "cron": "^4.3.3", @@ -2072,9 +2072,9 @@ } }, "node_modules/@pretendonetwork/grpc": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.4.tgz", - "integrity": "sha512-Ajtk8nHR2Auaks6SKUkm0cDVivjuIhitz6xaaxmVfb0hyVdZZO4EfjEtQdsRLmfbA+SWDxdszBPRD+LBsIdjxQ==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.5.tgz", + "integrity": "sha512-FU0uvhZr8gFgiIi+gBtD6+5or34bHcd9X+ZVpRdc7IiupW5V+uxiXigJKX4Vd46QC412y+BEGofCjM1bBlTJhg==", "license": "AGPL-3.0-only", "dependencies": { "@bufbuild/protobuf": "^2.2.2", @@ -11320,9 +11320,9 @@ } }, "@pretendonetwork/grpc": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.4.tgz", - "integrity": "sha512-Ajtk8nHR2Auaks6SKUkm0cDVivjuIhitz6xaaxmVfb0hyVdZZO4EfjEtQdsRLmfbA+SWDxdszBPRD+LBsIdjxQ==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@pretendonetwork/grpc/-/grpc-2.3.5.tgz", + "integrity": "sha512-FU0uvhZr8gFgiIi+gBtD6+5or34bHcd9X+ZVpRdc7IiupW5V+uxiXigJKX4Vd46QC412y+BEGofCjM1bBlTJhg==", "requires": { "@bufbuild/protobuf": "^2.2.2", "nice-grpc-common": "^2.0.2", diff --git a/package.json b/package.json index dd4bf9a..e538f21 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "dependencies": { "@aws-sdk/client-s3": "^3.723.0", "@pretendonetwork/boss-crypto": "^1.1.3", - "@pretendonetwork/grpc": "^2.3.4", + "@pretendonetwork/grpc": "^2.3.5", "@typegoose/auto-increment": "^4.13.1", "commander": "^14.0.0", "cron": "^4.3.3", diff --git a/src/cli/files-3ds.cmd.ts b/src/cli/files-3ds.cmd.ts index a5d42e7..b634cf1 100644 --- a/src/cli/files-3ds.cmd.ts +++ b/src/cli/files-3ds.cmd.ts @@ -12,12 +12,19 @@ const listCmd = new Command('ls') .description('List all task files in BOSS') .argument('', 'BOSS app to search in') .argument('', 'Task to search in') + .option('-c, --country [country]', 'Country to filter with') + .option('-l, --language [language]', 'Language to filter with') + .option('-a, --any', 'Shows any file regardless of country and language requirements') .action(commandHandler<[string, string]>(async (cmd): Promise => { const [appId, taskId] = cmd.args; + const opts = cmd.opts<{ country?: string; language?: string; any: boolean }>(); const ctx = getCliContext(); const { files } = await ctx.grpc.listFilesCTR({ bossAppId: appId, - taskId: taskId + taskId: taskId, + country: opts.country, + language: opts.language, + any: opts.any }); logOutputList(files, { format: cmd.format, @@ -40,7 +47,8 @@ const viewCmd = new Command('view') const ctx = getCliContext(); const { files } = await ctx.grpc.listFilesCTR({ bossAppId: appId, - taskId: taskId + taskId: taskId, + any: true }); const file = files.find(v => v.dataId === dataId); if (!file) { diff --git a/src/cli/files.cmd.ts b/src/cli/files.cmd.ts index 99469ac..9fd1553 100644 --- a/src/cli/files.cmd.ts +++ b/src/cli/files.cmd.ts @@ -12,12 +12,19 @@ const listCmd = new Command('ls') .description('List all task files in BOSS') .argument('', 'BOSS app to search in') .argument('', 'Task to search in') + .option('-c, --country [country]', 'Country to filter with') + .option('-l, --language [language]', 'Language to filter with') + .option('-a, --any', 'Shows any file regardless of country and language requirements') .action(commandHandler<[string, string]>(async (cmd): Promise => { const [appId, taskId] = cmd.args; + const opts = cmd.opts<{ country?: string; language?: string; any: boolean }>(); const ctx = getCliContext(); const { files } = await ctx.grpc.listFilesWUP({ bossAppId: appId, - taskId: taskId + taskId: taskId, + country: opts.country, + language: opts.language, + any: opts.any }); logOutputList(files, { format: cmd.format, @@ -41,7 +48,8 @@ const viewCmd = new Command('view') const ctx = getCliContext(); const { files } = await ctx.grpc.listFilesWUP({ bossAppId: appId, - taskId: taskId + taskId: taskId, + any: true }); const file = files.find(v => v.dataId === dataId); if (!file) { diff --git a/src/cli/seed.cmd.ts b/src/cli/seed.cmd.ts index 98d5473..967e0d9 100644 --- a/src/cli/seed.cmd.ts +++ b/src/cli/seed.cmd.ts @@ -81,19 +81,10 @@ export async function processTasksheet(ctx: CliContext, taskFiles: string[], fil bossAppId: bossAppId, id: taskName, titleId: BigInt(parseInt(xmlContents.TaskSheet.TitleId._text, 16)), - country: 'This value isnt used' + status: xmlContents.TaskSheet.ServiceStatus._text }); console.log(`${filename}: Created task`); } - await ctx.grpc.updateTask({ - bossAppId: bossAppId, - id: taskName, - updateData: { - titleId: BigInt(parseInt(xmlContents.TaskSheet.TitleId._text, 16)), - status: xmlContents.TaskSheet.ServiceStatus._text - } - }); - console.log(`${filename}: Updated title ID and status`); let xmlFiles = xmlContents.TaskSheet.Files?.File ?? []; if (!Array.isArray(xmlFiles)) { diff --git a/src/cli/tasks.cmd.ts b/src/cli/tasks.cmd.ts index 5b7ac21..da4c115 100644 --- a/src/cli/tasks.cmd.ts +++ b/src/cli/tasks.cmd.ts @@ -54,17 +54,20 @@ const createCmd = new Command('create') .argument('', 'BOSS app to store the task in') .requiredOption('--id ', 'Id of the task') .requiredOption('--title-id ', 'Title ID for the task') + .option('--status [status]', 'Initial status of the task') + .option('--interval [interval]', 'Interval of the task') .option('--desc [desc]', 'Description of the task') .action(commandHandler<[string]>(async (cmd): Promise => { const [appId] = cmd.args; const ctx = getCliContext(); - const opts = cmd.opts<{ id: string; titleId: string; desc?: string }>(); + const opts = cmd.opts<{ id: string; titleId: string; status?: string; interval?: string; desc?: string }>(); const { task } = await ctx.grpc.registerTask({ bossAppId: appId, id: opts.id, titleId: BigInt(parseInt(opts.titleId, 16)), - description: opts.desc ?? '', - country: 'This value isnt used' + status: opts.status ?? 'open', + interval: Number(opts.interval ?? 0), + description: opts.desc ?? '' }); if (!task) { console.log(`Failed to create task!`); diff --git a/src/database.ts b/src/database.ts index 9cf306e..fa8762e 100644 --- a/src/database.ts +++ b/src/database.ts @@ -54,7 +54,7 @@ export function getTask(bossAppID: string, taskID: string): Promise { +export function getCTRTaskFiles(allowDeleted: boolean, bossAppID: string, taskID: string, country?: string, language?: string, any: boolean = false): Promise { verifyConnected(); const filter: mongoose.FilterQuery = { @@ -74,7 +74,7 @@ export function getCTRTaskFiles(allowDeleted: boolean, bossAppID: string, taskID { supported_countries: country } ] }); - } else { + } else if (!any) { filter.$and?.push({ supported_countries: { $eq: [] } }); @@ -87,16 +87,20 @@ export function getCTRTaskFiles(allowDeleted: boolean, bossAppID: string, taskID { supported_languages: language } ] }); - } else { + } else if (!any) { filter.$and?.push({ supported_languages: { $eq: [] } }); } + if (filter.$and?.length === 0) { + delete filter.$and; + } + return FileCTR.find(filter); } -export function getWUPTaskFiles(allowDeleted: boolean, bossAppID: string, taskID: string, country?: string, language?: string): Promise { +export function getWUPTaskFiles(allowDeleted: boolean, bossAppID: string, taskID: string, country?: string, language?: string, any: boolean = false): Promise { verifyConnected(); const filter: mongoose.FilterQuery = { @@ -116,7 +120,7 @@ export function getWUPTaskFiles(allowDeleted: boolean, bossAppID: string, taskID { supported_countries: country } ] }); - } else { + } else if (!any) { filter.$and?.push({ supported_countries: { $eq: [] } }); @@ -129,12 +133,16 @@ export function getWUPTaskFiles(allowDeleted: boolean, bossAppID: string, taskID { supported_languages: language } ] }); - } else { + } else if (!any) { filter.$and?.push({ supported_languages: { $eq: [] } }); } + if (filter.$and?.length === 0) { + delete filter.$and; + } + return FileWUP.find(filter); } diff --git a/src/services/grpc/boss/v1/register-task.ts b/src/services/grpc/boss/v1/register-task.ts index 884c033..aabc29b 100644 --- a/src/services/grpc/boss/v1/register-task.ts +++ b/src/services/grpc/boss/v1/register-task.ts @@ -53,7 +53,7 @@ export async function registerTask(request: RegisterTaskRequest, context: CallCo in_game_id: taskID, boss_app_id: bossAppID, creator_pid: context.user?.pid, - status: 'open', // TODO - Make this configurable + status: 'open', title_id: titleID, description: description, created: Date.now(), diff --git a/src/services/grpc/boss/v2/list-files-ctr.ts b/src/services/grpc/boss/v2/list-files-ctr.ts index 00328ff..dd03ee7 100644 --- a/src/services/grpc/boss/v2/list-files-ctr.ts +++ b/src/services/grpc/boss/v2/list-files-ctr.ts @@ -10,6 +10,7 @@ export async function listFilesCTR(request: ListFilesCTRRequest): Promise ({ deleted: file.deleted, - dataId: file.serial_number, // TODO - Is this okay? + dataId: file.serial_number, taskId: file.task_id, bossAppId: file.boss_app_id, supportedCountries: file.supported_countries, diff --git a/src/services/grpc/boss/v2/list-files-wup.ts b/src/services/grpc/boss/v2/list-files-wup.ts index 5b8522a..a537561 100644 --- a/src/services/grpc/boss/v2/list-files-wup.ts +++ b/src/services/grpc/boss/v2/list-files-wup.ts @@ -10,6 +10,7 @@ export async function listFilesWUP(request: ListFilesWUPRequest): Promise ({ diff --git a/src/services/grpc/boss/v2/register-task.ts b/src/services/grpc/boss/v2/register-task.ts index e39df3e..30c67f4 100644 --- a/src/services/grpc/boss/v2/register-task.ts +++ b/src/services/grpc/boss/v2/register-task.ts @@ -16,6 +16,7 @@ export async function registerTask(request: RegisterTaskRequest, context: CallCo const taskID = request.id.trim(); const bossAppID = request.bossAppId.trim(); const titleID = request.titleId.toString(16).toLowerCase().padStart(16, '0'); + const status = request.status; const description = request.description.trim(); if (!taskID) { @@ -38,6 +39,10 @@ export async function registerTask(request: RegisterTaskRequest, context: CallCo throw new ServerError(Status.ALREADY_EXISTS, `Task ${taskID} already exists for BOSS app ${bossAppID}`); } + if (status !== 'open' && status !== 'close') { + throw new ServerError(Status.INVALID_ARGUMENT, `Status ${status} is invalid`); + } + // * BOSS tasks have 2 IDs // * - 1: The ID which is registered in-game // * - 2: The ID which is registered on the server @@ -53,7 +58,7 @@ export async function registerTask(request: RegisterTaskRequest, context: CallCo in_game_id: taskID, boss_app_id: bossAppID, creator_pid: context.user?.pid, - status: 'open', // TODO - Make this configurable + status, title_id: titleID, description: description, created: Date.now(), diff --git a/src/services/grpc/boss/v2/update-task.ts b/src/services/grpc/boss/v2/update-task.ts index 34cd712..be2413b 100644 --- a/src/services/grpc/boss/v2/update-task.ts +++ b/src/services/grpc/boss/v2/update-task.ts @@ -33,7 +33,7 @@ export async function updateTask(request: UpdateTaskRequest, context: CallContex throw new ServerError(Status.INVALID_ARGUMENT, `Task ${taskID} not found for BOSS app ${bossAppID}`); } - if (updateData.status !== 'open') { + if (updateData.status !== 'open' && updateData.status !== 'close') { throw new ServerError(Status.INVALID_ARGUMENT, `Status ${updateData.status} is invalid`); } diff --git a/src/types/mongoose/task.ts b/src/types/mongoose/task.ts index 9204472..d50e362 100644 --- a/src/types/mongoose/task.ts +++ b/src/types/mongoose/task.ts @@ -6,7 +6,7 @@ export interface ITask { in_game_id: string; boss_app_id: string; creator_pid: number; - status: 'open'; // TODO - Make this a union. What else is there? + status: 'open' | 'close'; // TODO - Make this a union. What else is there? title_id: string; description: string; created: bigint; From 35f23f710ce36a51814485004cadad3897daf406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Sun, 9 Nov 2025 21:42:12 +0000 Subject: [PATCH 23/30] feat: Store task interval --- src/models/task.ts | 1 + src/services/grpc/boss/v2/register-task.ts | 4 +++- src/services/grpc/boss/v2/update-task.ts | 1 + src/types/mongoose/task.ts | 3 ++- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/models/task.ts b/src/models/task.ts index cb374b1..21f4c72 100644 --- a/src/models/task.ts +++ b/src/models/task.ts @@ -15,6 +15,7 @@ const TaskSchema = new mongoose.Schema({ required: true, enum: ['open', 'close'] }, + interval: Number, title_id: String, description: String, created: BigInt, diff --git a/src/services/grpc/boss/v2/register-task.ts b/src/services/grpc/boss/v2/register-task.ts index 30c67f4..6ec103c 100644 --- a/src/services/grpc/boss/v2/register-task.ts +++ b/src/services/grpc/boss/v2/register-task.ts @@ -17,6 +17,7 @@ export async function registerTask(request: RegisterTaskRequest, context: CallCo const bossAppID = request.bossAppId.trim(); const titleID = request.titleId.toString(16).toLowerCase().padStart(16, '0'); const status = request.status; + const interval = request.interval; const description = request.description.trim(); if (!taskID) { @@ -59,6 +60,7 @@ export async function registerTask(request: RegisterTaskRequest, context: CallCo boss_app_id: bossAppID, creator_pid: context.user?.pid, status, + interval, title_id: titleID, description: description, created: Date.now(), @@ -73,7 +75,7 @@ export async function registerTask(request: RegisterTaskRequest, context: CallCo bossAppId: task.boss_app_id, creatorPid: task.creator_pid, status: task.status, - interval: 0, // TODO - Don't stub this + interval: task.interval, titleId: BigInt(parseInt(task.title_id, 16)), description: task.description, createdTimestamp: task.created, diff --git a/src/services/grpc/boss/v2/update-task.ts b/src/services/grpc/boss/v2/update-task.ts index be2413b..e883678 100644 --- a/src/services/grpc/boss/v2/update-task.ts +++ b/src/services/grpc/boss/v2/update-task.ts @@ -45,6 +45,7 @@ export async function updateTask(request: UpdateTaskRequest, context: CallContex task.boss_app_id = updateData.bossAppId ? updateData.bossAppId : task.boss_app_id; task.title_id = updateData.titleId ? updateData.titleId.toString(16).toLowerCase().padStart(16, '0') : task.title_id; task.status = updateData.status ? updateData.status : task.status; + task.interval = updateData.interval ? updateData.interval : task.interval; task.description = updateData.description ? updateData.description : task.description; task.updated = BigInt(Date.now()); diff --git a/src/types/mongoose/task.ts b/src/types/mongoose/task.ts index d50e362..6e92fdb 100644 --- a/src/types/mongoose/task.ts +++ b/src/types/mongoose/task.ts @@ -6,7 +6,8 @@ export interface ITask { in_game_id: string; boss_app_id: string; creator_pid: number; - status: 'open' | 'close'; // TODO - Make this a union. What else is there? + status: 'open' | 'close'; + interval: number; title_id: string; description: string; created: bigint; From ce8f6a13560ddb4030ec7f088a865886452f5899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Mon, 10 Nov 2025 12:52:39 +0000 Subject: [PATCH 24/30] feat(npdl): Support edge cases for country and language-specific files --- src/services/npdl.ts | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/services/npdl.ts b/src/services/npdl.ts index c9a5d5d..7585196 100644 --- a/src/services/npdl.ts +++ b/src/services/npdl.ts @@ -3,7 +3,7 @@ import { getCTRTaskFile } from '@/database'; import { config } from '@/config-manager'; import { restrictHostnames } from '@/middleware/host-limit'; import { getCDNFileAsStream, streamFileToResponse } from '@/cdn'; -import { handleEtag, sendEtagCacheResponse } from '@/util'; +import { handleEtag, isValidCountryCode, sendEtagCacheResponse } from '@/util'; const npdl = express.Router(); @@ -23,7 +23,26 @@ npdl.get([ }>, response) => { const { appID, taskID, countryCode, languageCode, fileName } = request.params; - const file = await getCTRTaskFile(appID, taskID, fileName, countryCode, languageCode); + // * There are some special cases that we need to account for some specific 3DS task files: + // * + // * 1. The country and language being represented in a single parameter with an underscore :languageCode_:countryCode + // * 2. Only the country parameter being set instead of the language + // * + // * This isn't the standard behavior as it doesn't work for all tasks, only some of them do need it + // * (this is so unstandard that you can't officially find tasks which use an underscore with the file list endpoint). + // * I'm sure whoever designed this behavior must be the most evil person I've ever met + let country: string | undefined; + let language: string | undefined; + if (countryCode == undefined && languageCode !== undefined && languageCode.includes('_')) { + [language, country] = languageCode.split('_'); + } else if (countryCode == undefined && languageCode !== undefined && isValidCountryCode(languageCode)) { + country = languageCode; + } else { + country = countryCode; + language = languageCode; + } + + const file = await getCTRTaskFile(appID, taskID, fileName, country, language); if (!file) { response.sendStatus(404); From 17bdbb2751c4c9c4f3e75e56dd6dbd901f1c19b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Tue, 11 Nov 2025 11:53:08 +0000 Subject: [PATCH 25/30] chore: Update pretendonetwork/boss-crypto to 1.2.2 Plus some other fixes. --- package-lock.json | 14 +++++++------- package.json | 2 +- src/cli/files-3ds.cmd.ts | 5 +++-- src/cli/files.cmd.ts | 3 ++- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index e8d3d58..beddf7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "AGPL-3.0-only", "dependencies": { "@aws-sdk/client-s3": "^3.723.0", - "@pretendonetwork/boss-crypto": "^1.1.3", + "@pretendonetwork/boss-crypto": "^1.2.2", "@pretendonetwork/grpc": "^2.3.5", "@typegoose/auto-increment": "^4.13.1", "commander": "^14.0.0", @@ -2047,9 +2047,9 @@ } }, "node_modules/@pretendonetwork/boss-crypto": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@pretendonetwork/boss-crypto/-/boss-crypto-1.1.3.tgz", - "integrity": "sha512-7SJFgpR7RVsfhWL9SGMbChYrE/IOU5PmO1kWIifXSX5H6GEAIvpdV9HV1werru/pA6DyWJw0dGWlPmHvT3AWnA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@pretendonetwork/boss-crypto/-/boss-crypto-1.2.2.tgz", + "integrity": "sha512-sGlMiXGIThWfbs85xdWuJdgmW6YV6aQ8znh3vWWwG08BwpzzqgflluFYKhy6P0rpiMUJQuDAWK1IcVIsjD1lhw==", "license": "LGPL-3.0-only" }, "node_modules/@pretendonetwork/eslint-config": { @@ -11299,9 +11299,9 @@ "optional": true }, "@pretendonetwork/boss-crypto": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@pretendonetwork/boss-crypto/-/boss-crypto-1.1.3.tgz", - "integrity": "sha512-7SJFgpR7RVsfhWL9SGMbChYrE/IOU5PmO1kWIifXSX5H6GEAIvpdV9HV1werru/pA6DyWJw0dGWlPmHvT3AWnA==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@pretendonetwork/boss-crypto/-/boss-crypto-1.2.2.tgz", + "integrity": "sha512-sGlMiXGIThWfbs85xdWuJdgmW6YV6aQ8znh3vWWwG08BwpzzqgflluFYKhy6P0rpiMUJQuDAWK1IcVIsjD1lhw==" }, "@pretendonetwork/eslint-config": { "version": "0.1.1", diff --git a/package.json b/package.json index e538f21..441bd4a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.723.0", - "@pretendonetwork/boss-crypto": "^1.1.3", + "@pretendonetwork/boss-crypto": "^1.2.2", "@pretendonetwork/grpc": "^2.3.5", "@typegoose/auto-increment": "^4.13.1", "commander": "^14.0.0", diff --git a/src/cli/files-3ds.cmd.ts b/src/cli/files-3ds.cmd.ts index b634cf1..93d488a 100644 --- a/src/cli/files-3ds.cmd.ts +++ b/src/cli/files-3ds.cmd.ts @@ -84,7 +84,8 @@ const downloadCmd = new Command('download') const ctx = getCliContext(); const { files } = await ctx.grpc.listFilesCTR({ bossAppId: appId, - taskId: taskId + taskId: taskId, + any: true }); const file = files.find(v => v.dataId === dataId); if (!file) { @@ -94,7 +95,7 @@ const downloadCmd = new Command('download') const npdl = ctx.getNpdlUrl(); const country = file.supportedCountries.length > 0 ? '/' + file.supportedCountries[0] : ''; - const language = file.supportedLanguages.length > 0 ? '/' + file.supportedCountries[0] : ''; + const language = file.supportedLanguages.length > 0 ? '/' + file.supportedLanguages[0] : ''; const { body, statusCode } = await request(`${npdl.url}/p01/nsa/${file.bossAppId}/${file.taskId}${country}${language}/${file.name}`, { headers: { Host: npdl.host diff --git a/src/cli/files.cmd.ts b/src/cli/files.cmd.ts index 9fd1553..6f6ea23 100644 --- a/src/cli/files.cmd.ts +++ b/src/cli/files.cmd.ts @@ -87,7 +87,8 @@ const downloadCmd = new Command('download') const ctx = getCliContext(); const { files } = await ctx.grpc.listFilesWUP({ bossAppId: appId, - taskId: taskId + taskId: taskId, + any: true }); const file = files.find(v => v.dataId === dataId); if (!file) { From f3c57f99ef207f9328b5dbdc8c7bed8e22262569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Tue, 11 Nov 2025 11:56:37 +0000 Subject: [PATCH 26/30] feat(3ds): Add support for no payload contents Having a task file with no payload contents is perfectly valid and supported officially. This can be used for files where only the metadata is relevant, such as on Team Kirby Clash Deluxe passwords which are stored on a file description and the prize comes from the attributes. --- src/cli/files-3ds.cmd.ts | 21 +++++++++++--------- src/services/grpc/boss/v2/upload-file-ctr.ts | 4 ---- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/cli/files-3ds.cmd.ts b/src/cli/files-3ds.cmd.ts index 93d488a..2d9e8bb 100644 --- a/src/cli/files-3ds.cmd.ts +++ b/src/cli/files-3ds.cmd.ts @@ -140,11 +140,12 @@ const createCmd = new Command('create') .option('--attribute3 [attribute3]', 'Attribute 3 for this task file') .option('--desc [desc]', 'Description for this task file') .option('-m, --mark-arrived-privileged', 'Only notify of new content to privileged titles') + .option('-n, --no-payload', 'Make this task file have no payload contents') .action(commandHandler<[string, string]>(async (cmd): Promise => { const [appId, taskId] = cmd.args; // TODO - Handle multiple payload contents - const opts = cmd.opts<{ name: string; titleId: string; contentDatatype: string; nsDataId: string; version: string; file: string; country: string[]; lang: string[]; attribute1?: string; attribute2?: string; attribute3?: string; desc?: string; markArrivedPrivileged: boolean }>(); - const fileBuf = await fs.readFile(opts.file); + const opts = cmd.opts<{ name: string; titleId: string; contentDatatype: string; nsDataId: string; version: string; file: string; country: string[]; lang: string[]; attribute1?: string; attribute2?: string; attribute3?: string; desc?: string; markArrivedPrivileged: boolean; payload: boolean }>(); + const fileBuf = opts.payload ? await fs.readFile(opts.file) : Buffer.alloc(0); const ctx = getCliContext(); const { file } = await ctx.grpc.uploadFileCTR({ taskId: taskId, @@ -158,13 +159,15 @@ const createCmd = new Command('create') description: opts.desc }, name: opts.name, - payloadContents: [{ - titleId: BigInt(parseInt(opts.titleId, 16)), - contentDatatype: Number(opts.contentDatatype), - nsDataId: Number(opts.nsDataId), - version: Number(opts.version), - content: fileBuf - }], + payloadContents: opts.payload + ? [{ + titleId: BigInt(parseInt(opts.titleId, 16)), + contentDatatype: Number(opts.contentDatatype), + nsDataId: Number(opts.nsDataId), + version: Number(opts.version), + content: fileBuf + }] + : [], flags: { markArrivedPrivileged: opts.markArrivedPrivileged } diff --git a/src/services/grpc/boss/v2/upload-file-ctr.ts b/src/services/grpc/boss/v2/upload-file-ctr.ts index b787ce6..0cdf8e5 100644 --- a/src/services/grpc/boss/v2/upload-file-ctr.ts +++ b/src/services/grpc/boss/v2/upload-file-ctr.ts @@ -57,10 +57,6 @@ export async function uploadFileCTR(request: UploadFileCTRRequest, context: Call } } - if (payloads.length === 0) { - throw new ServerError(Status.INVALID_ARGUMENT, 'Cannot upload empty file'); - } - if (!request.attributes) { request.attributes = { attribute1: '', From c17b68c45fc47dcdad798464c13a136655ab8e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B3pez=20Guimaraes?= Date: Tue, 11 Nov 2025 13:06:11 +0000 Subject: [PATCH 27/30] fix(cli): Allow download of files with no payload content --- src/cli/files-3ds.cmd.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/files-3ds.cmd.ts b/src/cli/files-3ds.cmd.ts index 2d9e8bb..efcb52b 100644 --- a/src/cli/files-3ds.cmd.ts +++ b/src/cli/files-3ds.cmd.ts @@ -117,7 +117,7 @@ const downloadCmd = new Command('download') const keys = ctx.get3DSKeys(); const decrypted = decrypt3DS(buffer, keys.aesKey); // TODO - Handle multiple payloads - buffer = decrypted.payload_contents[0].content; + buffer = decrypted.payload_contents[0]?.content ?? Buffer.alloc(0); } await pipeline(Readable.from(buffer), process.stdout); From a95019778cdd503844a37d9e8714e9da96e8489b Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Sun, 23 Nov 2025 13:37:11 -0500 Subject: [PATCH 28/30] feat(grpc): allow the send/receive message sizes to be customized --- README.md | 70 +++++++++++++++++++------------------ src/config-manager.ts | 2 ++ src/services/grpc/server.ts | 5 ++- 3 files changed, 42 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 365318c..d24d052 100644 --- a/README.md +++ b/README.md @@ -11,39 +11,41 @@ Handles all BOSS (Background Online Storage Service) related tasks for the Prete Configurations are loaded through environment variables. `.env` files are supported. -| Environment variable | Description | Default | -| ------------------------------------------------ | ----------------------------------------------------------------- | --------------------------------------------- | -| `PN_BOSS_CONFIG_HTTP_PORT` | The HTTP port the server listens on | None | -| `PN_BOSS_CONFIG_LOG_FORMAT` | What logging format to use, possible options: `pretty` or `json` | `pretty` | -| `PN_BOSS_CONFIG_LOG_LEVEL` | What log level to use | `info` | -| `PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY` | The BOSS WiiU AES key, needs to be dumped from a console | None | -| `PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY` | The BOSS WiiU HMAC key, needs to be dumped from a console | None | -| `PN_BOSS_CONFIG_BOSS_3DS_AES_KEY` | The BOSS 3DS AES key, needs to be dumped from a console | None | -| `PN_BOSS_CONFIG_MONGO_CONNECTION_STRING` | MongoDB connection string | None | -| `PN_BOSS_CONFIG_GRPC_BOSS_SERVER_ADDRESS` | Address for the GRPC server to listen on | None | -| `PN_BOSS_CONFIG_GRPC_BOSS_SERVER_PORT` | Port for the GRPC server to listen on | None | -| `PN_BOSS_CONFIG_GRPC_BOSS_SERVER_API_KEY` | API key that services will use to connect to the BOSS GRPC server | None | -| `PN_BOSS_CONFIG_GRPC_ACCOUNT_SERVER_ADDRESS` | Address of the account GRPC server | None | -| `PN_BOSS_CONFIG_GRPC_ACCOUNT_SERVER_PORT` | Port of the account GRPC server | None | -| `PN_BOSS_CONFIG_GRPC_ACCOUNT_SERVER_API_KEY` | API key of the account GRPC server | None | -| `PN_BOSS_CONFIG_GRPC_FRIENDS_SERVER_ADDRESS` | Address of the friends GRPC server | None | -| `PN_BOSS_CONFIG_GRPC_FRIENDS_SERVER_PORT` | Port of the friends GRPC server | None | -| `PN_BOSS_CONFIG_GRPC_FRIENDS_SERVER_API_KEY` | API key of the friends GRPC server | None | -| `PN_BOSS_CONFIG_S3_ENDPOINT` | S3 server endpoint | None | -| `PN_BOSS_CONFIG_S3_REGION` | S3 server region | None | -| `PN_BOSS_CONFIG_S3_BUCKET` | S3 server bucket | None | -| `PN_BOSS_CONFIG_S3_ACCESS_KEY` | S3 access key | None | -| `PN_BOSS_CONFIG_S3_ACCESS_SECRET` | S3 access key secret | None | -| `PN_BOSS_CONFIG_CDN_DISK_PATH` | Storage path for the CDN, use as alternative for S3 | None | -| `PN_BOSS_CONFIG_STREETPASS_RELAY_ENABLED` | Should Streetpass Relay be enabled? | `false` | -| `PN_BOSS_CONFIG_STREETPASS_RELAY_CLEAN_OLD_DATA` | Should old Streetpass Relay data be automatically cleaned up? | `false` | -| `PN_BOSS_CONFIG_DOMAINS_NPDI` | What domain should the NPDI component use? | `npdi.cdn.pretendo.cc` | -| `PN_BOSS_CONFIG_DOMAINS_NPDL` | What domain should the NPDL component use? | `npdl.cdn.pretendo.cc` | -| `PN_BOSS_CONFIG_DOMAINS_NPFL` | What domain should the NPFL component use? | `npfl.c.app.pretendo.cc` | -| `PN_BOSS_CONFIG_DOMAINS_NPPL` | What domain should the NPPL component use? | `nppl.app.pretendo.cc,nppl.c.app.pretendo.cc` | -| `PN_BOSS_CONFIG_DOMAINS_NPTS` | What domain should the NPTS component use? | `npts.app.pretendo.cc` | -| `PN_BOSS_CONFIG_DOMAINS_SPR` | What domain should the SPR component use? | `service.spr.app.pretendo.cc` | - +| Environment variable | Description | Default | +|--------------------------------------------------|---------------------------------------------------------------------|-----------------------------------------------| +| `PN_BOSS_CONFIG_HTTP_PORT` | The HTTP port the server listens on | None | +| `PN_BOSS_CONFIG_LOG_FORMAT` | What logging format to use, possible options: `pretty` or `json` | `pretty` | +| `PN_BOSS_CONFIG_LOG_LEVEL` | What log level to use | `info` | +| `PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY` | The BOSS WiiU AES key, needs to be dumped from a console | None | +| `PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY` | The BOSS WiiU HMAC key, needs to be dumped from a console | None | +| `PN_BOSS_CONFIG_BOSS_3DS_AES_KEY` | The BOSS 3DS AES key, needs to be dumped from a console | None | +| `PN_BOSS_CONFIG_MONGO_CONNECTION_STRING` | MongoDB connection string | None | +| `PN_BOSS_CONFIG_GRPC_MAX_RECEIVE_MESSAGE_LENGTH` | The maximum size, in megabytes, a message sent to the server can be | 4 | +| `PN_BOSS_CONFIG_GRPC_MAX_SEND_MESSAGE_LENGTH` | The maximum size, in megabytes, a message sent to the client can be | 4 | +| `PN_BOSS_CONFIG_GRPC_BOSS_SERVER_ADDRESS` | Address for the GRPC server to listen on | None | +| `PN_BOSS_CONFIG_GRPC_BOSS_SERVER_PORT` | Port for the GRPC server to listen on | None | +| `PN_BOSS_CONFIG_GRPC_BOSS_SERVER_API_KEY` | API key that services will use to connect to the BOSS GRPC server | None | +| `PN_BOSS_CONFIG_GRPC_ACCOUNT_SERVER_ADDRESS` | Address of the account GRPC server | None | +| `PN_BOSS_CONFIG_GRPC_ACCOUNT_SERVER_PORT` | Port of the account GRPC server | None | +| `PN_BOSS_CONFIG_GRPC_ACCOUNT_SERVER_API_KEY` | API key of the account GRPC server | None | +| `PN_BOSS_CONFIG_GRPC_FRIENDS_SERVER_ADDRESS` | Address of the friends GRPC server | None | +| `PN_BOSS_CONFIG_GRPC_FRIENDS_SERVER_PORT` | Port of the friends GRPC server | None | +| `PN_BOSS_CONFIG_GRPC_FRIENDS_SERVER_API_KEY` | API key of the friends GRPC server | None | +| `PN_BOSS_CONFIG_S3_ENDPOINT` | S3 server endpoint | None | +| `PN_BOSS_CONFIG_S3_REGION` | S3 server region | None | +| `PN_BOSS_CONFIG_S3_BUCKET` | S3 server bucket | None | +| `PN_BOSS_CONFIG_S3_ACCESS_KEY` | S3 access key | None | +| `PN_BOSS_CONFIG_S3_ACCESS_SECRET` | S3 access key secret | None | +| `PN_BOSS_CONFIG_CDN_DISK_PATH` | Storage path for the CDN, use as alternative for S3 | None | +| `PN_BOSS_CONFIG_STREETPASS_RELAY_ENABLED` | Should Streetpass Relay be enabled? | `false` | +| `PN_BOSS_CONFIG_STREETPASS_RELAY_CLEAN_OLD_DATA` | Should old Streetpass Relay data be automatically cleaned up? | `false` | +| `PN_BOSS_CONFIG_DOMAINS_NPDI` | What domain should the NPDI component use? | `npdi.cdn.pretendo.cc` | +| `PN_BOSS_CONFIG_DOMAINS_NPDL` | What domain should the NPDL component use? | `npdl.cdn.pretendo.cc` | +| `PN_BOSS_CONFIG_DOMAINS_NPFL` | What domain should the NPFL component use? | `npfl.c.app.pretendo.cc` | +| `PN_BOSS_CONFIG_DOMAINS_NPPL` | What domain should the NPPL component use? | `nppl.app.pretendo.cc,nppl.c.app.pretendo.cc` | +| `PN_BOSS_CONFIG_DOMAINS_NPTS` | What domain should the NPTS component use? | `npts.app.pretendo.cc` | +| `PN_BOSS_CONFIG_DOMAINS_SPR` | What domain should the SPR component use? | `service.spr.app.pretendo.cc` | + ## S3 server The S3 server is optional, you can set `PN_BOSS_CONFIG_CDN_DISK_PATH` if you want to use a local folder as CDN source instead. @@ -61,7 +63,7 @@ npm run build Configurations are loaded through environment variables. `.env` files are supported. | Environment variable | Description | | -| --------------------------- | ------------------------------------------------------------------------------------------- | -------- | +|-----------------------------|---------------------------------------------------------------------------------------------|----------| | `PN_BOSS_CLI_GRPC_HOST` | The Host that the BOSS GRPC server is on. Example: `localhost:5678` | Required | | `PN_BOSS_CLI_GRPC_APIKEY` | Master API key of the BOSS GRPC server. | Required | | `PN_BOSS_CLI_WIIU_AES_KEY` | The BOSS WiiU AES key, needs to be dumped from a console | Optional | diff --git a/src/config-manager.ts b/src/config-manager.ts index c8e5e23..dc7342f 100644 --- a/src/config-manager.ts +++ b/src/config-manager.ts @@ -45,6 +45,8 @@ export const config = { } }, grpc: { + max_receive_message_length: Number(process.env.PN_BOSS_CONFIG_GRPC_MAX_RECEIVE_MESSAGE_LENGTH_MB?.trim() || '4'), + max_send_message_length: Number(process.env.PN_BOSS_CONFIG_GRPC_MAX_SEND_MESSAGE_LENGTH_MB?.trim() || '4'), boss: { address: process.env.PN_BOSS_CONFIG_GRPC_BOSS_SERVER_ADDRESS?.trim() || '', port: Number(process.env.PN_BOSS_CONFIG_GRPC_BOSS_SERVER_PORT?.trim() || ''), diff --git a/src/services/grpc/server.ts b/src/services/grpc/server.ts index b41b699..0ef5475 100644 --- a/src/services/grpc/server.ts +++ b/src/services/grpc/server.ts @@ -11,7 +11,10 @@ import { config } from '@/config-manager'; import type { Server } from 'nice-grpc'; export async function startGRPCServer(): Promise { - const server: Server = createServer(); + const server: Server = createServer({ + 'grpc.max_receive_message_length': config.grpc.max_receive_message_length * 1024 * 1024, + 'grpc.max_send_message_length': config.grpc.max_send_message_length * 1024 * 1024 + }); server.with(apiKeyMiddlewareV1).with(authenticationMiddlewareV1).add(BossServiceDefinitionV1, bossServiceImplementationV1); server.with(apiKeyMiddlewareV2).with(authenticationMiddlewareV2).add(BossServiceDefinitionV2, bossServiceImplementationV2); From b1f648a176d8285bfc494793d77bd1aa4e25fd91 Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Tue, 2 Dec 2025 11:54:17 -0500 Subject: [PATCH 29/30] fix(grpc): Mongoose FileCTR.create needs an array when using transactions --- src/services/grpc/boss/v2/upload-file-ctr.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/grpc/boss/v2/upload-file-ctr.ts b/src/services/grpc/boss/v2/upload-file-ctr.ts index 0cdf8e5..5f5be21 100644 --- a/src/services/grpc/boss/v2/upload-file-ctr.ts +++ b/src/services/grpc/boss/v2/upload-file-ctr.ts @@ -82,7 +82,7 @@ export async function uploadFileCTR(request: UploadFileCTRRequest, context: Call await file.save({ session }); } - file = await FileCTR.create({ + [file] = await FileCTR.create([{ creator_pid: context.user?.pid, // * hash: String, // * file_key: String, @@ -105,7 +105,7 @@ export async function uploadFileCTR(request: UploadFileCTRRequest, context: Call }, created: Date.now(), updated: Date.now() - }); + }], { session }); const cryptoOptions = payloads.map(payload => ({ program_id: payload.titleId, From fa186e5dec4711eb825f0b2b85b9c1a5d23016f7 Mon Sep 17 00:00:00 2001 From: Jonathan Barrow Date: Tue, 2 Dec 2025 12:00:11 -0500 Subject: [PATCH 30/30] chore: update max send/receive grpc size config option names in the README these did not match the actual expected values before --- README.md | 68 +++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index d24d052..95ebbb4 100644 --- a/README.md +++ b/README.md @@ -11,40 +11,40 @@ Handles all BOSS (Background Online Storage Service) related tasks for the Prete Configurations are loaded through environment variables. `.env` files are supported. -| Environment variable | Description | Default | -|--------------------------------------------------|---------------------------------------------------------------------|-----------------------------------------------| -| `PN_BOSS_CONFIG_HTTP_PORT` | The HTTP port the server listens on | None | -| `PN_BOSS_CONFIG_LOG_FORMAT` | What logging format to use, possible options: `pretty` or `json` | `pretty` | -| `PN_BOSS_CONFIG_LOG_LEVEL` | What log level to use | `info` | -| `PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY` | The BOSS WiiU AES key, needs to be dumped from a console | None | -| `PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY` | The BOSS WiiU HMAC key, needs to be dumped from a console | None | -| `PN_BOSS_CONFIG_BOSS_3DS_AES_KEY` | The BOSS 3DS AES key, needs to be dumped from a console | None | -| `PN_BOSS_CONFIG_MONGO_CONNECTION_STRING` | MongoDB connection string | None | -| `PN_BOSS_CONFIG_GRPC_MAX_RECEIVE_MESSAGE_LENGTH` | The maximum size, in megabytes, a message sent to the server can be | 4 | -| `PN_BOSS_CONFIG_GRPC_MAX_SEND_MESSAGE_LENGTH` | The maximum size, in megabytes, a message sent to the client can be | 4 | -| `PN_BOSS_CONFIG_GRPC_BOSS_SERVER_ADDRESS` | Address for the GRPC server to listen on | None | -| `PN_BOSS_CONFIG_GRPC_BOSS_SERVER_PORT` | Port for the GRPC server to listen on | None | -| `PN_BOSS_CONFIG_GRPC_BOSS_SERVER_API_KEY` | API key that services will use to connect to the BOSS GRPC server | None | -| `PN_BOSS_CONFIG_GRPC_ACCOUNT_SERVER_ADDRESS` | Address of the account GRPC server | None | -| `PN_BOSS_CONFIG_GRPC_ACCOUNT_SERVER_PORT` | Port of the account GRPC server | None | -| `PN_BOSS_CONFIG_GRPC_ACCOUNT_SERVER_API_KEY` | API key of the account GRPC server | None | -| `PN_BOSS_CONFIG_GRPC_FRIENDS_SERVER_ADDRESS` | Address of the friends GRPC server | None | -| `PN_BOSS_CONFIG_GRPC_FRIENDS_SERVER_PORT` | Port of the friends GRPC server | None | -| `PN_BOSS_CONFIG_GRPC_FRIENDS_SERVER_API_KEY` | API key of the friends GRPC server | None | -| `PN_BOSS_CONFIG_S3_ENDPOINT` | S3 server endpoint | None | -| `PN_BOSS_CONFIG_S3_REGION` | S3 server region | None | -| `PN_BOSS_CONFIG_S3_BUCKET` | S3 server bucket | None | -| `PN_BOSS_CONFIG_S3_ACCESS_KEY` | S3 access key | None | -| `PN_BOSS_CONFIG_S3_ACCESS_SECRET` | S3 access key secret | None | -| `PN_BOSS_CONFIG_CDN_DISK_PATH` | Storage path for the CDN, use as alternative for S3 | None | -| `PN_BOSS_CONFIG_STREETPASS_RELAY_ENABLED` | Should Streetpass Relay be enabled? | `false` | -| `PN_BOSS_CONFIG_STREETPASS_RELAY_CLEAN_OLD_DATA` | Should old Streetpass Relay data be automatically cleaned up? | `false` | -| `PN_BOSS_CONFIG_DOMAINS_NPDI` | What domain should the NPDI component use? | `npdi.cdn.pretendo.cc` | -| `PN_BOSS_CONFIG_DOMAINS_NPDL` | What domain should the NPDL component use? | `npdl.cdn.pretendo.cc` | -| `PN_BOSS_CONFIG_DOMAINS_NPFL` | What domain should the NPFL component use? | `npfl.c.app.pretendo.cc` | -| `PN_BOSS_CONFIG_DOMAINS_NPPL` | What domain should the NPPL component use? | `nppl.app.pretendo.cc,nppl.c.app.pretendo.cc` | -| `PN_BOSS_CONFIG_DOMAINS_NPTS` | What domain should the NPTS component use? | `npts.app.pretendo.cc` | -| `PN_BOSS_CONFIG_DOMAINS_SPR` | What domain should the SPR component use? | `service.spr.app.pretendo.cc` | +| Environment variable | Description | Default | +|-----------------------------------------------------|---------------------------------------------------------------------|-----------------------------------------------| +| `PN_BOSS_CONFIG_HTTP_PORT` | The HTTP port the server listens on | None | +| `PN_BOSS_CONFIG_LOG_FORMAT` | What logging format to use, possible options: `pretty` or `json` | `pretty` | +| `PN_BOSS_CONFIG_LOG_LEVEL` | What log level to use | `info` | +| `PN_BOSS_CONFIG_BOSS_WIIU_AES_KEY` | The BOSS WiiU AES key, needs to be dumped from a console | None | +| `PN_BOSS_CONFIG_BOSS_WIIU_HMAC_KEY` | The BOSS WiiU HMAC key, needs to be dumped from a console | None | +| `PN_BOSS_CONFIG_BOSS_3DS_AES_KEY` | The BOSS 3DS AES key, needs to be dumped from a console | None | +| `PN_BOSS_CONFIG_MONGO_CONNECTION_STRING` | MongoDB connection string | None | +| `PN_BOSS_CONFIG_GRPC_MAX_RECEIVE_MESSAGE_LENGTH_MB` | The maximum size, in megabytes, a message sent to the server can be | 4 | +| `PN_BOSS_CONFIG_GRPC_MAX_SEND_MESSAGE_LENGTH_MB` | The maximum size, in megabytes, a message sent to the client can be | 4 | +| `PN_BOSS_CONFIG_GRPC_BOSS_SERVER_ADDRESS` | Address for the GRPC server to listen on | None | +| `PN_BOSS_CONFIG_GRPC_BOSS_SERVER_PORT` | Port for the GRPC server to listen on | None | +| `PN_BOSS_CONFIG_GRPC_BOSS_SERVER_API_KEY` | API key that services will use to connect to the BOSS GRPC server | None | +| `PN_BOSS_CONFIG_GRPC_ACCOUNT_SERVER_ADDRESS` | Address of the account GRPC server | None | +| `PN_BOSS_CONFIG_GRPC_ACCOUNT_SERVER_PORT` | Port of the account GRPC server | None | +| `PN_BOSS_CONFIG_GRPC_ACCOUNT_SERVER_API_KEY` | API key of the account GRPC server | None | +| `PN_BOSS_CONFIG_GRPC_FRIENDS_SERVER_ADDRESS` | Address of the friends GRPC server | None | +| `PN_BOSS_CONFIG_GRPC_FRIENDS_SERVER_PORT` | Port of the friends GRPC server | None | +| `PN_BOSS_CONFIG_GRPC_FRIENDS_SERVER_API_KEY` | API key of the friends GRPC server | None | +| `PN_BOSS_CONFIG_S3_ENDPOINT` | S3 server endpoint | None | +| `PN_BOSS_CONFIG_S3_REGION` | S3 server region | None | +| `PN_BOSS_CONFIG_S3_BUCKET` | S3 server bucket | None | +| `PN_BOSS_CONFIG_S3_ACCESS_KEY` | S3 access key | None | +| `PN_BOSS_CONFIG_S3_ACCESS_SECRET` | S3 access key secret | None | +| `PN_BOSS_CONFIG_CDN_DISK_PATH` | Storage path for the CDN, use as alternative for S3 | None | +| `PN_BOSS_CONFIG_STREETPASS_RELAY_ENABLED` | Should Streetpass Relay be enabled? | `false` | +| `PN_BOSS_CONFIG_STREETPASS_RELAY_CLEAN_OLD_DATA` | Should old Streetpass Relay data be automatically cleaned up? | `false` | +| `PN_BOSS_CONFIG_DOMAINS_NPDI` | What domain should the NPDI component use? | `npdi.cdn.pretendo.cc` | +| `PN_BOSS_CONFIG_DOMAINS_NPDL` | What domain should the NPDL component use? | `npdl.cdn.pretendo.cc` | +| `PN_BOSS_CONFIG_DOMAINS_NPFL` | What domain should the NPFL component use? | `npfl.c.app.pretendo.cc` | +| `PN_BOSS_CONFIG_DOMAINS_NPPL` | What domain should the NPPL component use? | `nppl.app.pretendo.cc,nppl.c.app.pretendo.cc` | +| `PN_BOSS_CONFIG_DOMAINS_NPTS` | What domain should the NPTS component use? | `npts.app.pretendo.cc` | +| `PN_BOSS_CONFIG_DOMAINS_SPR` | What domain should the SPR component use? | `service.spr.app.pretendo.cc` | ## S3 server The S3 server is optional, you can set `PN_BOSS_CONFIG_CDN_DISK_PATH` if you want to use a local folder as CDN source instead.