diff --git a/src/idz/userdb/decoder/saveProfile2.ts b/src/idz/userdb/decoder/saveProfile2.ts index 60dbbcf..b0bd220 100644 --- a/src/idz/userdb/decoder/saveProfile2.ts +++ b/src/idz/userdb/decoder/saveProfile2.ts @@ -1,6 +1,7 @@ import { car } from "./_car"; import { mission } from "./_mission"; import { BackgroundCode, CourseNo, TitleCode } from "../model/base"; +import { StoryCell, StoryRow } from "../model/story"; import { SaveProfileRequest } from "../request/saveProfile"; import { bitmap } from "./_bitmap"; import { AimeId } from "../../../model"; @@ -9,10 +10,10 @@ saveProfile2.msgCode = 0x0068; saveProfile2.msgLen = 0x0940; export function saveProfile2(buf: Buffer): SaveProfileRequest { - const storyRows = new Array(); + const storyRows = new Map(); for (let i = 0; i < 9; i++) { - const cells = new Array(); + const cells = new Map(); const rowOffset = 0x01a8 + i * 0x3c; for (let j = 0; j < 9; j++) { @@ -20,12 +21,12 @@ export function saveProfile2(buf: Buffer): SaveProfileRequest { const b = buf.readUInt16LE(rowOffset + 0x28 + j * 2); const cell = { a, b }; - cells.push(cell); + cells.set(j, cell); } const row = { cells }; - storyRows.push(row); + storyRows.set(i, row); } const coursePlays = new Map(); diff --git a/src/idz/userdb/decoder/saveProfile3.ts b/src/idz/userdb/decoder/saveProfile3.ts index 0d8876f..59eb230 100644 --- a/src/idz/userdb/decoder/saveProfile3.ts +++ b/src/idz/userdb/decoder/saveProfile3.ts @@ -1,6 +1,7 @@ import { car } from "./_car"; import { mission } from "./_mission"; import { BackgroundCode, CourseNo, TitleCode } from "../model/base"; +import { StoryCell, StoryRow } from "../model/story"; import { SaveProfileRequest } from "../request/saveProfile"; import { bitmap } from "./_bitmap"; import { AimeId } from "../../../model"; @@ -9,12 +10,12 @@ saveProfile3.msgCode = 0x0138; saveProfile3.msgLen = 0x0a70; export function saveProfile3(buf: Buffer): SaveProfileRequest { - const storyRows = new Array(); + const storyRows = new Map(); // Story layout has changed somewhat... for (let i = 0; i < 27; i++) { - const cells = new Array(); + const cells = new Map(); const rowOffset = 0x01ac + i * 0x18; for (let j = 0; j < 9; j++) { @@ -22,12 +23,12 @@ export function saveProfile3(buf: Buffer): SaveProfileRequest { const b = buf.readUInt8(rowOffset + 0x09 + j); const cell = { a, b }; - cells.push(cell); + cells.set(j, cell); } const row = { cells }; - storyRows.push(row); + storyRows.set(i, row); } const coursePlays = new Map(); diff --git a/src/idz/userdb/encoder/loadProfile2.ts b/src/idz/userdb/encoder/loadProfile2.ts index 5450351..4ab8757 100644 --- a/src/idz/userdb/encoder/loadProfile2.ts +++ b/src/idz/userdb/encoder/loadProfile2.ts @@ -37,14 +37,22 @@ export function loadProfile2(res: LoadProfileResponse) { } } - for (let i = 0; i < 9 && i < res.story.rows.length; i++) { - const row = res.story.rows[i]; + for (let i = 0; i < 9; i++) { + const row = res.story.rows.get(i); const rowOffset = 0x0228 + i * 0x26; - for (let j = 0; j < 9 && j < row.cells.length; j++) { - const cell = row.cells[j]; + if (row === undefined) { + continue; + } + + for (let j = 0; j < 9; j++) { + const cell = row.cells.get(j); const cellOffset = rowOffset + j * 4; + if (cell === undefined) { + continue; + } + buf.writeUInt16LE(cell.a, cellOffset + 0); buf.writeUInt16LE(cell.b, cellOffset + 2); } diff --git a/src/idz/userdb/encoder/loadProfile3.ts b/src/idz/userdb/encoder/loadProfile3.ts index 0841840..cbb543f 100644 --- a/src/idz/userdb/encoder/loadProfile3.ts +++ b/src/idz/userdb/encoder/loadProfile3.ts @@ -37,14 +37,22 @@ export function loadProfile3(res: LoadProfileResponse) { // space for story cells, and 27 rows * 19 bytes per row = 513 bytes, which // is the max that will fit (the final byte of each row is unused). - for (let i = 0; i < 27 && i < res.story.rows.length; i++) { - const row = res.story.rows[i]; + for (let i = 0; i < 27; i++) { + const row = res.story.rows.get(i); const rowOffset = 0x0256 + i * 0x13; - for (let j = 0; j < 9 && j < row.cells.length; j++) { - const cell = row.cells[j]; + if (row === undefined) { + continue; + } + + for (let j = 0; j < 9; j++) { + const cell = row.cells.get(j); const cellOffset = rowOffset + j * 2; + if (cell === undefined) { + continue; + } + buf.writeUInt8(cell.a, cellOffset + 0); buf.writeUInt8(cell.b, cellOffset + 1); } diff --git a/src/idz/userdb/handler/createProfile.ts b/src/idz/userdb/handler/createProfile.ts index 2b997f6..75b3138 100644 --- a/src/idz/userdb/handler/createProfile.ts +++ b/src/idz/userdb/handler/createProfile.ts @@ -1,7 +1,7 @@ import { MissionState } from "../model/mission"; import { Profile } from "../model/profile"; import { Settings } from "../model/settings"; -import { Story } from "../model/story"; +import { Story, StoryRow } from "../model/story"; import { Unlocks } from "../model/unlocks"; import { CreateProfileRequest } from "../request/createProfile"; import { CreateProfileResponse } from "../response/createProfile"; @@ -35,7 +35,11 @@ export async function createProfile( paperCup: 0, gauges: 5, }; - const story: Story = { x: 0, y: 0, rows: [] }; + const story: Story = { + x: 0, + y: 0, + rows: new Map(), + }; const unlocks: Unlocks = { auras: 1, cup: 0, diff --git a/src/idz/userdb/model/story.ts b/src/idz/userdb/model/story.ts index 9cff2c6..296177c 100644 --- a/src/idz/userdb/model/story.ts +++ b/src/idz/userdb/model/story.ts @@ -4,11 +4,11 @@ export interface StoryCell { } export interface StoryRow { - cells: StoryCell[]; + cells: Map; } export interface Story { x: number; y: number; - rows: StoryRow[]; + rows: Map; } diff --git a/src/idz/userdb/sql/story.ts b/src/idz/userdb/sql/story.ts index 758ed04..8eada11 100644 --- a/src/idz/userdb/sql/story.ts +++ b/src/idz/userdb/sql/story.ts @@ -6,6 +6,21 @@ import { FacetRepository } from "../repo"; import { Id } from "../../../model"; import { Transaction } from "../../../sql"; +function cellChanged( + lhs: StoryCell | undefined, + rhs: StoryCell | undefined +): boolean { + if (!lhs) { + return true; + } + + if (!rhs) { + return true; + } + + return lhs.a !== rhs.a || lhs.b !== rhs.b; +} + export class SqlStoryRepository implements FacetRepository { constructor(private readonly _txn: Transaction) {} @@ -19,22 +34,12 @@ export class SqlStoryRepository implements FacetRepository { // Must succeed even if nonexistent (required by save method below) - const result = { + const result: Story = { x: header !== undefined ? parseInt(header.x!) : 0, y: header !== undefined ? parseInt(header.y!) : 0, - rows: new Array(), + rows: new Map(), }; - for (let i = 0; i < 27; i++) { - const row: StoryRow = { cells: new Array() }; - - for (let j = 0; j < 9; j++) { - row.cells.push({ a: 0, b: 0 }); - } - - result.rows.push(row); - } - const loadCellSql = sql .select("sc.*") .from("idz_story_cell_state sc") @@ -45,7 +50,23 @@ export class SqlStoryRepository implements FacetRepository { for (const row of rows) { const rowNo = parseInt(row.row_no!); const colNo = parseInt(row.col_no!); - const cell = result.rows[rowNo].cells[colNo]; + + if (!result.rows.has(rowNo)) { + result.rows.set(rowNo, { + cells: new Map(), + }); + } + + const gridRow = result.rows.get(rowNo)!; + + if (gridRow.cells.has(colNo)) { + gridRow.cells.set(colNo, { + a: 0, + b: 0, + }); + } + + const cell = gridRow.cells.get(colNo)!; cell.a = parseInt(row.a!); cell.b = parseInt(row.b!); @@ -68,15 +89,15 @@ export class SqlStoryRepository implements FacetRepository { await this._txn.modify(headSql); - for (let i = 0; i < story.rows.length; i++) { - const exRow = existing.rows[i]; - const row = story.rows[i]; + for (const i of story.rows.keys()) { + const exRow = existing.rows.get(i); + const row = story.rows.get(i)!; - for (let j = 0; j < row.cells.length; j++) { - const exCell = exRow.cells[j]; - const cell = row.cells[j]; + for (const j of row.cells.keys()) { + const exCell = exRow !== undefined ? exRow.cells.get(j) : undefined; + const cell = row.cells.get(j)!; - if (cell.a === exCell.a && cell.b === exCell.b) { + if (!cellChanged(cell, exCell)) { continue; // Most if not all cells are unchanged on profile save. }