2023-03-03 15:23:46 +00:00
|
|
|
import { inject, injectable } from "tsyringe";
|
2023-10-19 17:21:17 +00:00
|
|
|
|
|
|
|
import { JsonUtil } from "@spt-aki/utils/JsonUtil";
|
|
|
|
import { VFS } from "@spt-aki/utils/VFS";
|
2023-11-13 11:43:37 -05:00
|
|
|
import { Queue } from "@spt-aki/utils/collections/queue/Queue";
|
2023-03-03 15:23:46 +00:00
|
|
|
|
|
|
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
|
|
/* eslint-disable @typescript-eslint/brace-style */
|
|
|
|
@injectable()
|
|
|
|
export class ImporterUtil
|
|
|
|
{
|
2023-11-13 12:31:52 -05:00
|
|
|
constructor(@inject("VFS") protected vfs: VFS, @inject("JsonUtil") protected jsonUtil: JsonUtil)
|
2023-03-03 15:23:46 +00:00
|
|
|
{}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Load files into js objects recursively (asynchronous)
|
|
|
|
* @param filepath Path to folder with files
|
|
|
|
* @returns Promise<T> return T type associated with this class
|
|
|
|
*/
|
|
|
|
public async loadRecursiveAsync<T>(
|
|
|
|
filepath: string,
|
2023-11-13 11:14:58 -05:00
|
|
|
onReadCallback: (fileWithPath: string, data: string) => void = () =>
|
|
|
|
{},
|
|
|
|
onObjectDeserialized: (fileWithPath: string, object: any) => void = () =>
|
|
|
|
{},
|
2023-03-03 15:23:46 +00:00
|
|
|
): Promise<T>
|
|
|
|
{
|
|
|
|
const result = {} as T;
|
|
|
|
|
|
|
|
// get all filepaths
|
|
|
|
const files = this.vfs.getFiles(filepath);
|
|
|
|
const directories = this.vfs.getDirs(filepath);
|
|
|
|
|
|
|
|
// add file content to result
|
|
|
|
for (const file of files)
|
|
|
|
{
|
|
|
|
if (this.vfs.getFileExtension(file) === "json")
|
|
|
|
{
|
|
|
|
const filename = this.vfs.stripExtension(file);
|
|
|
|
const filePathAndName = `${filepath}${file}`;
|
2023-11-13 11:14:58 -05:00
|
|
|
await this.vfs.readFileAsync(filePathAndName).then((fileData) =>
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
onReadCallback(filePathAndName, fileData);
|
|
|
|
const fileDeserialized = this.jsonUtil.deserializeWithCacheCheck(fileData, filePathAndName);
|
|
|
|
onObjectDeserialized(filePathAndName, fileDeserialized);
|
|
|
|
result[filename] = fileDeserialized;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// deep tree search
|
|
|
|
for (const dir of directories)
|
|
|
|
{
|
|
|
|
result[dir] = this.loadRecursiveAsync(`${filepath}${dir}/`);
|
|
|
|
}
|
|
|
|
|
|
|
|
// set all loadRecursive to be executed asynchronously
|
|
|
|
const resEntries = Object.entries(result);
|
2023-11-13 11:14:58 -05:00
|
|
|
const resResolved = await Promise.all(resEntries.map((ent) => ent[1]));
|
2023-03-03 15:23:46 +00:00
|
|
|
for (let resIdx = 0; resIdx < resResolved.length; resIdx++)
|
|
|
|
{
|
|
|
|
resEntries[resIdx][1] = resResolved[resIdx];
|
|
|
|
}
|
|
|
|
|
|
|
|
// return the result of all async fetch
|
|
|
|
return Object.fromEntries(resEntries) as T;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Load files into js objects recursively (synchronous)
|
|
|
|
* @param filepath Path to folder with files
|
2023-11-13 11:14:58 -05:00
|
|
|
* @returns
|
2023-03-03 15:23:46 +00:00
|
|
|
*/
|
2023-11-13 12:31:52 -05:00
|
|
|
public loadRecursive<T>(filepath: string, onReadCallback: (fileWithPath: string, data: string) => void = () =>
|
|
|
|
{}, onObjectDeserialized: (fileWithPath: string, object: any) => void = () =>
|
|
|
|
{}): T
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
const result = {} as T;
|
|
|
|
|
|
|
|
// get all filepaths
|
|
|
|
const files = this.vfs.getFiles(filepath);
|
|
|
|
const directories = this.vfs.getDirs(filepath);
|
|
|
|
|
|
|
|
// add file content to result
|
|
|
|
for (const file of files)
|
|
|
|
{
|
|
|
|
if (this.vfs.getFileExtension(file) === "json")
|
|
|
|
{
|
|
|
|
const filename = this.vfs.stripExtension(file);
|
|
|
|
const filePathAndName = `${filepath}${file}`;
|
|
|
|
const fileData = this.vfs.readFile(filePathAndName);
|
|
|
|
onReadCallback(filePathAndName, fileData);
|
|
|
|
const fileDeserialized = this.jsonUtil.deserializeWithCacheCheck(fileData, filePathAndName);
|
|
|
|
onObjectDeserialized(filePathAndName, fileDeserialized);
|
|
|
|
result[filename] = fileDeserialized;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// deep tree search
|
|
|
|
for (const dir of directories)
|
|
|
|
{
|
|
|
|
result[dir] = this.loadRecursive(`${filepath}${dir}/`);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async loadAsync<T>(
|
|
|
|
filepath: string,
|
|
|
|
strippablePath = "",
|
2023-11-13 11:14:58 -05:00
|
|
|
onReadCallback: (fileWithPath: string, data: string) => void = () =>
|
|
|
|
{},
|
|
|
|
onObjectDeserialized: (fileWithPath: string, object: any) => void = () =>
|
|
|
|
{},
|
2023-03-03 15:23:46 +00:00
|
|
|
): Promise<T>
|
|
|
|
{
|
|
|
|
const directoriesToRead = new Queue<string>();
|
|
|
|
const filesToProcess = new Queue<VisitNode>();
|
|
|
|
|
|
|
|
const promises = new Array<Promise<any>>();
|
|
|
|
|
|
|
|
const result = {} as T;
|
|
|
|
|
|
|
|
const files = this.vfs.getFiles(filepath);
|
|
|
|
const directories = this.vfs.getDirs(filepath);
|
2023-11-13 11:14:58 -05:00
|
|
|
|
|
|
|
directoriesToRead.enqueueAll(directories.map((d) => `${filepath}${d}`));
|
|
|
|
filesToProcess.enqueueAll(files.map((f) => new VisitNode(filepath, f)));
|
2023-03-03 15:23:46 +00:00
|
|
|
|
2023-12-21 22:27:19 +00:00
|
|
|
while (directoriesToRead.length !== 0)
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
const directory = directoriesToRead.dequeue();
|
2023-11-13 11:14:58 -05:00
|
|
|
filesToProcess.enqueueAll(this.vfs.getFiles(directory).map((f) => new VisitNode(`${directory}/`, f)));
|
|
|
|
directoriesToRead.enqueueAll(this.vfs.getDirs(directory).map((d) => `${directory}/${d}`));
|
2023-03-03 15:23:46 +00:00
|
|
|
}
|
|
|
|
|
2023-12-21 22:27:19 +00:00
|
|
|
while (filesToProcess.length !== 0)
|
2023-03-03 15:23:46 +00:00
|
|
|
{
|
|
|
|
const fileNode = filesToProcess.dequeue();
|
|
|
|
if (this.vfs.getFileExtension(fileNode.fileName) === "json")
|
|
|
|
{
|
|
|
|
const filePathAndName = `${fileNode.filePath}${fileNode.fileName}`;
|
|
|
|
promises.push(
|
2023-11-13 12:31:52 -05:00
|
|
|
this.vfs.readFileAsync(filePathAndName).then(async (fileData) =>
|
|
|
|
{
|
|
|
|
onReadCallback(filePathAndName, fileData);
|
|
|
|
return this.jsonUtil.deserializeWithCacheCheckAsync<any>(fileData, filePathAndName);
|
|
|
|
}).then(async (fileDeserialized) =>
|
|
|
|
{
|
|
|
|
onObjectDeserialized(filePathAndName, fileDeserialized);
|
|
|
|
const strippedFilePath = this.vfs.stripExtension(filePathAndName).replace(filepath, "");
|
|
|
|
this.placeObject(fileDeserialized, strippedFilePath, result, strippablePath);
|
|
|
|
}),
|
2023-03-03 15:23:46 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2023-11-13 11:14:58 -05:00
|
|
|
|
|
|
|
await Promise.all(promises).catch((e) => console.error(e));
|
|
|
|
|
2023-03-03 15:23:46 +00:00
|
|
|
return result;
|
|
|
|
}
|
2023-11-13 11:14:58 -05:00
|
|
|
|
|
|
|
protected placeObject<T>(fileDeserialized: any, strippedFilePath: string, result: T, strippablePath: string): void
|
|
|
|
{
|
2023-03-03 15:23:46 +00:00
|
|
|
const strippedFinalPath = strippedFilePath.replace(strippablePath, "");
|
|
|
|
let temp = result;
|
|
|
|
const propertiesToVisit = strippedFinalPath.split("/");
|
|
|
|
for (let i = 0; i < propertiesToVisit.length; i++)
|
|
|
|
{
|
|
|
|
const property = propertiesToVisit[i];
|
|
|
|
|
|
|
|
if (i === (propertiesToVisit.length - 1))
|
|
|
|
{
|
|
|
|
temp[property] = fileDeserialized;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!temp[property])
|
2023-11-13 11:14:58 -05:00
|
|
|
{
|
2023-03-03 15:23:46 +00:00
|
|
|
temp[property] = {};
|
2023-11-13 11:14:58 -05:00
|
|
|
}
|
2023-03-03 15:23:46 +00:00
|
|
|
temp = temp[property];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class VisitNode
|
|
|
|
{
|
2023-11-13 12:31:52 -05:00
|
|
|
constructor(public filePath: string, public fileName: string)
|
2023-11-13 11:14:58 -05:00
|
|
|
{}
|
|
|
|
}
|