Allow selecting Skyrim folder, more language tweaks

This commit is contained in:
Tyler Hallada 2022-03-15 22:33:44 -04:00
parent 8c30a7bd92
commit 0616a92bbc
5 changed files with 70 additions and 26 deletions

View File

@ -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"

View 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) => {
if (entry?.isFile && isPluginPath(entry.name)) {
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) => { 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);
} }
}); });
resolve(plugins); resolve(plugins);
});
}
});
if (isData) resolve(plugins);
}, reject); }, reject);
} }
); );

View File

@ -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}

View File

@ -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) => {

View File

@ -25,3 +25,11 @@
.button { .button {
margin-bottom: 24px; margin-bottom: 24px;
} }
.top-spacing {
margin-top: 34px;
}
.break-word {
word-break: break-word;
}