diff --git a/src/main/services/achievements/achievement-file-observer.ts b/src/main/services/achievements/achievement-file-observer.ts index d008304e..281ba58f 100644 --- a/src/main/services/achievements/achievement-file-observer.ts +++ b/src/main/services/achievements/achievement-file-observer.ts @@ -62,9 +62,7 @@ export const checkAchievementFileChange = async (games: Game[]) => { const achievementFileInsideDirectory = findAchievementFileInExecutableDirectory(game); - if (achievementFileInsideDirectory) { - gameAchievementFiles.push(achievementFileInsideDirectory); - } + gameAchievementFiles.push(...achievementFileInsideDirectory); if (!gameAchievementFiles.length) continue; diff --git a/src/main/services/achievements/find-achivement-files.ts b/src/main/services/achievements/find-achivement-files.ts index 0b694578..09823ca3 100644 --- a/src/main/services/achievements/find-achivement-files.ts +++ b/src/main/services/achievements/find-achivement-files.ts @@ -136,6 +136,10 @@ const getPathFromCracker = async (cracker: Cracker) => { ]; } + if (cracker === Cracker._3dm) { + return []; + } + achievementsLogger.error(`Cracker ${cracker} not implemented`); throw new Error(`Cracker ${cracker} not implemented`); }; @@ -163,22 +167,33 @@ export const findAchievementFiles = async (game: Game) => { export const findAchievementFileInExecutableDirectory = ( game: Game -): AchievementFile | null => { +): AchievementFile[] => { if (!game.executablePath) { - return null; + return []; } - const steamDataPath = path.join( - game.executablePath, - "..", - "SteamData", - "user_stats.ini" - ); - - return { - type: Cracker.userstats, - filePath: steamDataPath, - }; + return [ + { + type: Cracker.userstats, + filePath: path.join( + game.executablePath, + "..", + "SteamData", + "user_stats.ini" + ), + }, + { + type: Cracker._3dm, + filePath: path.join( + game.executablePath, + "..", + "3DMGAME", + "Player", + "stats", + "achievements.ini" + ), + }, + ]; }; export const findAllAchievementFiles = async () => { diff --git a/src/main/services/achievements/parse-achievement-file.ts b/src/main/services/achievements/parse-achievement-file.ts index e239c05e..2e05b0e2 100644 --- a/src/main/services/achievements/parse-achievement-file.ts +++ b/src/main/services/achievements/parse-achievement-file.ts @@ -45,6 +45,11 @@ export const parseAchievementFile = async ( return processSkidrow(parsed); } + if (type === Cracker._3dm) { + const parsed = await iniParse(filePath); + return process3DM(parsed); + } + achievementsLogger.log(`${type} achievements found on ${filePath}`); return []; }; @@ -139,6 +144,28 @@ const processGoldberg = (unlockedAchievements: any): UnlockedAchievement[] => { return newUnlockedAchievements; }; +const process3DM = (unlockedAchievements: any): UnlockedAchievement[] => { + const newUnlockedAchievements: UnlockedAchievement[] = []; + + const achievements = unlockedAchievements["State"]; + const times = unlockedAchievements["Time"]; + + for (const achievement of Object.keys(achievements)) { + if (achievements[achievement] == "0101") { + const time = times[achievement]; + + newUnlockedAchievements.push({ + name: achievement, + unlockTime: new DataView( + new Uint8Array(Buffer.from(time.toString(), "hex")).buffer + ).getUint32(0, true), + }); + } + } + + return newUnlockedAchievements; +}; + const processDefault = (unlockedAchievements: any): UnlockedAchievement[] => { const newUnlockedAchievements: UnlockedAchievement[] = []; diff --git a/src/main/services/achievements/update-local-unlocked-achivements.ts b/src/main/services/achievements/update-local-unlocked-achivements.ts index 6c0fed8f..4d21a603 100644 --- a/src/main/services/achievements/update-local-unlocked-achivements.ts +++ b/src/main/services/achievements/update-local-unlocked-achivements.ts @@ -7,6 +7,7 @@ import { parseAchievementFile } from "./parse-achievement-file"; import { mergeAchievements } from "./merge-achievements"; import type { UnlockedAchievement } from "@types"; import { getGameAchievementData } from "./get-game-achievement-data"; +import { achievementsLogger } from "../logger"; export const updateAllLocalUnlockedAchievements = async () => { const gameAchievementFilesMap = await findAllAchievementFiles(); @@ -47,10 +48,16 @@ export const updateAllLocalUnlockedAchievements = async () => { achievementFile.filePath, achievementFile.type ); - console.log("Parsed for", game.title, parsedAchievements); + if (parsedAchievements.length) { unlockedAchievements.push(...parsedAchievements); } + + achievementsLogger.log( + "Achievement file for", + game.title, + achievementFile.filePath + ); } mergeAchievements(objectId, "steam", unlockedAchievements, false); diff --git a/src/shared/constants.ts b/src/shared/constants.ts index e22d7712..4a826dcd 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -35,4 +35,5 @@ export enum Cracker { skidrow = "SKIDROW", creamAPI = "CreamAPI", smartSteamEmu = "SmartSteamEmu", + _3dm = "3dm", }