Skip to content

Commit

Permalink
Automatically determine D2R game dir from registry
Browse files Browse the repository at this point in the history
  • Loading branch information
olegbl committed Nov 18, 2023
1 parent 81d7b09 commit 54602fd
Show file tree
Hide file tree
Showing 22 changed files with 1,440 additions and 19 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = {
'react/require-default-props': 'off',
'react/destructuring-assignment': 'off',
'operator-assignment': ['warn', 'never'],
'no-await-in-loop': 'off',
'no-plusplus': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-inferrable-types': 'off',
Expand All @@ -24,6 +25,7 @@ module.exports = {
caughtErrorsIgnorePattern: '^_.*$',
},
],
'@typescript-eslint/return-await': 'off',
'prefer-destructuring': 'off',
'no-console': 'off',
},
Expand Down
2 changes: 1 addition & 1 deletion mods
Submodule mods updated from 884f63 to 3e3c1b
1 change: 1 addition & 0 deletions release/app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"ffi-napi": "^4.0.3",
"json5": "^2.2.0",
"ref-napi": "^3.0.3",
"regedit": "^5.1.2",
"vm2": "^3.9.19"
},
"license": "MIT"
Expand Down
70 changes: 70 additions & 0 deletions release/app/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,25 @@ acorn@^8.7.0:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5"
integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==

core-util-is@~1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==

debug@^3.1.0:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
dependencies:
ms "^2.1.1"

debug@^4.1.0:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"

debug@^4.1.1:
version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
Expand Down Expand Up @@ -50,6 +62,21 @@ get-uv-event-loop-napi-h@^1.0.5:
dependencies:
get-symbol-from-current-process-h "^1.0.1"

if-async@^3.7.4:
version "3.7.4"
resolved "https://registry.yarnpkg.com/if-async/-/if-async-3.7.4.tgz#55868deb0093d3c67bf7166e745353fb9bcb21a2"
integrity sha512-BFEH2mZyeF6KZKaKLVPZ0wMjIiWOdjvZ7zbx8ENec0qfZhJwKFbX/4jKM5LTKyJEc/GOqUKiiJ2IFKT9yWrZqA==

inherits@~2.0.1:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==

isarray@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==

json5@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
Expand Down Expand Up @@ -82,6 +109,16 @@ node-gyp-build@^4.2.1:
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3"
integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==

"readable-stream@>=1.0.33-1 <1.1.0-0":
version "1.0.34"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
integrity sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.1"
isarray "0.0.1"
string_decoder "~0.10.x"

"ref-napi@^2.0.1 || ^3.0.2", ref-napi@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/ref-napi/-/ref-napi-3.0.3.tgz#e259bfc2bbafb3e169e8cd9ba49037dd00396b22"
Expand All @@ -99,10 +136,43 @@ ref-struct-di@^1.1.0:
dependencies:
debug "^3.1.0"

regedit@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/regedit/-/regedit-5.1.2.tgz#51728e0cfd7bbba95d7ab09c227aeb1174abb4e1"
integrity sha512-pQpWqO/I40bMNoMO9kTQx3e5iK542kYcB/Z8X3Y7Hcri6ydc4KZ9ByUsEWFkBRMcwo+2irHuNK5s+pMGPr6VPw==
dependencies:
debug "^4.1.0"
if-async "^3.7.4"
stream-slicer "0.0.6"
through2 "^0.6.3"

stream-slicer@0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/stream-slicer/-/stream-slicer-0.0.6.tgz#f86b2ac5c2440b7a0a87b71f33665c0788046138"
integrity sha512-QsY0LbweYE5L+e+iBQgtkM5WUIf7+kCMA/m2VULv8rEEDDnlDPsPvOHH4nli6uaZOKQEt64u65h0l/eeZo7lCw==

string_decoder@~0.10.x:
version "0.10.31"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==

through2@^0.6.3:
version "0.6.5"
resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48"
integrity sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==
dependencies:
readable-stream ">=1.0.33-1 <1.1.0-0"
xtend ">=4.0.0 <4.1.0-0"

vm2@^3.9.19:
version "3.9.19"
resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.19.tgz#be1e1d7a106122c6c492b4d51c2e8b93d3ed6a4a"
integrity sha512-J637XF0DHDMV57R6JyVsTak7nIL8gy5KH4r1HiwWLf/4GBbb5MKL5y7LpmF4A8E2nR6XmzpmMFQ7V7ppPTmUQg==
dependencies:
acorn "^8.7.0"
acorn-walk "^8.2.0"

