Allow selecting Skyrim folder, more language tweaks
This commit is contained in:
parent
8c30a7bd92
commit
0616a92bbc
@ -47,12 +47,15 @@ const DataDirPicker: React.FC<Props> = () => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p className={styles["no-top-margin"]}>
|
<p className={styles["no-top-margin"]}>
|
||||||
Select or drag-and-drop your{" "}
|
Select or drag-and-drop your Skyrim{" "}
|
||||||
<strong>
|
<strong>
|
||||||
<code>Data</code>
|
<code>Data</code>
|
||||||
</strong>{" "}
|
</strong>{" "}
|
||||||
directory below to load the plugins and see all of the cell edits and
|
folder below to load the plugins and see all of the cell edits and
|
||||||
conflicts for your current mod load order.
|
conflicts for your current mod load order.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
The Data folder can be found in the installation directory of the game.
|
||||||
</p>
|
</p>
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
|
@ -17,6 +17,29 @@ export const DropZone: React.FC<Props> = ({ children, workerPool }) => {
|
|||||||
const [entered, setEntered] = useState(0);
|
const [entered, setEntered] = useState(0);
|
||||||
const overlay = useRef<HTMLDivElement>(null);
|
const overlay = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const findPluginsInDirHandle = async (
|
||||||
|
dirHandle: FileSystemDirectoryHandle & { values: any }
|
||||||
|
) => {
|
||||||
|
const plugins: { getFile: () => Promise<File> }[] = [];
|
||||||
|
const values = dirHandle.values();
|
||||||
|
// I'm scared of yield and generators so I'm just going to do a lame while loop
|
||||||
|
while (true) {
|
||||||
|
const next = await values.next();
|
||||||
|
if (next.done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (next.value.kind === "file" && isPluginPath(next.value.name)) {
|
||||||
|
plugins.push(next.value);
|
||||||
|
} else if (
|
||||||
|
next.value.kind === "directory" &&
|
||||||
|
next.value.name === "Data"
|
||||||
|
) {
|
||||||
|
plugins.push(...(await findPluginsInDirHandle(next.value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return plugins;
|
||||||
|
};
|
||||||
|
|
||||||
const handleDropFileSystemHandle = async (
|
const handleDropFileSystemHandle = async (
|
||||||
item: DataTransferItem
|
item: DataTransferItem
|
||||||
): Promise<boolean> => {
|
): Promise<boolean> => {
|
||||||
@ -29,20 +52,9 @@ export const DropZone: React.FC<Props> = ({ children, workerPool }) => {
|
|||||||
dispatch(setPluginsTxtAndApplyLoadOrder(await file.text()));
|
dispatch(setPluginsTxtAndApplyLoadOrder(await file.text()));
|
||||||
return true;
|
return true;
|
||||||
} else if (entry?.kind === "directory") {
|
} else if (entry?.kind === "directory") {
|
||||||
const plugins: { getFile: () => Promise<File> }[] = [];
|
const plugins = await findPluginsInDirHandle(
|
||||||
const values = (
|
|
||||||
entry as FileSystemDirectoryHandle & { values: any }
|
entry as FileSystemDirectoryHandle & { values: any }
|
||||||
).values();
|
);
|
||||||
// I'm scared of yield and generators so I'm just going to do a lame while loop
|
|
||||||
while (true) {
|
|
||||||
const next = await values.next();
|
|
||||||
if (next.done) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (next.value.kind == "file" && isPluginPath(next.value.name)) {
|
|
||||||
plugins.push(next.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const pluginFiles = await Promise.all(
|
const pluginFiles = await Promise.all(
|
||||||
plugins.map(async (plugin) => plugin.getFile())
|
plugins.map(async (plugin) => plugin.getFile())
|
||||||
);
|
);
|
||||||
@ -74,12 +86,26 @@ export const DropZone: React.FC<Props> = ({ children, workerPool }) => {
|
|||||||
(resolve, reject) => {
|
(resolve, reject) => {
|
||||||
const plugins: FileSystemFileEntry[] = [];
|
const plugins: FileSystemFileEntry[] = [];
|
||||||
reader.readEntries((entries) => {
|
reader.readEntries((entries) => {
|
||||||
|
let isData = true;
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
if (entry?.isFile && isPluginPath(entry.name)) {
|
if (entry?.isFile && isPluginPath(entry.name)) {
|
||||||
plugins.push(entry as FileSystemFileEntry);
|
plugins.push(entry as FileSystemFileEntry);
|
||||||
|
} else if (entry?.isDirectory && entry.name === "Data") {
|
||||||
|
isData = false;
|
||||||
|
const dataReader = (
|
||||||
|
entry as FileSystemDirectoryEntry
|
||||||
|
).createReader();
|
||||||
|
dataReader.readEntries((entries) => {
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
if (entry?.isFile && isPluginPath(entry.name)) {
|
||||||
|
plugins.push(entry as FileSystemFileEntry);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resolve(plugins);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
resolve(plugins);
|
if (isData) resolve(plugins);
|
||||||
}, reject);
|
}, reject);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -33,15 +33,19 @@ const PluginsLoader: React.FC<Props> = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<p>
|
<p className={styles["top-spacing"]}>
|
||||||
Paste or drag-and-drop your{" "}
|
Paste or drag-and-drop your{" "}
|
||||||
<strong>
|
<strong>
|
||||||
<code>plugins.txt</code>
|
<code>plugins.txt</code>
|
||||||
</strong>{" "}
|
</strong>{" "}
|
||||||
below to sort and enable the loaded plugins by your current load order.
|
below to sort and enable the loaded plugins by your current load order.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
The plugins.txt file is typically found at{" "}
|
The plugins.txt file is typically found at{" "}
|
||||||
<strong>
|
<strong>
|
||||||
<code>%LOCALAPPDATA%\Skyrim Special Edition\plugins.txt</code>
|
<code className={styles["break-word"]}>
|
||||||
|
C:\Users\username\AppData\Local\Skyrim Special Edition
|
||||||
|
</code>
|
||||||
</strong>
|
</strong>
|
||||||
</p>
|
</p>
|
||||||
<button onClick={onPluginsTxtButtonClick} className={styles.button}>
|
<button onClick={onPluginsTxtButtonClick} className={styles.button}>
|
||||||
@ -54,16 +58,21 @@ const PluginsLoader: React.FC<Props> = () => {
|
|||||||
<p>
|
<p>
|
||||||
The plugins.txt file is typically found at{" "}
|
The plugins.txt file is typically found at{" "}
|
||||||
<strong>
|
<strong>
|
||||||
<code>
|
<code className={styles["break-word"]}>
|
||||||
C:\Users\username\AppData\Local\Skyrim Special Edition
|
C:\Users\username\AppData\Local\Skyrim Special Edition
|
||||||
</code>
|
</code>
|
||||||
</strong>{" "}
|
</strong>{" "}
|
||||||
(or{" "}
|
(or{" "}
|
||||||
<strong>
|
<strong>
|
||||||
<code>%LOCALAPPDATA%\Skyrim Special Edition</code>
|
<code className={styles["break-word"]}>
|
||||||
|
%LOCALAPPDATA%\Skyrim Special Edition
|
||||||
|
</code>
|
||||||
</strong>
|
</strong>
|
||||||
) . You can also drag-and-drop the file anywhere on the window to
|
).
|
||||||
load the file.
|
<br />
|
||||||
|
<br />
|
||||||
|
You can also drag-and-drop the file anywhere on the window to load
|
||||||
|
the file.
|
||||||
</p>
|
</p>
|
||||||
<textarea
|
<textarea
|
||||||
value={editPluginsTxt ?? undefined}
|
value={editPluginsTxt ?? undefined}
|
||||||
|
@ -12,9 +12,7 @@ export const excludedPlugins = [
|
|||||||
|
|
||||||
export const isPluginPath = (path: string) => {
|
export const isPluginPath = (path: string) => {
|
||||||
if (
|
if (
|
||||||
path.endsWith(".esp") ||
|
/^((Skyrim Special Edition|Skyrim|SkyrimVR)\/)?(Data\/)?[^/\\]*\.es[mpl]$/i.test(path)
|
||||||
path.endsWith(".esm") ||
|
|
||||||
path.endsWith(".esl")
|
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -22,7 +20,7 @@ export const isPluginPath = (path: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const isPlugin = (file: File) => {
|
export const isPlugin = (file: File) => {
|
||||||
return isPluginPath(file.name);
|
return isPluginPath(file.webkitRelativePath ?? file.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parsePluginFiles = (pluginFiles: File[], workerPool: WorkerPool) => {
|
export const parsePluginFiles = (pluginFiles: File[], workerPool: WorkerPool) => {
|
||||||
|
@ -25,3 +25,11 @@
|
|||||||
.button {
|
.button {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.top-spacing {
|
||||||
|
margin-top: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.break-word {
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user