Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion etc/Dependency Report.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ This is a list of all modules leaked by Vortex to extensions. Any module listed
| dequal | 2.0.3 |
| dnd-core | 9.5.1 |
| draggabilly | 2.4.1 |
| drivelist | 10.0.2 |
| electron-context-menu | 3.6.1 |
| electron-redux | 1.4.9-sync |
| electron-updater | 4.6.5 |
Expand Down
7 changes: 0 additions & 7 deletions flatpak/generated-sources.json
Original file line number Diff line number Diff line change
Expand Up @@ -588,13 +588,6 @@
"dest-filename": "c67356006470e5066ea447e04a3968dca367339d",
"dest": "flatpak-node/yarn-mirror"
},
{
"type": "file",
"url": "https://codeload.github.com/TanninOne/drivelist/tar.gz/720d1890db11482ec05fc0f6aa176cfa6e6844dd",
"sha256": "02a2297326e2198a3d3194ff5516a1d18a81d9cb25a797a58322d68ac1014d44",
"dest-filename": "720d1890db11482ec05fc0f6aa176cfa6e6844dd",
"dest": "flatpak-node/yarn-mirror"
},
{
"type": "file",
"url": "https://codeload.github.com/TanninOne/electron-redux/tar.gz/66bbd9d389579806e8c4ebd87bd513a668cc64a8",
Expand Down
43 changes: 8 additions & 35 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ allowBuilds:
bsatk: true
core-js: true
crash-dump: true
drivelist: true
electron: true
esbuild: true

koffi: true
font-scanner: true
gamebryo-savegame: true
leveldown: true
Expand Down Expand Up @@ -154,7 +154,6 @@ catalog:
dequal: ^2.0.3
dnd-core: ^9.4.0
draggabilly: ^2.2.0
drivelist: git+https://github.com/TanninOne/drivelist#720d1890db11482ec05fc0f6aa176cfa6e6844dd
electron: 41.3.0
electron-builder: 24.13.3
electron-context-menu: ^3.1.1
Expand Down
1 change: 0 additions & 1 deletion src/main/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
"dequal": "catalog:",
"dnd-core": "catalog:",
"draggabilly": "catalog:",
"drivelist": "catalog:",
"electron-context-menu": "catalog:",
"electron-redux": "catalog:",
"electron-updater": "catalog:",
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@
"dequal": "catalog:",
"dnd-core": "catalog:",
"draggabilly": "catalog:",
"drivelist": "catalog:",
"electron": "catalog:",
"electron-context-menu": "catalog:",
"electron-redux": "catalog:",
Expand All @@ -120,6 +119,7 @@
"immutability-helper": "catalog:",
"interweave": "catalog:",
"is-admin": "catalog:",
"koffi": "^2.9.0",
"json-loader": "catalog:",
"json-socket": "catalog:",
"jsonwebtoken": "catalog:",
Expand Down
135 changes: 98 additions & 37 deletions src/renderer/src/extensions/gamemode_management/util/getDriveList.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,111 @@
import type { list as listT } from "drivelist";
import { readFile } from "fs/promises";
import type { IExtensionApi } from "../../../types/IExtensionContext";

/**
* Returns mount paths for fixed (non-removable) system drives.
* Uses koffi FFI to call Win32 APIs on Windows, /proc/mounts on Linux.
*/
function getDriveList(api: IExtensionApi): Promise<string[]> {
let list: typeof listT;
try {
list = require("drivelist").list;
if (typeof list !== "function") {
throw new Error('Failed to load "drivelist" module');
}
} catch (err) {
const impl =
process.platform === "win32" ? getFixedDrivesWindows : getFixedDrivesLinux;

return impl().catch((err) => {
api.showErrorNotification(
"Failed to query list of system drives",
{
message:
"Vortex was not able to query the operating system for the list of system drives. " +
"If this error persists, please configure the list manually.",
error: err,
},
"Failed to determine list of disk drives. " +
"Please review the settings before scanning for games.",
err,
{ allowReport: false },
);
return ["C:"];
});
}

/**
* Enumerate fixed drives via Win32 GetLogicalDriveStringsW + GetDriveTypeW.
*
* GetLogicalDriveStringsW returns a double-null-terminated buffer of drive
* root paths ("C:\\\0D:\\\0\0"). GetDriveTypeW returns the type for each;
* DRIVE_FIXED (3) is what we want.
*/
function getFixedDrivesWindows(): Promise<string[]> {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const koffi = require("koffi");
Comment thread
halgari marked this conversation as resolved.
Outdated
const kernel32 = koffi.load("kernel32.dll");

const GetLogicalDriveStringsW = kernel32.func(
"uint32 GetLogicalDriveStringsW(uint32 nBufferLength, uint16 *lpBuffer)",
);
const GetDriveTypeW = kernel32.func(
"uint32 GetDriveTypeW(const uint16 *lpRootPathName)",
);

const DRIVE_FIXED = 3;

// Buffer for drive strings — 26 drives * 4 chars ("X:\\\0") = 104 UTF-16 code units max
const buf = Buffer.alloc(256);
const len = GetLogicalDriveStringsW(buf.length / 2, buf) as number;

if (len === 0) {
return Promise.resolve(["C:"]);
}

return list()
.then((disks) =>
disks
.sort()
.filter((disk) => disk.isSystem && !disk.isRemovable)
.reduce((prev, disk) => {
if (disk.mountpoints) {
prev.push(...disk.mountpoints.map((mp) => mp.path));
} else if (disk["mountpoint"] !== undefined) {
prev.push(disk["mountpoint"]);
}
return prev;
}, []),
const drives: string[] = [];
let offset = 0;

while (offset < len * 2) {
// Read null-terminated UTF-16LE string
const codes: number[] = [];
while (offset < buf.length) {
const code = buf.readUInt16LE(offset);
offset += 2;
if (code === 0) break;
codes.push(code);
}
if (codes.length === 0) break;

const rootPath = String.fromCharCode(...codes); // e.g. "C:\\"

// Check drive type
const widePath = Buffer.alloc((rootPath.length + 1) * 2);
for (let i = 0; i < rootPath.length; i++) {
widePath.writeUInt16LE(rootPath.charCodeAt(i), i * 2);
}

const driveType = GetDriveTypeW(widePath) as number;
if (driveType === DRIVE_FIXED) {
// Strip trailing backslash: "C:\\" → "C:"
drives.push(rootPath.replace(/\\$/, ""));
}
}

return Promise.resolve(drives.length > 0 ? drives : ["C:"]);
}

async function getFixedDrivesLinux(): Promise<string[]> {
const raw = await readFile("/proc/mounts", "utf8");
const drives = raw
.split("\n")
.filter((line) => line.length > 0)
.map((line) => {
const parts = line.split(" ");
return { device: parts[0], mountpoint: parts[1], fstype: parts[2] };
})
.filter(
({ device, fstype }) =>
device.startsWith("/dev/") &&
!device.startsWith("/dev/loop") &&
["ext4", "btrfs", "xfs", "zfs", "ext3", "ext2", "f2fs"].includes(
fstype,
),
)
.catch((err) => {
api.showErrorNotification(
"Failed to determine list of disk drives. " +
"Please review the settings before scanning for games.",
err,
{ allowReport: false },
);
return ["C:"];
});
.map(({ mountpoint }) =>
// Unescape octal sequences in mount paths (e.g. \040 for space)
mountpoint.replace(/\\([0-7]{3})/g, (_, oct) =>
String.fromCharCode(parseInt(oct, 8)),
),
);

return drives.length > 0 ? drives : ["/"];
}

export default getDriveList;
Loading