From 81cb1725718461c215d78d23d7dabe3ce83bdf3b Mon Sep 17 00:00:00 2001 From: Tau Date: Tue, 16 Apr 2019 15:38:12 -0400 Subject: [PATCH] Persist earned bonus tickets --- src/idz/decoder/saveProfile.ts | 24 ++++++++++++++++++ src/idz/encoder/loadProfile2.ts | 11 +++++++++ src/idz/handler/createProfile.ts | 2 +- src/idz/handler/loadProfile.ts | 2 ++ src/idz/handler/saveProfile.ts | 1 + src/idz/model/index.ts | 1 + src/idz/model/tickets.ts | 10 ++++++++ src/idz/request/saveProfile.ts | 2 ++ src/idz/response/loadProfile2.ts | 2 ++ src/idz/world/defs.ts | 2 ++ src/idz/world/index.ts | 5 ++++ src/idz/world/tickets.ts | 42 ++++++++++++++++++++++++++++++++ 12 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 src/idz/model/tickets.ts create mode 100644 src/idz/world/tickets.ts diff --git a/src/idz/decoder/saveProfile.ts b/src/idz/decoder/saveProfile.ts index 72d2190..322c8d2 100644 --- a/src/idz/decoder/saveProfile.ts +++ b/src/idz/decoder/saveProfile.ts @@ -35,6 +35,15 @@ export function saveProfile(buf: Buffer): SaveProfileRequest { coursePlays.set(i, buf.readUInt16LE(0x04c0 + 2 * i)); } + const freeCar = { + validFrom: buf.readUInt32LE(0x0138), + }; + + const freeContinue = { + validFrom: buf.readUInt32LE(0x0038), + validTo: buf.readUInt32LE(0x003c), + }; + return { type: "save_profile_req", profileId: buf.readUInt32LE(0x0004) as Id, @@ -63,6 +72,21 @@ export function saveProfile(buf: Buffer): SaveProfileRequest { 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(0x045a), pack: buf.readUInt32LE(0x0034), diff --git a/src/idz/encoder/loadProfile2.ts b/src/idz/encoder/loadProfile2.ts index 5698acc..3f6f235 100644 --- a/src/idz/encoder/loadProfile2.ts +++ b/src/idz/encoder/loadProfile2.ts @@ -54,6 +54,17 @@ export function loadProfile2(res: LoadProfileResponse2) { buf.writeUInt16LE(playCount, 0x0460 + 2 * courseId); } + const { freeCar, freeContinue } = res.tickets; + + if (freeCar) { + buf.writeUInt32LE((freeCar.validFrom.getTime() / 1000) | 0, 0x01e4); + } + + if (freeContinue) { + buf.writeUInt32LE((freeContinue.validFrom.getTime() / 1000) | 0, 0x03dc); + buf.writeUInt32LE((freeContinue.validTo.getTime() / 1000) | 0, 0x03e0); + } + buf.writeUInt16LE(0x0065, 0x0000); buf.writeUInt8(res.unlocks.cup, 0x00b4); buf.writeUInt16LE(res.unlocks.gauges, 0x00b8); diff --git a/src/idz/handler/createProfile.ts b/src/idz/handler/createProfile.ts index e130330..5c8d596 100644 --- a/src/idz/handler/createProfile.ts +++ b/src/idz/handler/createProfile.ts @@ -4,7 +4,6 @@ import { Profile } from "../model/profile"; import { Settings } from "../model/settings"; import { Story } from "../model/story"; import { Team } from "../model/team"; -import { TimeAttackScore } from "../model/timeAttack"; import { Unlocks } from "../model/unlocks"; import { CreateProfileRequest } from "../request/createProfile"; import { GenericResponse } from "../response/generic"; @@ -46,6 +45,7 @@ export async function createProfile( w.settings().save(profile.id, settings), w.story().save(profile.id, story), w.unlocks().save(profile.id, unlocks), + w.tickets().save(profile.id, {}), ]); return { diff --git a/src/idz/handler/loadProfile.ts b/src/idz/handler/loadProfile.ts index 7be088f..5f1ab8c 100644 --- a/src/idz/handler/loadProfile.ts +++ b/src/idz/handler/loadProfile.ts @@ -20,6 +20,7 @@ export async function loadProfile( const story = await w.story().load(profile.id); const timeAttack = await w.timeAttack().loadAll(profile.id); const unlocks = await w.unlocks().load(profile.id); + const tickets = await w.tickets().load(profile.id); return { type: "load_profile_v2_res", @@ -41,5 +42,6 @@ export async function loadProfile( carCount, story, unlocks, + tickets, }; } diff --git a/src/idz/handler/saveProfile.ts b/src/idz/handler/saveProfile.ts index a3280bf..f3f3836 100644 --- a/src/idz/handler/saveProfile.ts +++ b/src/idz/handler/saveProfile.ts @@ -30,6 +30,7 @@ export async function saveProfile( w.titles().saveAll(req.profileId, req.titles), w.unlocks().save(req.profileId, req.unlocks), w.settings().save(req.profileId, req.settings), + w.tickets().save(req.profileId, req.tickets), ]); return { diff --git a/src/idz/model/index.ts b/src/idz/model/index.ts index 35f200d..2dc2be7 100644 --- a/src/idz/model/index.ts +++ b/src/idz/model/index.ts @@ -6,5 +6,6 @@ export { Profile } from "./profile"; export { Settings } from "./settings"; export { Story } from "./story"; export { Team } from "./team"; +export { Tickets } from "./tickets"; export { TimeAttackScore } from "./timeAttack"; export { Unlocks } from "./unlocks"; diff --git a/src/idz/model/tickets.ts b/src/idz/model/tickets.ts new file mode 100644 index 0000000..141bf05 --- /dev/null +++ b/src/idz/model/tickets.ts @@ -0,0 +1,10 @@ +export interface Tickets { + freeCar?: { + validFrom: Date; + // Valid To cannot be controlled, it is always 14 days after issue date. + }; + freeContinue?: { + validFrom: Date; + validTo: Date; + }; +} diff --git a/src/idz/request/saveProfile.ts b/src/idz/request/saveProfile.ts index b449c83..3873c4c 100644 --- a/src/idz/request/saveProfile.ts +++ b/src/idz/request/saveProfile.ts @@ -4,6 +4,7 @@ import { MissionState } from "../model/mission"; import { Profile } from "../model/profile"; import { Settings } from "../model/settings"; import { Story } from "../model/story"; +import { Tickets } from "../model/tickets"; import { Unlocks } from "../model/unlocks"; export interface SaveProfileRequest { @@ -22,5 +23,6 @@ export interface SaveProfileRequest { car: Car; story: Story; unlocks: Unlocks; + tickets: Tickets; settings: Settings; } diff --git a/src/idz/response/loadProfile2.ts b/src/idz/response/loadProfile2.ts index 66aa4f1..00f2a27 100644 --- a/src/idz/response/loadProfile2.ts +++ b/src/idz/response/loadProfile2.ts @@ -5,6 +5,7 @@ import { MissionState } from "../model/mission"; import { Profile } from "../model/profile"; import { Settings } from "../model/settings"; import { Story } from "../model/story"; +import { Tickets } from "../model/tickets"; import { TimeAttackScore } from "../model/timeAttack"; import { Unlocks } from "../model/unlocks"; @@ -28,5 +29,6 @@ export interface LoadProfileResponse2 { carCount: number; story: Story; unlocks: Unlocks; + tickets: Tickets; // giga TODO } diff --git a/src/idz/world/defs.ts b/src/idz/world/defs.ts index df8bd36..68ad0bc 100644 --- a/src/idz/world/defs.ts +++ b/src/idz/world/defs.ts @@ -84,6 +84,8 @@ export interface World { // Also not a facet. w/e one step at a time. story(): FacetRepository; + tickets(): FacetRepository; + timeAttack(): TimeAttackRepository; titles(): FlagRepository; diff --git a/src/idz/world/index.ts b/src/idz/world/index.ts index 3ef1a12..fb79fc1 100644 --- a/src/idz/world/index.ts +++ b/src/idz/world/index.ts @@ -6,6 +6,7 @@ import { CarRepositoryImpl } from "./car"; import { CoursePlaysRepositoryImpl } from "./coursePlays"; import { World } from "./defs"; import { ProfileRepositoryImpl } from "./profile"; +import { TicketsRepositoryImpl } from "./tickets"; import { TimeAttackRepositoryImpl } from "./timeAttack"; import * as Model from "../model"; @@ -49,6 +50,10 @@ class WorldImpl implements World { return new FacetRepositoryImpl(this._root, "story"); } + tickets() { + return new TicketsRepositoryImpl(this._root); + } + timeAttack() { return new TimeAttackRepositoryImpl(this._root); } diff --git a/src/idz/world/tickets.ts b/src/idz/world/tickets.ts new file mode 100644 index 0000000..c9930b3 --- /dev/null +++ b/src/idz/world/tickets.ts @@ -0,0 +1,42 @@ +import { loadJson, saveJson } from "./_util"; +import { FacetRepository } from "./defs"; +import { Tickets } from "../model/tickets"; +import { Id, Profile } from "../model"; + +interface TicketsJson { + freeCar?: { + validFrom: string; + // Valid To cannot be controlled, it is always 14 days after issue date. + }; + freeContinue?: { + validFrom: string; + validTo: string; + }; +} + +export class TicketsRepositoryImpl implements FacetRepository { + private readonly _path: string; + + constructor(root: string) { + this._path = root + "/tickets.json"; + } + + async load(profileId: Id): Promise { + const json: TicketsJson = await loadJson(this._path); + + return { + freeCar: json.freeCar && { + validFrom: new Date(json.freeCar.validFrom), + }, + freeContinue: json.freeContinue && { + validFrom: new Date(json.freeContinue.validFrom), + validTo: new Date(json.freeContinue.validTo), + }, + }; + } + + save(profileId: Id, tickets: Tickets): Promise { + // Date objects stringify to ISO8601 so this does this right thing. + return saveJson(this._path, tickets); + } +}