"xtend@>=4.0.0 <4.1.0-0":
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
31 changes: 28 additions & 3 deletions src/main/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
statSync,
writeFileSync,
} from 'fs';
import regedit from 'regedit';
import path from 'path';
import ffi from 'ffi-napi';
import ref from 'ref-napi';
Expand Down Expand Up @@ -134,6 +135,26 @@ export const BridgeAPI: BridgeAPIImplementation = {
return getAppPath();
},

getGamePath: async () => {
rendererConsole.debug('BridgeAPI.getGamePath');

try {
regedit.setExternalVBSLocation(path.join(getAppPath(), './tools'));
const regKey =
'HKLM\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Diablo II Resurrected';
const result = await regedit.promisified.list([regKey]);
return result[regKey].values.InstallLocation.value.toString();
} catch (error) {
// useful for debugging, but not useful to expose to user
rendererConsole.debug(
'BridgeAPI.getGamePath',
'Failed to fetch game path from the registry',
String(error)
);
return null;
}
},

execute: (
executablePath: string,
args: string[] = [],
Expand Down Expand Up @@ -565,7 +586,7 @@ export const BridgeAPI: BridgeAPIImplementation = {
// remove byte order mark
.replace(/^\uFEFF/, '');
try {
return json5.parse(cleanContent);
return json5.parse<JSONData>(cleanContent);
} catch (e) {
return e instanceof Error ? e : new Error(String(e));
}
Expand Down Expand Up @@ -708,9 +729,13 @@ export function initBridgeAPI(mainWindow: BrowserWindow): void {
// hook up bridge API calls
Object.keys(BridgeAPI).forEach((apiName) => {
const apiCall = BridgeAPI[apiName as keyof typeof BridgeAPI];
ipcMain.on(apiName, (event, args: unknown[] | void) => {
ipcMain.on(apiName, async (event, args: unknown[] | void) => {
// @ts-ignore[2556] - can't enforce strict typing for data coming across the bridge
event.returnValue = apiCall(...(args ?? []));
let result = apiCall(...(args ?? []));
if (result instanceof Promise) {
result = await result;
}
event.returnValue = result;
});
});

Expand Down
1 change: 1 addition & 0 deletions src/main/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const BridgeAPINames = [
'execute',
'extractFile',
'getAppPath',
'getGamePath',
'getVersion',
'installMods',
'openStorage',
Expand Down
1 change: 1 addition & 0 deletions src/renderer/ModInstallButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export default function ModInstallButton({
isDryRun: isUninstall,
isPreExtractedData: preferences.isPreExtractedData,
mergedPath: preferences.mergedPath,
outputModName: preferences.outputModName,
preExtractedDataPath: preferences.preExtractedDataPath,
rawGamePath: preferences.rawGamePath,
};
Expand Down
13 changes: 8 additions & 5 deletions src/renderer/Preferences.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useContext, useMemo } from 'react';
import useSavedState from './useSavedState';

const BridgeAPI = window.electron.BridgeAPI;

export const Context = React.createContext<IPreferences | null>(null);

export function usePreferences(): IPreferences {
Expand All @@ -16,14 +18,15 @@ type Props = {
};

export function PreferencesProvider({ children }: Props): JSX.Element {
const [rawGamePath, setRawGamePath] = useSavedState(
'paths',
'C:\\Battle.net\\Games\\Diablo II Resurrected'
);
const defaultGamePath =
BridgeAPI.getGamePath() ??
'C:\\Program Files\\Battle.net\\Games\\Diablo II Resurrected';

const [rawGamePath, setRawGamePath] = useSavedState('paths', defaultGamePath);

const [preExtractedDataPath, setPreExtractedDataPath] = useSavedState(
'pre-extracted-data-path',
'C:\\Battle.net\\Games\\Diablo II Resurrected\\data'
`${rawGamePath}\\data`
);

const [outputModName, setOutputModName] = useSavedState(
Expand Down
23 changes: 13 additions & 10 deletions src/renderer/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,13 @@ declare global {
};

type BridgeAPIImplementation = {
closeStorage: () => void;
closeStorage: () => boolean | Error;
copyFile: (
fromPath: string,
toPath: string,
overwrite?: boolean
) => number | Error;
createDirectory: (filePath: string) => void;
createDirectory: (filePath: string) => boolean | Error;
deleteFile: (filePath: string, isRelative: boolean) => number | Error;
execute: (
executablePath: string,
Expand All @@ -220,14 +220,15 @@ declare global {
gamePath: string,
filePath: string,
targetPath: string
) => void;
) => boolean | Error;
getAppPath: () => string;
getGamePath: () => Promise<string | null>;
getVersion: () => string;
installMods: (
modsToInstall: Mod[],
options: IInstallModsOptions
) => string[];
openStorage: (gamePath: string) => void;
openStorage: (gamePath: string) => boolean | Error;
readDirectory: (
filePath: string
) => { name: string; isDirectory: boolean }[] | Error;
Expand All @@ -244,19 +245,21 @@ declare global {
isRelative: boolean,
data: string
) => number | Error;
writeJson: (filePath: string, data: JSONData) => void;
writeJson: (filePath: string, data: JSONData) => number | Error;
writeModConfig: (id: string, value: ModConfigValue) => number | Error;
writeTsv: (filePath: string, data: TSVData) => void;
writeTxt: (filePath: string, data: string) => void;
writeTsv: (filePath: string, data: TSVData) => number | Error;
writeTxt: (filePath: string, data: string) => number | Error;
};

type APIWithThrownErrors<T> = {
// errors get thrown
// async methods are made synchronous
type BridgeAPIDeriver<T> = {
[K in keyof T]: T[K] extends (...args: infer A) => infer R
? (...args: A) => Exclude<R, Error>
? (...args: A) => Exclude<R extends Promise<infer PR> ? PR : R, Error>
: never;
};

type BridgeAPI = APIWithThrownErrors<BridgeAPIImplementation>;
type BridgeAPI = BridgeAPIDeriver<BridgeAPIImplementation>;

interface Window {
electron: {
Expand Down
75 changes: 75 additions & 0 deletions tools/ArchitectureAgnosticRegistry.vbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
' Notes: wanted to implement this using a class but:
' 1. No matter what I did I could not assign the result of GetObject to a private member
' 2. It looks as if all methods were treated as subs from the outside world which is not good since
' some of these need to return a value
'

Set private_oReg = GetObject("winmgmts:\root\default:StdRegProv")

Function SetStringValue(constHive, strSubKey, strValueName, strValue)
SetStringValue = private_oReg.SetStringValue(constHive, strSubKey, strValueName, strValue)
End Function

Sub GetStringValue(constHive, strKey, strValueName, strValue)
private_oReg.GetStringValue constHive, strKey, strValueName, strValue
End Sub

Function SetExpandedStringValue(constHive, strSubKey, strValueName, strValue)
SetExpandedStringValue = private_oReg.SetExpandedStringValue(constHive, strSubKey, strValueName, strValue)
End Function

Sub GetExpandedStringValue(constHive, strKey, strValueName, strValue)
private_oReg.GetExpandedStringValue constHive, strKey, strValueName, strValue
End Sub

Function SetMultiStringValue(constHive, strSubKey, strValueName, arrValue)
SetMultiStringValue = private_oReg.SetMultiStringValue(constHive, strSubKey, strValueName, arrValue)
End Function

Sub GetMultiStringValue(constHive, strKey, strValueName, arrStrValue)
private_oReg.GetMultiStringValue constHive, strKey, strValueName, arrStrValue
End Sub

Function SetDWORDValue(constHive, strSubKey, strValueName, arrValue)
SetDWORDValue = private_oReg.SetDWORDValue(constHive, strSubKey, strValueName, arrValue)
End Function

Sub GetDWORDValue(constHive, strKey, strValueName, intDWordValue)
private_oReg.GetDWORDValue constHive, strKey, strValueName, intDWordValue
End Sub

Function SetQWORDValue(constHive, strSubKey, strValueName, strQWordValue)
SetQWORDValue = private_oReg.SetQWORDValue(constHive, strSubKey, strValueName, strQWordValue)
End Function

Sub GetQWORDValue(constHive, strKey, strValueName, intQWordValue)
private_oReg.GetQWORDValue constHive, strKey, strValueName, intQWordValue
End Sub

Function SetBinaryValue(constHive, strSubKey, strValueName, arrValue)
SetBinaryValue = private_oReg.SetBinaryValue(constHive, strSubKey, strValueName, arrValue)
End Function

Sub GetBinaryValue(constHive, strKey, strValueName, arrBinaryValue)
private_oReg.GetBinaryValue constHive, strKey, strValueName, arrBinaryValue
End Sub

Function EnumKey(constHive, strSubKey, arrKeyNames)
EnumKey = private_oReg.EnumKey(constHive, strSubKey, arrKeyNames)
End Function

Function EnumValues(constHive, strSubKey, arrValueNames, arrValueTypes)
EnumValues = private_oReg.EnumValues(constHive, strSubKey, arrValueNames, arrValueTypes)
End Function

Function CreateKey(constHive, strSubKey)
CreateKey = private_oReg.CreateKey(constHive, strSubKey)
End Function

Function DeleteKey(constHive, strSubKey)
DeleteKey = private_oReg.DeleteKey(constHive, strSubKey)
End Function

Function DeleteValue(constHive, strSubKey, strValue)
DeleteValue = private_oReg.DeleteValue(constHive, strSubKey, strValue)
End Function
Loading

0 comments on commit 54602fd

Please sign in to comment.