From 8817029258d437b75297ed46afc4c71f005d4977 Mon Sep 17 00:00:00 2001 From: Tau Date: Sat, 6 Apr 2019 14:02:16 -0400 Subject: [PATCH] add idz repository layer --- .gitignore | 1 + src/idz/decoder/_bitmap.ts | 15 ++++++ src/idz/decoder/_chara.ts | 3 ++ src/idz/decoder/createProfile.ts | 4 +- src/idz/decoder/discoverProfile.ts | 3 +- src/idz/decoder/loadProfile.ts | 3 +- src/idz/decoder/loadStocker.ts | 4 +- src/idz/decoder/saveSettings.ts | 4 +- src/idz/decoder/saveStocker.ts | 8 ++- src/idz/encoder/_chara.ts | 4 +- src/idz/encoder/_team.ts | 3 +- src/idz/encoder/loadProfile2.ts | 9 +--- src/idz/handler/_team.ts | 7 ++- src/idz/handler/createProfile.ts | 35 ++++++++++++- src/idz/handler/discoverProfile.ts | 6 +-- src/idz/handler/index.ts | 2 +- src/idz/handler/loadProfile.ts | 67 +++++++----------------- src/idz/handler/loadStocker.ts | 8 +-- src/idz/handler/saveSettings.ts | 6 ++- src/idz/handler/saveStocker.ts | 7 ++- src/idz/index.ts | 7 ++- src/idz/model/base.ts | 7 +++ src/idz/model/chara.ts | 4 ++ src/idz/model/index.ts | 6 +++ src/idz/model/profile.ts | 11 ++++ src/idz/model/team.ts | 6 +++ src/idz/request/createProfile.ts | 2 - src/idz/request/discoverProfile.ts | 4 +- src/idz/request/loadProfile.ts | 4 +- src/idz/request/loadStocker.ts | 5 +- src/idz/request/saveSettings.ts | 4 +- src/idz/request/saveStocker.ts | 8 ++- src/idz/response/_team.ts | 4 +- src/idz/response/loadProfile2.ts | 8 +-- src/idz/world.ts | 3 -- src/idz/world/_singleton.ts | 82 ++++++++++++++++++++++++++++++ src/idz/world/base.ts | 29 +++++++++++ src/idz/world/index.ts | 62 ++++++++++++++++++++++ src/idz/world/profile.ts | 19 +++++++ 39 files changed, 375 insertions(+), 99 deletions(-) create mode 100644 src/idz/decoder/_bitmap.ts create mode 100644 src/idz/model/base.ts create mode 100644 src/idz/model/index.ts create mode 100644 src/idz/model/profile.ts create mode 100644 src/idz/model/team.ts delete mode 100644 src/idz/world.ts create mode 100644 src/idz/world/_singleton.ts create mode 100644 src/idz/world/base.ts create mode 100644 src/idz/world/index.ts create mode 100644 src/idz/world/profile.ts diff --git a/.gitignore b/.gitignore index fd172d6..6566c9e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ bin/ node_modules/ pki/ +state/ diff --git a/src/idz/decoder/_bitmap.ts b/src/idz/decoder/_bitmap.ts new file mode 100644 index 0000000..32f75fd --- /dev/null +++ b/src/idz/decoder/_bitmap.ts @@ -0,0 +1,15 @@ +export function bitmap(buf: Buffer): number[] { + const result = new Array(); + + for (let byteNo = 0; byteNo < buf.length; byteNo++) { + const byte = buf.readUInt8(byteNo); + + for (let bitNo = 0; bitNo < 8; bitNo++) { + if (byte & (1 << bitNo)) { + result.push(8 * byteNo + bitNo); + } + } + } + + return result; +} diff --git a/src/idz/decoder/_chara.ts b/src/idz/decoder/_chara.ts index 12815a3..2ba3b7b 100644 --- a/src/idz/decoder/_chara.ts +++ b/src/idz/decoder/_chara.ts @@ -1,3 +1,4 @@ +import { BackgroundCode, TitleCode } from "../model/base"; import { Chara } from "../model/chara"; export function chara(buf: Buffer): Chara { @@ -10,5 +11,7 @@ export function chara(buf: Buffer): Chara { field_0A: buf.readUInt16LE(0x0a), field_0C: buf.readUInt16LE(0x0c), field_0E: buf.readUInt16LE(0x0e), + title: buf.readUInt16LE(0x10) as TitleCode, // Swapped on load + background: buf.readUInt16LE(0x12) as BackgroundCode, // Swapped on load }; } diff --git a/src/idz/decoder/createProfile.ts b/src/idz/decoder/createProfile.ts index 89cfbfa..a7ae11c 100644 --- a/src/idz/decoder/createProfile.ts +++ b/src/idz/decoder/createProfile.ts @@ -19,8 +19,6 @@ export function createProfile(buf: Buffer): CreateProfileRequest { ), field_0034: buf.readUInt32LE(0x0034), car: car(buf.slice(0x0040, 0x00a0)), - chara: chara(buf.slice(0x00a0, 0x00b0)), - field_00B0: buf.readUInt16LE(0x00b0), - field_00B2: buf.readUInt8(0x00b2), + chara: chara(buf.slice(0x00a0, 0x00b4)), }; } diff --git a/src/idz/decoder/discoverProfile.ts b/src/idz/decoder/discoverProfile.ts index 6e80928..511d548 100644 --- a/src/idz/decoder/discoverProfile.ts +++ b/src/idz/decoder/discoverProfile.ts @@ -1,4 +1,5 @@ import { RequestCode } from "./_defs"; +import { AimeId } from "../model/base"; import { DiscoverProfileRequest } from "../request/discoverProfile"; discoverProfile.msgCode = 0x006b as RequestCode; @@ -7,6 +8,6 @@ discoverProfile.msgLen = 0x0010; export function discoverProfile(buf: Buffer): DiscoverProfileRequest { return { type: "discover_profile_req", - aimeId: buf.readUInt32LE(0x0004), + aimeId: buf.readUInt32LE(0x0004) as AimeId, }; } diff --git a/src/idz/decoder/loadProfile.ts b/src/idz/decoder/loadProfile.ts index f3079ae..7ce901f 100644 --- a/src/idz/decoder/loadProfile.ts +++ b/src/idz/decoder/loadProfile.ts @@ -1,4 +1,5 @@ import { RequestCode } from "./_defs"; +import { AimeId } from "../model/base"; import { LoadProfileRequest } from "../request/loadProfile"; loadProfile.msgCode = 0x0067 as RequestCode; @@ -7,7 +8,7 @@ loadProfile.msgLen = 0x0020; export function loadProfile(buf: Buffer): LoadProfileRequest { return { type: "load_profile_req", - aimeId: buf.readUInt32LE(0x0004), + aimeId: buf.readUInt32LE(0x0004) as AimeId, pcbId: buf.slice(0x0008, buf.indexOf("\0", 0x0008)).toString("ascii"), }; } diff --git a/src/idz/decoder/loadStocker.ts b/src/idz/decoder/loadStocker.ts index 89a2076..bf834ad 100644 --- a/src/idz/decoder/loadStocker.ts +++ b/src/idz/decoder/loadStocker.ts @@ -1,4 +1,6 @@ import { RequestCode } from "./_defs"; +import { Id } from "../model/base"; +import { Profile } from "../model/profile"; import { LoadStockerRequest } from "../request/loadStocker"; loadStocker.msgCode = 0x00a7 as RequestCode; @@ -7,6 +9,6 @@ loadStocker.msgLen = 0x0010; export function loadStocker(buf: Buffer): LoadStockerRequest { return { type: "load_stocker_req", - profileId: buf.readUInt32LE(0x0004), + profileId: buf.readUInt32LE(0x0004) as Id, }; } diff --git a/src/idz/decoder/saveSettings.ts b/src/idz/decoder/saveSettings.ts index 27cfa45..6d6c932 100644 --- a/src/idz/decoder/saveSettings.ts +++ b/src/idz/decoder/saveSettings.ts @@ -1,4 +1,6 @@ import { RequestCode } from "./_defs"; +import { Id } from "../model/base"; +import { Profile } from "../model/profile"; import { SaveSettingsRequest } from "../request/saveSettings"; saveSettings.msgCode = 0x00a5 as RequestCode; @@ -11,7 +13,7 @@ export function saveSettings(buf: Buffer): SaveSettingsRequest { return { type: "save_settings_req", - profileId: buf.readUInt32LE(0x0004), + profileId: buf.readUInt32LE(0x0004) as Id, dpoint: buf.readUInt32LE(0x0008), settings: { bgMusic: buf.readUInt16LE(0x0002), diff --git a/src/idz/decoder/saveStocker.ts b/src/idz/decoder/saveStocker.ts index 3babed4..0d5903c 100644 --- a/src/idz/decoder/saveStocker.ts +++ b/src/idz/decoder/saveStocker.ts @@ -1,4 +1,8 @@ +import { bitmap } from "./_bitmap"; +import { chara } from "./_chara"; import { RequestCode } from "./_defs"; +import { BackgroundCode, Id } from "../model/base"; +import { Profile } from "../model/profile"; import { SaveStockerRequest } from "../request/saveStocker"; saveStocker.msgCode = 0x00a6 as RequestCode; @@ -7,6 +11,8 @@ saveStocker.msgLen = 0x00c0; export function saveStocker(buf: Buffer): SaveStockerRequest { return { type: "save_stocker_req", - profileId: buf.readUInt32LE(0x0004), + profileId: buf.readUInt32LE(0x0004) as Id, + backgrounds: bitmap(buf.slice(0x0008, 0x002c)) as BackgroundCode[], + chara: chara(buf.slice(0x009e, 0x00b2)), }; } diff --git a/src/idz/encoder/_chara.ts b/src/idz/encoder/_chara.ts index 77f7096..82f07db 100644 --- a/src/idz/encoder/_chara.ts +++ b/src/idz/encoder/_chara.ts @@ -1,7 +1,7 @@ import { Chara } from "../model/chara"; export function chara(chara: Chara): Buffer { - const buf = Buffer.alloc(0x0010); + const buf = Buffer.alloc(0x0014); buf.writeUInt8(chara.gender === "male" ? 0 : 1, 0x00); buf.writeUInt16LE(chara.clothing, 0x02); @@ -11,6 +11,8 @@ export function chara(chara: Chara): Buffer { buf.writeUInt16LE(chara.field_0A, 0x0a); buf.writeUInt16LE(chara.field_0C, 0x0c); buf.writeUInt16LE(chara.field_0E, 0x0e); + buf.writeUInt16LE(chara.background, 0x10); // Swapped on save + buf.writeUInt16LE(chara.title, 0x12); // Swapped on save return buf; } diff --git a/src/idz/encoder/_team.ts b/src/idz/encoder/_team.ts index 08ff26d..e9afa70 100644 --- a/src/idz/encoder/_team.ts +++ b/src/idz/encoder/_team.ts @@ -5,7 +5,8 @@ import { LoadTeamResponse } from "../response/loadTeam"; export function _team(res: CreateTeamResponse | LoadTeamResponse) { const buf = Buffer.alloc(0x0ca0); - iconv.encode(res.name, "shift_jis").copy(buf, 0x0024); + buf.writeUInt32LE(res.team.id, 0x000c); + iconv.encode(res.team.name, "shift_jis").copy(buf, 0x0024); for (let i = 0; i < 6; i++) { const base = 0x011c + i * 0x004c; diff --git a/src/idz/encoder/loadProfile2.ts b/src/idz/encoder/loadProfile2.ts index 1b352b7..c4eadc1 100644 --- a/src/idz/encoder/loadProfile2.ts +++ b/src/idz/encoder/loadProfile2.ts @@ -1,17 +1,14 @@ import iconv = require("iconv-lite"); +import { bitmap } from "./_bitmap"; import { car } from "./_car"; import { chara } from "./_chara"; import { LoadProfileResponse2 } from "../response/loadProfile2"; -import { bitmap } from "./_bitmap"; export function loadProfile2(res: LoadProfileResponse2) { // Story stuff //buf.writeUInt32LE(0x1ff, 0x0228); - // Flairs (bitmap) - // buf.fill(0xff, 0x071c, 0x071c + 0xb4); - const settingsPack = ((res.settings.forceQuitEn ? 1 : 0) << 0) | (res.settings.steeringForce << 1) | @@ -33,10 +30,8 @@ export function loadProfile2(res: LoadProfileResponse2) { buf.writeInt32LE(res.fame, 0x0404); iconv.encode(res.name + "\0", "shift_jis").copy(buf, 0x03ee); chara(res.chara).copy(buf, 0x070c); - buf.writeUInt16LE(res.background, 0x071c); - buf.writeUInt16LE(res.title, 0x071e); bitmap(res.titles, 0xb4).copy(buf, 0x720); - buf.writeInt32LE(res.teamId, 0x07e0); + buf.writeInt32LE(res.teamId || 0, 0x07e0); car(res.car).copy(buf, 0x0c5c); return buf; diff --git a/src/idz/handler/_team.ts b/src/idz/handler/_team.ts index 44d5444..197ad72 100644 --- a/src/idz/handler/_team.ts +++ b/src/idz/handler/_team.ts @@ -1,3 +1,5 @@ +import { Id } from "../model/base"; +import { Team } from "../model/team"; import { CreateTeamRequest } from "../request/createTeam"; import { LoadTeamRequest } from "../request/loadTeam"; import { CreateTeamResponse } from "../response/createTeam"; @@ -9,7 +11,10 @@ export function _team( req: CreateTeamRequest | LoadTeamRequest ): CreateTeamResponse | LoadTeamResponse { const bits = { - name: "ASS GENTLEMEN", + team: { + id: 2 as Id, + name: "ASS GENTLEMEN", + }, members: [], }; diff --git a/src/idz/handler/createProfile.ts b/src/idz/handler/createProfile.ts index a31fe03..54fbf3c 100644 --- a/src/idz/handler/createProfile.ts +++ b/src/idz/handler/createProfile.ts @@ -1,11 +1,42 @@ +import { Id } from "../model/base"; +import { Profile } from "../model/profile"; +import { Team } from "../model/team"; import { CreateProfileRequest } from "../request/createProfile"; import { GenericResponse } from "../response/generic"; import { World } from "../world"; -export function createProfile( +export async function createProfile( w: World, req: CreateProfileRequest -): GenericResponse { +): Promise { + const profile: Profile = { + id: 1 as Id, + teamId: 2 as Id, + name: req.name, + lv: 1, + fame: 0, + dpoint: 0, + }; + + const settings = { + bgMusic: 15, + forceQuitEn: false, + steeringForce: 4, + bgVolume: 10, + seVolume: 10, + cornerGuide: true, + lineGuide: false, + ghostEn: false, + taResultSkip: false, + }; + + await Promise.all([ + w.profile().save(profile.id, profile), + w.chara().save(profile.id, req.chara), + w.car().save(profile.id, req.car), + w.settings().save(profile.id, settings), + ]); + return { type: "generic_res", status: 1, diff --git a/src/idz/handler/discoverProfile.ts b/src/idz/handler/discoverProfile.ts index 87f6d22..f2df986 100644 --- a/src/idz/handler/discoverProfile.ts +++ b/src/idz/handler/discoverProfile.ts @@ -2,12 +2,12 @@ import { DiscoverProfileRequest } from "../request/discoverProfile"; import { DiscoverProfileResponse } from "../response/discoverProfile"; import { World } from "../world"; -export function discoverProfile( +export async function discoverProfile( w: World, req: DiscoverProfileRequest -): DiscoverProfileResponse { +): Promise { return { type: "discover_profile_res", - exists: true, + exists: await w.profile().discoverByAimeId(req.aimeId), }; } diff --git a/src/idz/handler/index.ts b/src/idz/handler/index.ts index 5d0da83..626e583 100644 --- a/src/idz/handler/index.ts +++ b/src/idz/handler/index.ts @@ -26,7 +26,7 @@ import { Request } from "../request"; import { Response } from "../response"; import { World } from "../world"; -export function dispatch(w: World, req: Request): Response { +export async function dispatch(w: World, req: Request): Promise { switch (req.type) { case "create_profile_req": return createProfile(w, req); diff --git a/src/idz/handler/loadProfile.ts b/src/idz/handler/loadProfile.ts index 71e388a..65e8daa 100644 --- a/src/idz/handler/loadProfile.ts +++ b/src/idz/handler/loadProfile.ts @@ -2,58 +2,27 @@ import { LoadProfileRequest } from "../request/loadProfile"; import { LoadProfileResponse2 } from "../response/loadProfile2"; import { World } from "../world"; -export function loadProfile( +export async function loadProfile( w: World, req: LoadProfileRequest -): LoadProfileResponse2 { +): Promise { + const profile = await w.profile().loadByAimeId(req.aimeId); + const settings = await w.settings().load(profile.id); + const chara = await w.chara().load(profile.id); + const titles = await w.titles().load(profile.id); + const car = await w.car().load(profile.id); + return { type: "load_profile_v2_res", - name: "てすと", - profileId: 0x11223344, - lv: 99, - fame: 1234, - dpoint: 54321, - teamId: 0x22334455, - settings: { - bgMusic: 15, - forceQuitEn: false, - steeringForce: 4, - bgVolume: 10, - seVolume: 10, - cornerGuide: true, - lineGuide: false, - ghostEn: false, - taResultSkip: false, - }, - chara: { - gender: "male", - clothing: 0, - mouth: 0, - field_06: 0, - field_08: 0, - field_0A: 0, - field_0C: 0, - field_0E: 0, - }, - background: 0, - title: 0, - titles: [], - car: { - field_00: 0, - field_02: 0, - field_04: [], - model: 0, - color: 0, - transmission: 0, - field_4A: 0, - field_4C: 21527, - field_50_lo: 3674393, - field_50_hi: 6407, - field_58: 0, - field_5A: 0, - field_5B: 0, - field_5C: 1, - field_5E: 0, - }, + name: profile.name, + profileId: profile.id, + lv: profile.lv, + fame: profile.fame, + dpoint: profile.dpoint, + teamId: profile.teamId, + settings, + chara, + titles, + car, }; } diff --git a/src/idz/handler/loadStocker.ts b/src/idz/handler/loadStocker.ts index 923580f..d00a6f7 100644 --- a/src/idz/handler/loadStocker.ts +++ b/src/idz/handler/loadStocker.ts @@ -2,13 +2,15 @@ import { LoadStockerRequest } from "../request/loadStocker"; import { LoadStockerResponse } from "../response/loadStocker"; import { World } from "../world"; -export function loadStocker( +export async function loadStocker( w: World, req: LoadStockerRequest -): LoadStockerResponse { +): Promise { + const backgrounds = await w.backgrounds().load(req.profileId); + return { type: "load_stocker_res", status: 1, - backgrounds: [], + backgrounds, }; } diff --git a/src/idz/handler/saveSettings.ts b/src/idz/handler/saveSettings.ts index a5bc1c8..8e746cb 100644 --- a/src/idz/handler/saveSettings.ts +++ b/src/idz/handler/saveSettings.ts @@ -2,10 +2,12 @@ import { SaveSettingsRequest } from "../request/saveSettings"; import { GenericResponse } from "../response/generic"; import { World } from "../world"; -export function saveSettings( +export async function saveSettings( w: World, req: SaveSettingsRequest -): GenericResponse { +): Promise { + await w.settings().save(req.profileId, req.settings); + return { type: "generic_res", status: 1, // ignored but whatever diff --git a/src/idz/handler/saveStocker.ts b/src/idz/handler/saveStocker.ts index 06d6df6..d943f7c 100644 --- a/src/idz/handler/saveStocker.ts +++ b/src/idz/handler/saveStocker.ts @@ -2,10 +2,13 @@ import { SaveStockerRequest } from "../request/saveStocker"; import { GenericResponse } from "../response/generic"; import { World } from "../world"; -export function saveStocker( +export async function saveStocker( w: World, req: SaveStockerRequest -): GenericResponse { +): Promise { + await w.backgrounds().save(req.profileId, req.backgrounds); + await w.chara().save(req.profileId, req.chara); + return { type: "generic_res", status: 1, // ignored diff --git a/src/idz/index.ts b/src/idz/index.ts index 909c919..2ce2f49 100644 --- a/src/idz/index.ts +++ b/src/idz/index.ts @@ -2,18 +2,17 @@ import { Socket } from "net"; import { dispatch } from "./handler"; import { setup } from "./setup"; -import { World } from "./world"; - -const world = new World(); +import { createWorld } from "./world"; export default async function idz(socket: Socket) { + const world = await createWorld("./state"); const { input, output } = setup(socket); console.log("Idz: Connection opened"); try { for await (const req of input) { - output.write(dispatch(world, req)); + output.write(await dispatch(world, req)); } } catch (e) { console.log("Idz: Error", e); diff --git a/src/idz/model/base.ts b/src/idz/model/base.ts new file mode 100644 index 0000000..0cb793a --- /dev/null +++ b/src/idz/model/base.ts @@ -0,0 +1,7 @@ +// Quasi-nominative "brands". These double-underscore properties never actually +// exist at run time, we just pretend they do. + +export type Id = number & { __id: T }; +export type AimeId = number & { __brand: "aime_id" }; +export type BackgroundCode = number & { __brand: "background_code" }; +export type TitleCode = number & { __brand: "title_code" }; diff --git a/src/idz/model/chara.ts b/src/idz/model/chara.ts index 5784005..9f8ceb0 100644 --- a/src/idz/model/chara.ts +++ b/src/idz/model/chara.ts @@ -1,3 +1,5 @@ +import { BackgroundCode, TitleCode } from "./base"; + export interface Chara { gender: "male" | "female"; clothing: number; @@ -7,4 +9,6 @@ export interface Chara { field_0A: number; field_0C: number; field_0E: number; + title: TitleCode; + background: BackgroundCode; } diff --git a/src/idz/model/index.ts b/src/idz/model/index.ts new file mode 100644 index 0000000..9ee2cfa --- /dev/null +++ b/src/idz/model/index.ts @@ -0,0 +1,6 @@ +export { AimeId, BackgroundCode, Id, TitleCode } from "./base"; +export { Car } from "./car"; +export { Chara } from "./chara"; +export { Profile } from "./profile"; +export { Settings } from "./settings"; +export { Team } from "./team"; diff --git a/src/idz/model/profile.ts b/src/idz/model/profile.ts new file mode 100644 index 0000000..4f64c44 --- /dev/null +++ b/src/idz/model/profile.ts @@ -0,0 +1,11 @@ +import { Id } from "./base"; +import { Team } from "./team"; + +export interface Profile { + id: Id; + teamId?: Id; + name: string; + lv: number; + fame: number; + dpoint: number; +} diff --git a/src/idz/model/team.ts b/src/idz/model/team.ts new file mode 100644 index 0000000..5531606 --- /dev/null +++ b/src/idz/model/team.ts @@ -0,0 +1,6 @@ +import { Id } from "./base"; + +export interface Team { + id: Id; + name: string; +} diff --git a/src/idz/request/createProfile.ts b/src/idz/request/createProfile.ts index 216e61b..735279b 100644 --- a/src/idz/request/createProfile.ts +++ b/src/idz/request/createProfile.ts @@ -9,6 +9,4 @@ export interface CreateProfileRequest { field_0034: number; car: Car; chara: Chara; - field_00B0: number; - field_00B2: number; } diff --git a/src/idz/request/discoverProfile.ts b/src/idz/request/discoverProfile.ts index edcabba..5d9001f 100644 --- a/src/idz/request/discoverProfile.ts +++ b/src/idz/request/discoverProfile.ts @@ -1,4 +1,6 @@ +import { AimeId } from "../model"; + export interface DiscoverProfileRequest { type: "discover_profile_req"; - aimeId: number; + aimeId: AimeId; } diff --git a/src/idz/request/loadProfile.ts b/src/idz/request/loadProfile.ts index 923a74e..df2fb91 100644 --- a/src/idz/request/loadProfile.ts +++ b/src/idz/request/loadProfile.ts @@ -1,5 +1,7 @@ +import { AimeId } from "../model/base"; + export interface LoadProfileRequest { type: "load_profile_req"; - aimeId: number; + aimeId: AimeId; pcbId: string; } diff --git a/src/idz/request/loadStocker.ts b/src/idz/request/loadStocker.ts index 1974828..ca4eb05 100644 --- a/src/idz/request/loadStocker.ts +++ b/src/idz/request/loadStocker.ts @@ -1,4 +1,7 @@ +import { Id } from "../model/base"; +import { Profile } from "../model/profile"; + export interface LoadStockerRequest { type: "load_stocker_req"; - profileId: number; + profileId: Id; } diff --git a/src/idz/request/saveSettings.ts b/src/idz/request/saveSettings.ts index 1006d87..6a065dc 100644 --- a/src/idz/request/saveSettings.ts +++ b/src/idz/request/saveSettings.ts @@ -1,8 +1,10 @@ +import { Id } from "../model/base"; +import { Profile } from "../model/profile"; import { Settings } from "../model/settings"; export interface SaveSettingsRequest { type: "save_settings_req"; - profileId: number; + profileId: Id; dpoint: number; // ?? why settings: Settings; field_0010: number; diff --git a/src/idz/request/saveStocker.ts b/src/idz/request/saveStocker.ts index 4e5c8e4..6a456be 100644 --- a/src/idz/request/saveStocker.ts +++ b/src/idz/request/saveStocker.ts @@ -1,4 +1,10 @@ +import { BackgroundCode, Id } from "../model/base"; +import { Chara } from "../model/chara"; +import { Profile } from "../model/profile"; + export interface SaveStockerRequest { type: "save_stocker_req"; - profileId: number; + profileId: Id; + backgrounds: BackgroundCode[]; + chara: Chara; } diff --git a/src/idz/response/_team.ts b/src/idz/response/_team.ts index cb65547..7d9d004 100644 --- a/src/idz/response/_team.ts +++ b/src/idz/response/_team.ts @@ -1,3 +1,5 @@ +import { Team } from "../model/team"; + export interface TeamMember { name: string; lv: number; @@ -5,7 +7,7 @@ export interface TeamMember { } export interface BaseTeamResponse { - name: string; + team: Team; members: TeamMember[]; // giga TODO } diff --git a/src/idz/response/loadProfile2.ts b/src/idz/response/loadProfile2.ts index 6137371..5e709ae 100644 --- a/src/idz/response/loadProfile2.ts +++ b/src/idz/response/loadProfile2.ts @@ -1,19 +1,19 @@ +import { Id } from "../model/base"; import { Car } from "../model/car"; import { Chara } from "../model/chara"; +import { Profile } from "../model/profile"; import { Settings } from "../model/settings"; export interface LoadProfileResponse2 { type: "load_profile_v2_res"; name: string; - profileId: number; + profileId: Id; lv: number; fame: number; dpoint: number; - teamId: number; + teamId?: number; settings: Settings; chara: Chara; - background: number; - title: number; titles: number[]; car: Car; // giga TODO diff --git a/src/idz/world.ts b/src/idz/world.ts deleted file mode 100644 index ef92ed8..0000000 --- a/src/idz/world.ts +++ /dev/null @@ -1,3 +0,0 @@ -export class World { - // Hand out references to some sort of backing store Repository objects here. -} diff --git a/src/idz/world/_singleton.ts b/src/idz/world/_singleton.ts new file mode 100644 index 0000000..b1724c5 --- /dev/null +++ b/src/idz/world/_singleton.ts @@ -0,0 +1,82 @@ +import * as fs from "fs"; +import { promisify } from "util"; + +import { Repository } from "./base"; +import { Id } from "../model/base"; + +const exists = promisify(fs.exists); +const readFile = promisify(fs.readFile); +const writeFile = promisify(fs.writeFile); + +export class SingletonJsonRepo implements Repository { + private _path: string; + + constructor(root: string, name: string) { + this._path = `${root}/${name}.json`; + } + + discover(id: Id): Promise { + return this._discoverSingleton(); + } + + load(id: Id): Promise { + return this._loadSingleton(); + } + + save(id: Id, obj: T) { + console.log("Idz: Repository: Saving ", this._path); + + const json = JSON.stringify(obj, undefined, 2); + + return writeFile(this._path, json, { encoding: "utf8" }); + } + + protected _discoverSingleton(): Promise { + return exists(this._path); + } + + protected async _loadSingleton(): Promise { + console.log("Idz: Repository: Loading ", this._path); + + const json = await readFile(this._path, { encoding: "utf8" }); + + return JSON.parse(json); + } +} + +export class SingletonTextRepo + implements Repository { + private _path: string; + + constructor(root: string, name: string) { + this._path = `${root}/${name}.txt`; + } + + discover(id: Id): Promise { + return exists(this._path); + } + + async load(id: Id): Promise { + console.log("Idz: Repository: Loading ", this._path); + + const existence = await exists(this._path); + + if (!existence) { + return new Array() as T; + } + + const text = await readFile(this._path, { encoding: "utf8" }); + const lines = text.split("\n"); + const codes = lines.map(parseInt); + + return codes as T; + } + + save(id: Id, codes: T): Promise { + console.log("Idz: Repository: Saving ", this._path); + + const text = codes.map(code => code.toString()).join("\n") + "\n"; + + return writeFile(this._path, text, { encoding: "utf8" }); + } +} diff --git a/src/idz/world/base.ts b/src/idz/world/base.ts new file mode 100644 index 0000000..c59a064 --- /dev/null +++ b/src/idz/world/base.ts @@ -0,0 +1,29 @@ +import * as Model from "../model"; + +export interface Repository { + discover(id: Model.Id): Promise; + + load(id: Model.Id): Promise; + + save(id: Model.Id, obj: T): Promise; +} + +export interface ProfileRepository extends Repository { + discoverByAimeId(id: Model.AimeId): Promise; + + loadByAimeId(id: Model.AimeId): Promise; +} + +export interface World { + backgrounds(): Repository; + + car(): Repository; + + chara(): Repository; + + profile(): ProfileRepository; + + settings(): Repository; + + titles(): Repository; +} diff --git a/src/idz/world/index.ts b/src/idz/world/index.ts new file mode 100644 index 0000000..cb69283 --- /dev/null +++ b/src/idz/world/index.ts @@ -0,0 +1,62 @@ +import * as fs from "fs"; +import { promisify } from "util"; + +import { World } from "./base"; +import { ProfileRepositoryImpl } from "./profile"; +import { SingletonJsonRepo, SingletonTextRepo } from "./_singleton"; +import * as Model from "../model"; + +export { World }; + +const exists = promisify(fs.exists); +const mkdir = promisify(fs.mkdir); + +class WorldImpl implements World { + constructor(private readonly _root: string) {} + + backgrounds() { + return new SingletonTextRepo( + this._root, + "backgrounds" + ); + } + + car() { + return new SingletonJsonRepo(this._root, "car"); + } + + chara() { + return new SingletonJsonRepo( + this._root, + "chara" + ); + } + + profile() { + return new ProfileRepositoryImpl(this._root); + } + + settings() { + return new SingletonJsonRepo( + this._root, + "settings" + ); + } + + titles() { + return new SingletonTextRepo( + this._root, + "titles" + ); + } +} + +export async function createWorld(root: string): Promise { + const doesExist = await exists(root); + + if (!doesExist) { + await mkdir(root); + } + + return new WorldImpl(root); +} diff --git a/src/idz/world/profile.ts b/src/idz/world/profile.ts new file mode 100644 index 0000000..cf9849f --- /dev/null +++ b/src/idz/world/profile.ts @@ -0,0 +1,19 @@ +import { SingletonJsonRepo } from "./_singleton"; +import { ProfileRepository } from "./base"; +import { AimeId } from "../model/base"; +import { Profile } from "../model/profile"; + +export class ProfileRepositoryImpl extends SingletonJsonRepo + implements ProfileRepository { + constructor(root) { + super(root, "profile"); + } + + discoverByAimeId(id: AimeId): Promise { + return this._discoverSingleton(); + } + + loadByAimeId(id: AimeId): Promise { + return this._loadSingleton(); + } +}