From fecc0e7981fe665b733124a890ddbb87d4c5303e Mon Sep 17 00:00:00 2001 From: Tau Date: Tue, 7 May 2019 19:40:01 -0400 Subject: [PATCH] wip --- src/idz/decoder/index.ts | 10 +- src/idz/decoder/loadProfile.ts | 14 --- src/idz/decoder/loadProfile2.ts | 30 ++++++ .../{saveProfile.ts => saveProfile2.ts} | 9 +- src/idz/decoder/saveProfile3.ts | 101 ++++++++++++++++++ src/idz/encoder/loadProfile.ts | 4 + src/idz/encoder/loadProfile2.ts | 2 +- src/idz/encoder/loadProfile3.ts | 101 ++++++++++++++++++ src/idz/handler/loadProfile.ts | 2 +- src/idz/handler/saveProfile.ts | 4 + src/idz/request/loadProfile.ts | 12 ++- src/idz/request/saveProfile.ts | 12 ++- src/idz/response/loadProfile.ts | 9 +- 13 files changed, 283 insertions(+), 27 deletions(-) delete mode 100644 src/idz/decoder/loadProfile.ts create mode 100644 src/idz/decoder/loadProfile2.ts rename src/idz/decoder/{saveProfile.ts => saveProfile2.ts} (92%) create mode 100644 src/idz/decoder/saveProfile3.ts create mode 100644 src/idz/encoder/loadProfile3.ts diff --git a/src/idz/decoder/index.ts b/src/idz/decoder/index.ts index 92586fe..4ef7c5f 100644 --- a/src/idz/decoder/index.ts +++ b/src/idz/decoder/index.ts @@ -11,7 +11,7 @@ import { loadConfig2 } from "./loadConfig2"; import { loadGarage } from "./loadGarage"; import { loadGeneralReward } from "./loadGeneralReward"; import { loadGhost } from "./loadGhost"; -import { loadProfile } from "./loadProfile"; +import { loadProfile2 } from "./loadProfile2"; import { loadRewardTable } from "./loadRewardTable"; import { loadServerList } from "./loadServerList"; import { loadStocker } from "./loadStocker"; @@ -23,7 +23,8 @@ import { msg00AD } from "./msg00AD"; import { saveExpedition } from "./saveExpedition"; import { saveGarage } from "./saveGarage"; import { saveNewCar } from "./saveNewCar"; -import { saveProfile } from "./saveProfile"; +import { saveProfile2 } from "./saveProfile2"; +import { saveProfile3 } from "./saveProfile3"; import { saveSettings } from "./saveSettings"; import { saveStocker } from "./saveStocker"; import { saveTimeAttack } from "./saveTimeAttack"; @@ -56,7 +57,7 @@ const funcList: ReaderFn[] = [ loadGarage, loadGeneralReward, loadGhost, - loadProfile, + loadProfile2, loadRewardTable, loadServerList, loadStocker, @@ -70,7 +71,8 @@ const funcList: ReaderFn[] = [ saveExpedition, saveGarage, saveNewCar, - saveProfile, + saveProfile2, + saveProfile3, saveSettings, saveStocker, saveTimeAttack, diff --git a/src/idz/decoder/loadProfile.ts b/src/idz/decoder/loadProfile.ts deleted file mode 100644 index 69b91be..0000000 --- a/src/idz/decoder/loadProfile.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { RequestCode } from "./_defs"; -import { LoadProfileRequest } from "../request/loadProfile"; -import { AimeId } from "../../model"; - -loadProfile.msgCode = 0x0067 as RequestCode; -loadProfile.msgLen = 0x0020; - -export function loadProfile(buf: Buffer): LoadProfileRequest { - return { - type: "load_profile_req", - aimeId: buf.readUInt32LE(0x0004) as AimeId, - luid: buf.slice(0x0008, buf.indexOf("\0", 0x0008)).toString("ascii"), - }; -} diff --git a/src/idz/decoder/loadProfile2.ts b/src/idz/decoder/loadProfile2.ts new file mode 100644 index 0000000..edc88b7 --- /dev/null +++ b/src/idz/decoder/loadProfile2.ts @@ -0,0 +1,30 @@ +import { RequestCode } from "./_defs"; +import { + LoadProfileRequest2, + LoadProfileRequest3, +} from "../request/loadProfile"; +import { AimeId } from "../../model"; + +loadProfile2.msgCode = 0x0067 as RequestCode; +loadProfile2.msgLen = 0x0020; + +export function loadProfile2(buf: Buffer): LoadProfileRequest2 { + return { + type: "load_profile_req", + format: 2, + aimeId: buf.readUInt32LE(0x0004) as AimeId, + luid: buf.slice(0x0008, buf.indexOf("\0", 0x0008)).toString("ascii"), + }; +} + +loadProfile3.msgCode = 0x0012f as RequestCode; +loadProfile3.msgLen = 0x0020; + +export function loadProfile3(buf: Buffer): LoadProfileRequest3 { + return { + type: "load_profile_req", + format: 3, + aimeId: buf.readUInt32LE(0x0004) as AimeId, + luid: buf.slice(0x0008, buf.indexOf("\0", 0x0008)).toString("ascii"), + }; +} diff --git a/src/idz/decoder/saveProfile.ts b/src/idz/decoder/saveProfile2.ts similarity index 92% rename from src/idz/decoder/saveProfile.ts rename to src/idz/decoder/saveProfile2.ts index 77337dc..cf1f42c 100644 --- a/src/idz/decoder/saveProfile.ts +++ b/src/idz/decoder/saveProfile2.ts @@ -3,13 +3,13 @@ import { mission } from "./_mission"; import { RequestCode } from "./_defs"; import { BackgroundCode, CourseNo, ExtId, TitleCode } from "../model/base"; import { Profile } from "../model/profile"; -import { SaveProfileRequest } from "../request/saveProfile"; +import { SaveProfileRequest2 } from "../request/saveProfile"; import { bitmap } from "./_bitmap"; -saveProfile.msgCode = 0x0068 as RequestCode; -saveProfile.msgLen = 0x0940; +saveProfile2.msgCode = 0x0068 as RequestCode; +saveProfile2.msgLen = 0x0940; -export function saveProfile(buf: Buffer): SaveProfileRequest { +export function saveProfile2(buf: Buffer): SaveProfileRequest2 { const storyRows = new Array(); for (let i = 0; i < 9; i++) { @@ -46,6 +46,7 @@ export function saveProfile(buf: Buffer): SaveProfileRequest { return { type: "save_profile_req", + format: 2, profileId: buf.readUInt32LE(0x0004) as ExtId, lv: buf.readUInt16LE(0x0026), exp: buf.readUInt32LE(0x0028), diff --git a/src/idz/decoder/saveProfile3.ts b/src/idz/decoder/saveProfile3.ts new file mode 100644 index 0000000..48584eb --- /dev/null +++ b/src/idz/decoder/saveProfile3.ts @@ -0,0 +1,101 @@ +import { car } from "./_car"; +import { mission } from "./_mission"; +import { RequestCode } from "./_defs"; +import { BackgroundCode, CourseNo, ExtId, TitleCode } from "../model/base"; +import { Profile } from "../model/profile"; +import { SaveProfileRequest2 } from "../request/saveProfile"; +import { bitmap } from "./_bitmap"; + +saveProfile3.msgCode = 0x0138 as RequestCode; +saveProfile3.msgLen = 0x0a70; + +export function saveProfile3(buf: Buffer): SaveProfileRequest2 { + const storyRows = new Array(); + + /* Story seems to have changed somewhat + + for (let i = 0; i < 9; i++) { + const cells = new Array(); + const rowOffset = 0xXXXX + i * 0x3c; + + for (let j = 0; j < 9; j++) { + const a = buf.readUInt32LE(rowOffset + 0x04 + j * 4); + const b = buf.readUInt16LE(rowOffset + 0x28 + j * 2); + const cell = { a, b }; + + cells.push(cell); + } + + const row = { cells }; + + storyRows.push(row); + } + */ + + const coursePlays = new Map(); + + for (let i = 0; i < 20; i++) { + coursePlays.set(i as CourseNo, buf.readUInt16LE(0x0554 + 2 * i)); + } + + const freeCar = { + validFrom: buf.readUInt32LE(0x0138), + }; + + const freeContinue = { + validFrom: buf.readUInt32LE(0x0038), + validTo: buf.readUInt32LE(0x003c), + }; + + return { + type: "save_profile_req", + format: 2, + profileId: buf.readUInt32LE(0x0004) as ExtId, + lv: buf.readUInt16LE(0x0026), + exp: buf.readUInt32LE(0x0028), + fame: buf.readUInt32LE(0x04fc), + dpoint: buf.readUInt32LE(0x04f8), + mileage: buf.readUInt32LE(0x0008), + title: buf.readUInt16LE(0x0040) as TitleCode, + titles: bitmap(buf.slice(0x0042, 0x00f6)), + background: buf.readUInt8(0x0874) as BackgroundCode, + coursePlays, + missions: { + team: mission(buf.slice(0x0430, 0x0452)), + solo: mission(buf.slice(0x0848, 0x086a)), + }, + car: car(buf.slice(0x0958, 0x09b8)), + story: { + x: buf.readUInt16LE(0x0818), + y: buf.readUInt8(0x07fc), + rows: storyRows, + }, + unlocks: { + cup: buf.readUInt8(0x0110), + gauges: buf.readUInt16LE(0x0114), + music: buf.readUInt16LE(0x0140), + lastMileageReward: buf.readUInt32LE(0x013c), + }, + tickets: { + freeCar: + freeCar.validFrom !== 0 + ? { + validFrom: new Date(freeCar.validFrom * 1000), + } + : undefined, + freeContinue: + freeContinue.validFrom !== 0 && freeContinue.validTo !== 0 + ? { + validFrom: new Date(freeContinue.validFrom * 1000), + validTo: new Date(freeContinue.validTo * 1000), + } + : undefined, + }, + settings: { + music: buf.readUInt16LE(0x04ee), + pack: buf.readUInt32LE(0x0034), + paperCup: buf.readUInt8(0x00f6), + gauges: buf.readUInt8(0x00f7), + }, + }; +} diff --git a/src/idz/encoder/loadProfile.ts b/src/idz/encoder/loadProfile.ts index 1435428..df29669 100644 --- a/src/idz/encoder/loadProfile.ts +++ b/src/idz/encoder/loadProfile.ts @@ -1,5 +1,6 @@ import { loadProfile1 } from "./loadProfile1"; import { loadProfile2 } from "./loadProfile2"; +import { loadProfile3 } from "./loadProfile3"; import { LoadProfileResponse } from "../response/loadProfile"; export function loadProfile(res: LoadProfileResponse) { @@ -10,6 +11,9 @@ export function loadProfile(res: LoadProfileResponse) { case 2: return loadProfile2(res); + case 3: + return loadProfile3(res); + default: const exhaust: never = res; diff --git a/src/idz/encoder/loadProfile2.ts b/src/idz/encoder/loadProfile2.ts index 9c9bc17..9cd8832 100644 --- a/src/idz/encoder/loadProfile2.ts +++ b/src/idz/encoder/loadProfile2.ts @@ -87,8 +87,8 @@ export function loadProfile2(res: LoadProfileResponse2) { buf.writeUInt32LE(res.dpoint, 0x03e8); buf.writeUInt32LE(res.fame, 0x0404); iconv.encode(res.name + "\0", "shift_jis").copy(buf, 0x03ee); - buf.writeUInt16LE(res.story.x, 0x06bc); buf.writeUInt8(res.story.y, 0x0670); + buf.writeUInt16LE(res.story.x, 0x06bc); mission(res.missions.solo).copy(buf, 0x06e4); chara(res.chara).copy(buf, 0x070c); bitmap(res.titles, 0xb4).copy(buf, 0x720); diff --git a/src/idz/encoder/loadProfile3.ts b/src/idz/encoder/loadProfile3.ts new file mode 100644 index 0000000..1485385 --- /dev/null +++ b/src/idz/encoder/loadProfile3.ts @@ -0,0 +1,101 @@ +import iconv = require("iconv-lite"); + +import { bitmap } from "./_bitmap"; +import { car } from "./_car"; +import { chara } from "./_chara"; +import { mission } from "./_mission"; +import { LoadProfileResponse3 } from "../response/loadProfile"; + +export function loadProfile3(res: LoadProfileResponse3) { + const buf = Buffer.alloc(0x0ea0); + + // Initialize all TA grades to uhh... fuck knows + buf.fill(0xff, 0x07e4, 0x080c); + + for (const score of res.timeAttack) { + const { routeNo } = score; + + buf.writeUInt32LE( + (new Date(score.timestamp).getTime() / 1000) | 0, // Date ctor hack + 0x00e4 + routeNo * 4 + ); + + buf.writeUInt16LE(0, 0x067c + 2 * routeNo); // ??? + buf.writeUInt16LE(0xffff, 0x0184 + 2 * routeNo); // National rank + buf.writeUInt32LE((score.totalTime * 1000) | 0, 0x00e4 + 4 * routeNo); + buf.writeUInt8(score.flags, 0x06cc + routeNo); + buf.writeUInt8(score.grade, 0x07e4 + routeNo); + + for (let i = 0; i < 3; i++) { + buf.writeUInt16LE( + (score.sectionTimes[i] * 1000) >> 2, + 0x06f4 + 6 * routeNo + 2 * i + ); + } + } + + for (let i = 0; i < 9 && i < res.story.rows.length; i++) { + const row = res.story.rows[i]; + const rowOffset = 0x0256 + i * 0x26; + + for (let j = 0; j < 9 && j < row.cells.length; j++) { + const cell = row.cells[j]; + const cellOffset = rowOffset + j * 4; + + buf.writeUInt16LE(cell.a, cellOffset + 0); + buf.writeUInt16LE(cell.b, cellOffset + 2); + } + } + + for (const [courseId, playCount] of res.coursePlays.entries()) { + if (courseId < 0 || courseId >= 20) { + throw new Error(`Course id out of range: ${courseId}`); + } + + buf.writeUInt16LE(playCount, 0x053c + 2 * courseId); + } + + const { freeCar, freeContinue } = res.tickets; + + if (freeCar) { + buf.writeUInt32LE((freeCar.validFrom.getTime() / 1000) | 0, 0x0214); + } + + if (freeContinue) { + buf.writeUInt32LE((freeContinue.validFrom.getTime() / 1000) | 0, 0x04b8); + buf.writeUInt32LE((freeContinue.validTo.getTime() / 1000) | 0, 0x04bc); + } + + buf.writeUInt16LE(0x012e, 0x0000); + buf.writeUInt8(res.unlocks.cup, 0x00b4); + buf.writeUInt16LE(res.unlocks.gauges, 0x00b8); + buf.writeUInt32LE(res.unlocks.lastMileageReward, 0x0218); + buf.writeUInt16LE(res.unlocks.music, 0x021c); + buf.writeUInt16LE(0, 0x0456); // Team leader + mission(res.missions.team).copy(buf, 0x0460); + buf.writeUInt16LE(0xffff, 0x0462); // [1] + buf.writeUInt32LE(res.profileId, 0x0494); + buf.writeUInt32LE(res.mileage, 0x0498); + buf.writeUInt16LE(res.settings.music, 0x04a4); + buf.writeUInt16LE(res.lv, 0x04a8); + buf.writeUInt32LE(res.exp, 0x04ac); + buf.writeUInt32LE(res.settings.pack, 0x04b4); + buf.writeUInt32LE(res.dpoint, 0x04c4); + buf.writeUInt32LE(res.fame, 0x04e0); + iconv.encode(res.name + "\0", "shift_jis").copy(buf, 0x04ca); + buf.writeUInt8(res.story.y, 0x080c); + buf.writeUInt16LE(res.story.x, 0x0828); + mission(res.missions.solo).copy(buf, 0x0858); + chara(res.chara).copy(buf, 0x0880); + bitmap(res.titles, 0xb4).copy(buf, 0x0894); + buf.writeUInt8(res.settings.paperCup, 0x094d); + buf.writeUInt8(res.settings.gauges, 0x094e); + buf.writeUInt32LE(res.teamId || 0xffffffff, 0x0954); + buf.writeUInt32LE(res.carCount, 0x0dd0); + car(res.car).copy(buf, 0x0dd4); + + // [1] Currently unknown, but if this field is zero then the player will have + // a "model record" emblem in their profile card. + + return buf; +} diff --git a/src/idz/handler/loadProfile.ts b/src/idz/handler/loadProfile.ts index 2926bfa..7e93dfc 100644 --- a/src/idz/handler/loadProfile.ts +++ b/src/idz/handler/loadProfile.ts @@ -24,7 +24,7 @@ export async function loadProfile( return { type: "load_profile_res", - format: 2, + format: req.format as any, // TS fart name: profile.name, profileId: profile.id, lv: profile.lv, diff --git a/src/idz/handler/saveProfile.ts b/src/idz/handler/saveProfile.ts index 5cb8a4a..a157c27 100644 --- a/src/idz/handler/saveProfile.ts +++ b/src/idz/handler/saveProfile.ts @@ -6,6 +6,10 @@ export async function saveProfile( w: Repositories, req: SaveProfileRequest ): Promise { + console.log("*** PROFILE SAVE TEMPORARILY DISABLED ***"); + + return { type: "generic_res", status: 1 }; + const now = new Date(); const profile = await w.profile().load(req.profileId); const chara = await w.chara().load(req.profileId); diff --git a/src/idz/request/loadProfile.ts b/src/idz/request/loadProfile.ts index d9f9535..e2c2143 100644 --- a/src/idz/request/loadProfile.ts +++ b/src/idz/request/loadProfile.ts @@ -1,7 +1,17 @@ import { AimeId } from "../../model"; -export interface LoadProfileRequest { +interface LoadProfileRequestBase { type: "load_profile_req"; aimeId: AimeId; luid: string; } + +export interface LoadProfileRequest2 extends LoadProfileRequestBase { + format: 2; +} + +export interface LoadProfileRequest3 extends LoadProfileRequestBase { + format: 3; +} + +export type LoadProfileRequest = LoadProfileRequest2 | LoadProfileRequest3; diff --git a/src/idz/request/saveProfile.ts b/src/idz/request/saveProfile.ts index 6306d7d..eac5a46 100644 --- a/src/idz/request/saveProfile.ts +++ b/src/idz/request/saveProfile.ts @@ -7,7 +7,7 @@ import { Story } from "../model/story"; import { Tickets } from "../model/tickets"; import { Unlocks } from "../model/unlocks"; -export interface SaveProfileRequest { +interface SaveProfileRequestBase { type: "save_profile_req"; profileId: ExtId; lv: number; @@ -26,3 +26,13 @@ export interface SaveProfileRequest { tickets: Tickets; settings: Settings; } + +export interface SaveProfileRequest2 extends SaveProfileRequestBase { + format: 2; +} + +export interface SaveProfileRequest3 extends SaveProfileRequestBase { + format: 3; +} + +export type SaveProfileRequest = SaveProfileRequest2 | SaveProfileRequest3; diff --git a/src/idz/response/loadProfile.ts b/src/idz/response/loadProfile.ts index 7366edb..691b2bc 100644 --- a/src/idz/response/loadProfile.ts +++ b/src/idz/response/loadProfile.ts @@ -41,4 +41,11 @@ export interface LoadProfileResponse2 extends LoadProfileResponseBase { format: 2; } -export type LoadProfileResponse = LoadProfileResponse1 | LoadProfileResponse2; +export interface LoadProfileResponse3 extends LoadProfileResponseBase { + format: 3; +} + +export type LoadProfileResponse = + | LoadProfileResponse1 + | LoadProfileResponse2 + | LoadProfileResponse3;