mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 18:57:03 +02:00
fix: vfs in core
This commit is contained in:
1 parent
91f4f2edcf
commit
efb479fece
9 files changed
+9
-16
No files matched your search
@@ -2,11 +2,11 @@ import {
|
||||
Hierarchy,
|
||||
Sc2ReadStream,
|
||||
ScgReadStream,
|
||||
SteamVFS,
|
||||
VertexAttribute,
|
||||
} from "@blitzkit/core";
|
||||
import { Accessor, Document, Node, Scene } from "@gltf-transform/core";
|
||||
import { times } from "lodash-es";
|
||||
import { SteamVFS } from "../steam/vfs";
|
||||
import {
|
||||
vertexAttributeGLTFName,
|
||||
vertexAttributeGltfVectorSizes,
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
Hierarchy,
|
||||
Sc2ReadStream,
|
||||
ScgReadStream,
|
||||
SteamVFS,
|
||||
Textures,
|
||||
VertexAttribute,
|
||||
} from "@blitzkit/core";
|
||||
@@ -10,6 +9,7 @@ import { Document, Material, Node, Scene } from "@gltf-transform/core";
|
||||
import { dedup, prune } from "@gltf-transform/functions";
|
||||
import { times } from "lodash-es";
|
||||
import { dirname } from "path";
|
||||
import { SteamVFS } from "../../steam/vfs";
|
||||
import { readBaseColor } from "../readBaseColor";
|
||||
import { readNormal } from "../readNormal";
|
||||
import { readRoughnessMetallic } from "../readRoughnessMetallic";
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
import { readDVPL } from "@blitzkit/core/src/blitz";
|
||||
import { XMLParser } from "fast-xml-parser";
|
||||
import { normalize } from "path/posix";
|
||||
import SteamUser, { EConnectionProtocol } from "steam-user";
|
||||
import { parse } from "yaml";
|
||||
|
||||
interface SteamManifestFile {
|
||||
chunks: {
|
||||
sha: string;
|
||||
crc: number;
|
||||
offset: string;
|
||||
cb_original: number;
|
||||
cb_compressed: number;
|
||||
}[];
|
||||
filename: string;
|
||||
size: string;
|
||||
flags: number;
|
||||
sha_filename: string;
|
||||
sha_content: string;
|
||||
linktarget: unknown;
|
||||
}
|
||||
|
||||
interface SteamManifest {
|
||||
files: SteamManifestFile[];
|
||||
depot_id: number;
|
||||
gid_manifest: string;
|
||||
creation_time: number;
|
||||
filenames_encrypted: boolean;
|
||||
cb_disk_original: string;
|
||||
cb_disk_compressed: string;
|
||||
unique_chunks: number;
|
||||
crc_encrypted: number;
|
||||
crc_clear: number;
|
||||
}
|
||||
|
||||
export class SteamVFS {
|
||||
private steam = new SteamUser({ protocol: EConnectionProtocol.TCP });
|
||||
private textDecoder = new TextDecoder();
|
||||
private xmlParser = new XMLParser();
|
||||
|
||||
private manifest: Map<string, SteamManifestFile> = new Map();
|
||||
|
||||
constructor(
|
||||
private username: string,
|
||||
private password: string,
|
||||
private app: number,
|
||||
private depot: number
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
this.steam.logOn({
|
||||
accountName: this.username,
|
||||
password: this.password,
|
||||
});
|
||||
|
||||
await new Promise((resolve) => this.steam.once("loggedOn", resolve));
|
||||
|
||||
const productInfo = await this.steam.getProductInfo([this.app], []);
|
||||
const manifest: SteamManifest = await new Promise((resolve) => {
|
||||
// @ts-expect-error
|
||||
this.steam.getManifest(
|
||||
this.app,
|
||||
this.depot,
|
||||
productInfo.apps[this.app].appinfo.depots[this.depot].manifests.public
|
||||
.gid,
|
||||
"public",
|
||||
// @ts-expect-error
|
||||
(_, response) => {
|
||||
resolve(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const paths: string[] = [];
|
||||
for (const file of manifest.files) {
|
||||
const path = file.filename.replaceAll("\\", "/");
|
||||
paths.push(path);
|
||||
this.manifest.set(path, file);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
has(path: string) {
|
||||
const normalized = normalize(path);
|
||||
const dvplPath = `${normalized}.dvpl`;
|
||||
|
||||
return (
|
||||
(this.manifest.has(dvplPath) && dvplPath) ||
|
||||
(this.manifest.has(normalized) && normalized)
|
||||
);
|
||||
}
|
||||
|
||||
assert(requested: string) {
|
||||
const path = this.has(requested);
|
||||
|
||||
if (!path) throw new Error(`File not found: ${requested}`);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
dir(path: string) {
|
||||
const parentSegments = path.split("/").length;
|
||||
const children: string[] = [];
|
||||
|
||||
for (const child of this.manifest.keys()) {
|
||||
const childSegments = child.split("/");
|
||||
|
||||
if (
|
||||
child.startsWith(path) &&
|
||||
parentSegments + 1 === childSegments.length
|
||||
) {
|
||||
children.push(childSegments.at(-1)!);
|
||||
}
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
async file(requested: string) {
|
||||
const path = this.assert(requested);
|
||||
const fileManifest = this.manifest.get(path)!;
|
||||
const downloaded: { type: "complete"; file: Buffer } =
|
||||
// @ts-expect-error
|
||||
await this.steam.downloadFile(this.app, this.depot, fileManifest);
|
||||
let buffer = downloaded.file;
|
||||
|
||||
if (path.endsWith(".dvpl")) buffer = readDVPL(downloaded.file);
|
||||
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
|
||||
async text(path: string) {
|
||||
const file = await this.file(path);
|
||||
return this.textDecoder.decode(file);
|
||||
}
|
||||
|
||||
async yaml<Type>(path: string) {
|
||||
const file = await this.text(path);
|
||||
return parse(file) as Type;
|
||||
}
|
||||
|
||||
async xml<Type>(path: string) {
|
||||
const file = await this.text(path);
|
||||
return this.xmlParser.parse(file) as Type;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.steam.logOff();
|
||||
}
|
||||
|
||||
[Symbol.dispose]() {
|
||||
this.dispose();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user